1. 网络中进程之间如何通信

一起学习的可以后台私信“资料”送相关学习资料可以一起学习交流大家也可以关注一下,记得后台私信“资料”送学习视频需要C/C++ Linux服务器架构师学习资料后台私信“资料”(资料包括C/C++,Linux,golang技术,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,ffmpeg等等。。。),免费分享


进 程通信的概念最初来源于单机系统。由于每个进程都在自己的地址范围内运行,为保证两个相互通信的进

程之间既互不干扰又协调一致工作,操作系统为进程通信提供了相应设施,如

UNIX BSD有:管道(pipe)、命名管道(named pipe)软中断信号(signal)

UNIX system V有:消息(message)、共享存储区(shared memory)和信号量(semaphore)等.

他们都仅限于用在本机进程之间通信。网间进程通信要解决的是不同主机进程间的相互通信问题(可把同机进程通信看作是其中的特例)。为此,首先要解决的是网间进程标识问题。同一主机上,不同进程可用进程号(process ID)唯一标识。但在网络环境下,各主机独立分配的进程号不能唯一标识该进程。例如,主机A赋于某进程号5,在B机中也可以存在5号进程,因此,“5号进程”这句话就没有意义了。 其次,操作系统支持的网络协议众多,不同协议的工作方式不同,地址格式也不同。因此,网间进程通信还要解决多重协议的识别问题。

其实TCP/IP协议族已经帮我们解决了这个问题,网络层的“ip地址”可以唯一标识网络中的主机,而传输层的“协议+端口”可以唯一标识主机中的应用程序(进程)。这样利用三元组(ip地址,协议,端口)就可以标识网络的进程了,网络中的进程通信就可以利用这个标志与其它进程进行交互。

使用TCP/IP协议的应用程序通常采用应用编程接口:UNIX BSD的套接字(socket)和UNIX System V的TLI(已经被淘汰),来实现网络进程之间的通信。就目前而言,几乎所有的应用程序都是采用socket,而现在又是网络时代,网络中进程通信是无处不在,这就是我为什么说“一切皆socket”。

160824df7260e7d3d0733bd64b0eaa03.png

2. 什么是TCP/IP、UDP

TCP/IP(Transmission Control Protocol/Internet Protocol)即传输控制协议/网间协议,是一个工业标准的协议集,它是为广域网(WANs)设计的。

TCP/IP协议存在于OS中,网络服务通过OS提供,在OS中增加支持TCP/IP的系统调用——Berkeley套接字,如Socket,Connect,Send,Recv等

UDP(User Data Protocol,用户数据报协议)是与TCP相对应的协议。它是属于TCP/IP协议族中的一种。如图:

fc3e6e50e515bb6bc46d8a7d61fa6f87.png

TCP/IP协议族包括运输层、网络层、链路层,而socket所在位置如图,Socket是应用层与TCP/IP协议族通信的中间软件抽象层。

86f5481fcc23cc274916b5d5a57d6e24.png

3. Socket是什么

1、 socket套接字:

socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,都可以用“打开open –> 读写write/read –> 关闭close”模式来操作。Socket就是该模式的一个实现, socket即是一种特殊的文件,一些socket函数就是对其进行的操作(读/写IO、打开、关闭). 说白了Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。

使用socket进行TCP通信时,经常使用的函数有:将从 二、创建套接字讲起

d4c7dedfc0223ac76040fc9b0fb4b790.png

一、简述

1. 不同主机进程间的通信——Socket

1)网络中的数据传输是一种I/O操作

2)read、write、close操作可应用于Socket描述符

3)Socket是一种文件描述符,代表了一个通信管道的一个端点

4)在Socket类型的文件描述符上,可以完成建立连接,数据传输等操作

5)常用的Socket类型有两种:

流式Socket:SOCK_STREAM , 提供面向连接的Socket

数据报式Socket:SOCK_DGRAM , 提供面向无连接的Socket

2. 字节序

1)概念:是指多字节数据的存储顺序

2)分类:

小端格式:将低位字节数据存储在低地址

大端格式:将高位字节数据存储在低地址

3)特点:网络协议指定了通讯字节序--大端

只有在多字节数据处理时才需要考虑字节序

