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 %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 <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查看,如下图:

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

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

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

  • 10
    点赞
  • 83
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值