contentprovider实现跨进程通信_linux使用多进程实现一个TCP server,使多个客户端和服务器通信...

最近看了一些零声学院的linux视频,把“多进程进行socket编程”好好理解了一下,整理出来的。

用TCP协议编写了一个简单的服务器、客户端,其中服务器一直在监听本机8000号端口。如果收到客户端的链接,就在服务器端把客户端的IP和端口号打印出来,收到客户端发送的数据,服务器会把数据变成大写并发送回客户端。要实现多个客户端连接到服务器,就需要解决阻塞问题,比如当服务器在read阻塞读客户端数据时,如果客户端没有数据到达,服务器端就会阻塞在read函数上,这时如果有新的客户端连接请求,由于服务器阻塞在read函数,就不能及时响应客户端的请求,使用多进程并发可以解决这个问题,实现多个客户端连接同一个服务器,当服务器接收到一个客户端的连接后,就fork出一个的进程去处理客户端数据,让父进程去accept接收新的客户端连接请求。代码及详细解释如下:

服务器端程序:server.c

#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <unistd.h>
#include <ctype.h> 
#define SERVER_PORT 8000 //监听本机8000端口
#define MAX 4096
int main(void)
         {	
         struct sockaddr_in serveraddr,clientaddr;
         int sockfd,addrlen,confd,len;
         char ipstr[128];
char buf[4096];
pid_t pid;	
//1.socket
sockfd = socket(AF_INET,SOCK_STREAM,0);
//2.bind	bzero(&serveraddr,sizeof(serveraddr));
//地址族协议ipv4
serveraddr.sin_family = AF_INET;
//ip地址
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
serveraddr.sin_port = htons(SERVER_PORT);
bind(sockfd,(struct sockaddr *)&serveraddr,sizeof(serveraddr));
//3.listen
listen(sockfd,128);	
while(1){	
  //4. accept阻塞监听客户端的链接请求
  addrlen = sizeof(clientaddr);	
  confd = accept(sockfd,(struct sockaddr *)&clientaddr,&addrlen);
  //如果有客户端连接上服务器,就输出客户端的ip地址和端口号
  printf("client ip %stport %dn",	
         inet_ntop(AF_INET,(struct sockaddr *)&clientaddr.sin_addr.s_addr,ipstr,sizeof(ipstr)),ntohs(clientaddr.sin_port));
  //这块是多进程的关键,当accept收到了客户端的连接之后,就创建子进程,让子进程去处理客户端  
  //发来的数据,父进程里面关闭confd(因为用不到了),然后父进程回到while循环继续监听客户端的连接
  pid = fork();
  //5. 子进程处理客户端请求
  if(pid == 0){//子进程
    close(sockfd);
    while(1){//循环读取客户端发来的数据,把小写变成大写	
      len = read(confd,buf,sizeof(buf));	
      int i = 0;
      while(i < len){	
        buf[i] = toupper(buf[i]);	
        i++;
      }	
      write(confd,buf,len);	
    }	
    close(confd);
    return 0;	
  }	
  else if(pid > 0){//父进程关闭文件描述符,释放资源	
    close(confd); 
  }
}	
return 0;
}

客户端程序:client.c

#include <netinet/in.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/stat.h>
#include <ctype.h>
#include <stdlib.h>
#define SERVER_PORT 8000
#define MAXLINE 4096
int main(void)
         {
         struct sockaddr_in serveraddr;
         int confd,len;
         char ipstr[] = "10.170.20.238";//这是服务器的地址,使用ifconfig来查看
char buf[MAXLINE];
//1.创建一个socket
confd = socket(AF_INET,SOCK_STREAM,0);	
//2.初始化服务器地址,指明我要连接哪个服务器
bzero(&serveraddr,sizeof(serveraddr));	
serveraddr.sin_family = AF_INET;	
inet_pton(AF_INET,ipstr,&serveraddr.sin_addr.s_addr);
serveraddr.sin_port = htons(SERVER_PORT);
//3.链接服务器	
connect(confd,(struct sockaddr *)&serveraddr,sizeof(serveraddr));
while(fgets(buf,sizeof(buf),stdin)){
  //4.请求服务器处理数据
  write(confd,buf,strlen(buf));
  len = read(confd,buf,sizeof(buf));	
  write(STDOUT_FILENO,buf,len);	
}
//5.关闭socket
close(confd);	
return 0;
}

我们在虚拟机下,使用一个虚拟机开启多个终端来观察效果,如果你的虚拟机联网了,就使用ifconfig命令来查看IP地址,填入客户端代码的 ipstr 数组中,这里我的ip是"10.170.20.238",也可以不联网,使用sudo ifconfig ens33 10.170.20.238 自己给虚拟机设置一个虚拟IP(关机之后这个ip就失效了)

注意:sudo ifconfig ens33 192.168.1.12 中的 ens33 每个人的虚拟机不一定一样,还是使用ifconfig查看,如下图:

6ebd6e7292bc496bd6d5f9158357bf51.png

设置好IP以后,编译server.c和client.c文件,然后开多个终端执行,执行结果如下,开了一个服务器,三个客户端,服务器能正确处理三个客户端的请求。(客户端的端口号是随机的)

3c2b658433b885c84117a5c2ee752092.png

上图是开了三个客户端的,那服务器到底能连接多少个客户端呢?这取决于服务器端机器的内存和性能

这个程序存在两个问题,一个是出错处理,为了理解方便我就没加,还有一个就是子进程回收问题,子进程回收我们一般可以使用wait或waitpid让父进程去回收子进程资源,但是服务器端的父进程在等待客户端的连接请求,没办法去回收,还可以使用信号去回收子进程,子进程退出时会给父进程发送SIGCHLD信号,我们可以在信号处理函数里面去回收子进程,但是,执行信号处理函数,会打断父进程的accept,这样就又没法及时响应客户端的连接了,所以我们还是使用waitpid,多创建一个进程通过指定waitpid的第一个参数为-1(回收指定进程组内的任意子进程),专门用来回收子进程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值