运行在同一台计算机上的进程相互通信时,一般不用考虑字节序

异构计算机之间通讯,需要转换自己的字节序为网络字节序

4)确定主机字节序:

   #include     #include     #include           void main()    {     printf("程序开始");      union{     short data;     char c[sizeof(short)];     }un;      un.data = 0x0102;      if( un.c[0] == 1 && un.c[1] == 2){     printf("大端格式");     }else if( un.c[0] == 2 && un.c[1] == 1){     printf("小端格式");     }     printf("程序结束");     return;    }

5)字节序转换函数

   1)主机字节序数据转换成网络字节序数据
int16_t htons(uint16_t host16bit) 把16位值从主机字节序转到网络字节序uint32_t htonl(uint32_t host32bit)  把32位值从主机字节序转到网络字节序功能:    将32或16位主机字节序数据转换成网络字节序数据参数:    uint32_t: unsigned int    hostint32:待转换的32位主机字节序数据返回值:    成功:返回网络字节序的值2)网络字节序数据转换成主机字节序数据uint16_t ntohs(uint16_t net16bit) 把16位值从网络字节序转到主机字节序uint32_t ntohs(uint32_t net32bit)  把32位值从网络字节序转到主机字节序功能:    将32或16位网络字节序数据转换成主机字节序数据参数:    uint32_t: unsignedint    netint32: 待转换的32位网络字节序数据返回值:    成功:返回主机字节序的值

3. 通用套接字地址结构sockaddr

套接字数据结构用于保存套接字信息,与使用该结构的网络协议有关,每一种网络协议都有其本身的网络地址数据结构,都是以sockaddr_开头的,不同的网络协议有不同的后缀,如IPv4对应的是skocaddr_in

1)地址标识了特定通信域中的套接字端点,

地址格式与特定通信域相关,

为使不同格式地址能被传入套接字函数,地址被强制转换成通用套接字地址结构

      通用套接字地址结构如下:
struct sockaddr{  sa_family_t  sa_family; //2 字节,地址族 AF_xxx  char  sa_data[14]; //14 字节的协议地 址,包含套接字IP和端口号};头文件:#include 

4. 套接字地址结构sockaddr_in

1)在IPv4因特网域(AF_INET)中,套接字地址结构用sockaddr_in命名

struct sockaddr_in{  sa_family_t  sin_family; //2字节,地址族  in_port_t  sin_port; //2字节,端口号  struct in_addr  sin_addr; //4字节,IP地址  unsigned char  sin_zero[8]; //8字节, };struct in_addr{  in_addr_t s_addr; //4字节};sin_zero说明:用来将sockaddr_in结构填充到与sockaddr同样长度,可用bzero()或memset()函数将其置为0头文件:#include 

5. 地址转换函数

1)int inet_pton(int family, const char* strptr,void *addrptr);

功能:

将点分十进制数串转换成32位无符号整数

参数:

family 协议族

strptr 点分十进制数串

addrptr 32位无符号整数的地址

返回值:

成功:1

失败:其它

头文件:#include

2)const char *inet_ntop(int family, const void* addrptr, char *strptr, size_t len);

功能:

将32位无符号整数转换成点分十进制数串

参数:

family 协议族

addrptr 32位无符号整数

strptr 点分十进制数串

len strptr缓存区长度

len的宏定义

#define INET_ADDRSTRLEN 16

#define INET6_ADDRSTRLEN 46 //for ipv6

返回值:

成功:则返回字符串的首地址

失败:返回NULL

头文件:#include

6.服务模型

TCP:

7ffe89665854689b77cdff8a92d23ebe.png

Udp:

ac92ffeb170ac1b385e886c0a092cd0b.png

二、创建套接字

创建套接字是进行任何网络通信时必须做的第一步

1.int socket(int family, int type,int protocol);

功能:

创建一个用于网络通信的I/O描述符(套接字)

参数:

family:协议族

AF_INET,AF_INET6,AF_LOCAL,AF_ROUTE,AF_KEY

常用值 AF_INET 互联网协议族

type:套接字类型

SOCK_STREAM(流式套接字)

SOCK_DGRAM(数据包套接字)

