在单进程下进行socket的编程,服务器通过accept()获取到客户端的文件描述符,并且与该客户端进行交互。但是实际有两方面的因素都促使服务器应该能够同时与多个客户端进行交互。
1.listen()函数将已经完成三次握手和即将完成三次握手的客户端文件描述符存放到队列中。 2.在实际客户端与服务器进行交互时,服务器必须能够同时与多个客户端进行交互。
多进程编程的优势: 1.能够处理同时处理多个客户端的连接,并且能够与客户端进行交互。 2.子进程可以共享fork()之间父进程打开的文件描述符,而不需要在进程之间传递文件描述符,简化了编程。 3.服务器处理的每个客户连接都是相互独立的,因为进程之间是相互独立的。。 4.任务分派明确,父进程只需处理客户端的连接,子进程只需要与客户端进程交互即可。
多进程服务器端代码:
代码语言:javascript复制#include <sys/scoket.h>
#include <arpa/inet>
#include <netinet/in.h>
#include <assert.h>
#Include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include <stdlib.h>
void zmobie(){
wait(NULL);
}
void communicateWithCli(int c){
char buff[128] = {0};
while(1){
int n = recv(c,buff,127,0);
if(n<=0){
printf("one client quit!");
break;
}
printf("%d : %s",c,buff);
send(c,"OK",2,0);
}
}
int main(){
signal(SIGCHLD,zmobie);
int fd = socket(AF_INET,SOCK_STREAM,0);
assert(fd!=-1);
struct sockaddr_in cli,ser;
ser.sin_family = AF_INET;
ser.sin_port = htons(5000);
ser.sin_addr.s_addr = inet_addr("127.0.0.1");
int res = bind(fd,(struct sockaddr*)&ser,sizeof(ser));
assert(res!=-1);
res = listen(fd,5);
assert(res!=-1);
while(1){
socklen_t len = sizeof(cli);
int c = accept(fd,(struct sockaddr*)&cli,&len);
if(c==-1){
printf("link error!");
continue;//继续进行连接
}
pid_t pid = fork();
assert(pid!=-1);
//子进程
if(pid==0){
communicateWithCli(c);
exit(0);//向父亲发出信号,避免成为僵死进程,因为父进程是一直启动的
}
//父进程
else{
close(c);
}
}
close(fd);
return 0;
}
执行结果:
从执行结果可以看出,有四个客户端可以同时和服务器进行交互,通过多进程的方式就解决了在最前提出的问题。
值得探究的是为什么每次服务器端输出的客户端的文件描述符都是4?
为什么在父进程中需要关闭客户端的文件描述符? 文件描述符作为进程的资源是有限的,处于C/S的模式下,主进程只需要负责与客户端连接,而不需要去做具体的逻辑处理。连接之后就不需要该客户端的文件描述符了,具体与客户端进行交互的进程中已经拿到对应的文件描述符了。因此,在父进程中关闭客户端的文件描述符可以减少不必要的资源浪费。
基于C/S模式,采用多进程编程的缺点 1.在系统可以打开的进程是有限个数的,为每一个客户端分配一个进程开销太大,如果客户端占着不用,或者只有少量数据的传输,开进程是划不来的。 2.一个客户端独占一个进程,资源浪费。 3.当客户端退出时,子进程结束。 4.如果由大量的客户端同时连接服务器,服务器可能会崩溃。