这是Linux下进程间通信(IPC)系列的第三篇也是最后一篇文章。第一篇文章通过共享存储(文件和共享内存段)关注IPC,第二篇文章使用管道(无名或命名)和消息队列来实现相同的目标。本文重点介绍从高度(插槽)到低(信号)的IPC。代码示例将强有力地丰富下面的解释。
插座
正如管道有两种类型(命名和未命名),有两种类型的套接字。 IPC套接字(即Unix套接字)在同一设备(主机)上提供基于进程通道的通信功能;网络套接字使进程能够在不同的主机上运行,从而也具有通过网络进行通信的能力。网络套接字需要来自底层协议的支持,例如TCP(传输控制协议)或UDP(用户数据报协议)。
相比之下,IPC套接字依靠本地系统内核的支持进行通信;特别是,IPC通信使用本地文件作为套接字地址。虽然这两个套接字的实现是不同的,但实质上,IPC套接字和网络套接字的API是相同的。下一个示例将包含网络套接字的内容,但示例服务器和客户端程序可以在同一台计算机上运行,因为服务器使用localhost(127.0.0.1)网络地址,该地址在本地计算机上表示。本地机器地址。
套接字在配置为双向的流(下面讨论)中配置,并且它们的控制遵循C/S(客户端/服务器)模式:客户端通过尝试连接到服务器来启动对话,而服务器将尝试接受连接。如果一切顺利,来自客户端的请求和来自服务器的响应将通过管道传输,直到其中任何一个关闭通道,断开连接。
迭代服务器(仅用于开发)将始终处理连接到它的客户端:从一开始,第一个客户端,然后关闭连接,然后是第二个客户端,以及循环。这种方法的一个缺点是处理特定客户端可能会挂起,导致其他客户端等待。生产级服务器将是并发的,通常使用多进程或多线程组合。例如,我桌面上的Nginx Web服务器有一个由4个工作者组成的进程池,可以同时处理客户端请求。在下面的代码示例中,我们将使用一个迭代服务器来保持我们正在处理的问题,只关注底层API,而不是并发问题。最后,随着各种POSIX改进的出现,套接字API随着时间的推移发生了巨大变化。有意编写服务器和客户端的当前示例代码,但它突出显示了基于流的套接字中连接的两个方面。以下是流控制的摘要,其中服务器在一个终端中打开,客户端在另一个终端中打开:
服务器等待客户端的连接,对于给定的成功连接,它从客户端读取数据。
为了强调双方之间的对话,服务器将响应从客户端接收的数据。这些数据都是构成某些书籍标题的ASCII字符代码。
客户端将书的标题写入服务器端进程,并从服务器端响应中读取相同的标题。客户端和服务器都在屏幕上打印标题。下面是服务器端输出,客户端的输出完全相同:
在客户端听9876端口.
战争与和平
傲慢与偏见
喧哗与骚动
示例1.使用套接字的客户端程序
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#include'sock.h'
Void报告(const char * msg,int terminate){
PERROR(MSG);
如果(终止)退出(-1);/*失败* /
}
Int main(){
Int fd=socket(AF_INET,/*网络与AF_LOCAL * /
SOCK_STREAM,/*可靠,双向: TCP * /
0);/*系统选择底层协议* /
如果(fd <0)报告('socket',1);/*终止* /
/*将服务器的本地地址绑定在内存中* /结构sockaddr_in saddr;
Memset(&saddr,0,sizeof(saddr));/*清除字节* /
Saddr.sin_family=AF_INET;/*与AF_LOCAL * /
Saddr.sin_addr.s_addr=htonl(INADDR_ANY);/* host-to-network endian * /
Saddr.sin_port=htons(PortNumber);/*用于收听* /
if(bind(fd,(struct sockaddr *)&saddr,sizeof(saddr))<0)
报告('bind',1);/*终止* /
/*听插座* /
如果(listen(fd,MaxConnects)<0)/*监听客户端,最多MaxConnects * /
报告('听',1);/*终止* /
Fprintf(stderr,'收听客户端的端口%i . \ n',PortNumber);
/*服务器传统上无限期地监听* /
虽然(1){
结构sockaddr_in caddr;/*客户地址* /
Int len=sizeof(caddr);/*地址长度可能会改变* /
Int client_fd=accept(fd,(struct sockaddr *)&caddr,&len);/*接受块* /
if(client_fd <0){
报告('接受',0);/*没有终止,虽然有问题* /继续;
}
/*从客户端读取* /
Int i;
For(i=0; i
字符缓冲区[BuffSize + 1];
Memset(缓冲区,'\ 0',sizeof(缓冲区));
Int count=read(client_fd,buffer,sizeof(buffer));
如果(计数>
0){
放(缓冲液);
写(client_fd,buffer,sizeof(buffer));/*回显为确认* /
}
}
关闭(client_fd);/*中断连接* /
}/* while(1)* /
返回0;
}
上述服务器端程序执行典型的四个步骤以准备响应客户端的请求,然后接受其他独立请求。此处的每个步骤都以服务器端程序调用的系统函数命名。
套接字(.):获取套接字连接的文件描述符
绑定(.):将套接字绑定到服务器主机上的地址
听(.):听取客户请求
接受(.):接受特定的客户请求
上述套接字调用的完整形式是:
Int sockfd=socket(AF_INET,/*与AF_LOCAL * /
SOCK_STREAM,/*可靠,双向* /
0);/*系统选择协议(TCP)* /
第一个参数具体指定使用网络套接字而不是IPC套接字。第二个参数有几个选项,但SOCK_STREAM和SOCK_DGRAM(数据报)是最常用的选项。基于流的套接字支持可信通道,如果报告,则会丢失或更改信息。该信道是双向的,从一端到另一端的有效载荷的大小可以是任意的。相反,基于数据报的套接字大多不可信,没有方向性,需要固定大小的有效载荷。 socket的第三个参数指定协议。对于此处显示的基于流的套接字,只有一个协议选择:TCP,此处表示为0.因为成功调用套接字将返回类似的文件描述符,可以读取和写入套接字,以及相应的语法类似于读写本地文件。