SOCK_RAW (原始套接字)

SOCK_SEQPACKET

protocol:协议类别

0,IPPROTO_TCP,IPPROTO_UDP,IPPROTO_SCTP

常用值 0

返回值:套接字

socket创建的套接字特点

使用socket创建套接字时,系统不会分配端口

使用socket创建的是主动套接字,但作为服务器,

需要被动等待别人的连接

头文件:#include

4baa602a8f907da9ea0e901ee272a65c.png

Socket编程实例

服务器端:一直监听本机的8000号端口,如果收到连接请求,将接收请求并接收客户端发来的消息,并向客户端返回消息。

/* File Name: server.c */#include#include#include#include#include#include#include#define DEFAULT_PORT 8000#define MAXLINE 4096int main(int argc, char** argv){    int    socket_fd, connect_fd;    struct sockaddr_in     servaddr;    char    buff[4096];    int     n;    //初始化Socket    if( (socket_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1 ){    printf("create socket error: %s(errno: %d)",strerror(errno),errno);    exit(0);    }    //初始化    memset(&servaddr, 0, sizeof(servaddr));    servaddr.sin_family = AF_INET;    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//IP地址设置成INADDR_ANY,让系统自动获取本机的IP地址。    servaddr.sin_port = htons(DEFAULT_PORT);//设置的端口为DEFAULT_PORT     //将本地地址绑定到所创建的套接字上    if( bind(socket_fd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1){    printf("bind socket error: %s(errno: %d)",strerror(errno),errno);    exit(0);    }    //开始监听是否有客户端连接    if( listen(socket_fd, 10) == -1){    printf("listen socket error: %s(errno: %d)",strerror(errno),errno);    exit(0);    }    printf("======waiting for client's request======");    while(1){//阻塞直到有客户端连接,不然多浪费CPU资源。        if( (connect_fd = accept(socket_fd, (struct sockaddr*)NULL, NULL)) == -1){        printf("accept socket error: %s(errno: %d)",strerror(errno),errno);        continue;    }//接受客户端传过来的数据    n = recv(connect_fd, buff, MAXLINE, 0);//向客户端发送回应数据    if(!fork()){ /*紫禁城*/        if(send(connect_fd, "Hello,you are connected!", 26,0) == -1)        perror("send error");        close(connect_fd);        exit(0);    }    buff[n] = '0';    printf("recv msg from client: %s", buff);    close(connect_fd);    }    close(socket_fd);}

客户端:

/* File Name: client.c */ #include#include#include#include#include#include#include #define MAXLINE 4096  int main(int argc, char** argv){    int    sockfd, n,rec_len;    char    recvline[4096], sendline[4096];    char    buf[MAXLINE];    struct sockaddr_in    servaddr;      if( argc != 2){    printf("usage: ./client ");    exit(0);    }      if( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){    printf("create socket error: %s(errno: %d)", strerror(errno),errno);    exit(0);    }      memset(&servaddr, 0, sizeof(servaddr));    servaddr.sin_family = AF_INET;    servaddr.sin_port = htons(8000);    if( inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0){    printf("inet_pton error for %s",argv[1]);    exit(0);    }      if( connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0){    printf("connect error: %s(errno: %d)",strerror(errno),errno);    exit(0);    }      printf("send msg to server: ");    fgets(sendline, 4096, stdin);    if( send(sockfd, sendline, strlen(sendline), 0) < 0)    {    printf("send msg error: %s(errno: %d)", strerror(errno), errno);    exit(0);    }    if((rec_len = recv(sockfd, buf, MAXLINE,0)) == -1) {       perror("recv error");       exit(1);    }    buf[rec_len]  = '0';    printf("Received : %s ",buf);    close(sockfd);    exit(0);}

inet_pton 是Linux下IP地址转换函数,可以在将IP地址在“点分十进制”和“整数”之间转换 ,是inet_addr的扩展。

一起学习的可以后台私信“资料”送相关学习资料可以一起学习交流大家也可以关注一下,记得后台私信“资料”送学习视频需要C/C++ Linux服务器架构师学习资料后台私信“资料”(资料包括C/C++,Linux,golang技术,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,ffmpeg等等。。。),免费分享

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