linux虚拟机与真实服务器构建,linux使用多进程实现一个TCP server,使多个客户端和服务器通信...

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

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

服务器端程序:server.c

#include #include #include #include #include #include #include #include #include #include #define SERVER_PORT 8000 //监听本机8000端口#define MAX 4096int main(void) { struct sockaddr_in serveraddr,clientaddr; int sockfd,addrlen,confd,len; char ipstr[128];char buf[4096];pid_t pid; //1.socketsockfd = socket(AF_INET,SOCK_STREAM,0);//2.bind bzero(&serveraddr,sizeof(serveraddr));//地址族协议ipv4serveraddr.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.listenlisten(sockfd,128); while(1){ //4. accept阻塞监听客户端的链接请求 addrlen = sizeof(clientaddr); confd = accept(sockfd,(struct sockaddr *)&clientaddr,&addrlen); //如果有客户端连接上服务器,就输出客户端的ip地址和端口号 printf("client ip %s\tport %d\n", 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 #include #include #include #include #include #include #include #include #include #define SERVER_PORT 8000#define MAXLINE 4096int main(void) { struct sockaddr_in serveraddr; int confd,len; char ipstr[] = "10.170.20.238";//这是服务器的地址,使用ifconfig来查看char buf[MAXLINE];//1.创建一个socketconfd = 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.关闭socketclose(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查看,如下图:

294326a2305909b5a5ebf3099e284e77.png

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

ab97dbe9bd54afc7f874c4d25fdab941.png

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

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

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值