网络基础 TCP多进程/多线程服务器

TCP协议

TCP协议段格式

  • 源/目的端口号: 表示数据是从哪个进程来, 到哪个进程去;
  • 32位序号/32位确认号: 以后再了解
  • 4位TCP报头⻓度: 表⽰该TCP头部有多少个32位bit(有多少个4字节); 所以TCP头部最⼤⻓度是15 * 4 =60
  • 6位标志位:
  1. URG: 紧急指针是否有效
  2. ACK: 确认号是否有效
  3. PSH: 提示接收端应⽤程序立刻从TCP缓冲区把数据读走
  4. RST: 对方要求重新建立连接; 我们把携带RST标识的称为复位报⽂段
  5. SYN: 请求建立连接; 我们把携带SYN标识的称为同步报文段
  6. FIN: 通知对方, 本端要关闭了, 我们称携带FIN标识的为结束报⽂段
  • 16位窗口大小: 描述目前接受能力,填写的是自己的接受缓冲区大小
  • 16位校验和: 发送端填充, CRC校验. 接收端校验不通过, 则认为数据有问题. 此处的检验和不光包含TCP首部, 也包含TCP数据部分.
  • 16位紧急指针: 标识哪部分数据是紧急数据;
  • 40字节头部选项: 暂时忽略;

编写TCP多进程服务器:

服务器代码:

#include<stdio.h>
#include<unistd.h>	//unistd.h 是 C 和 C++ 程序设计语言中提供对 POSIX 操作系统 API 的访问功能的头文件的名称。
#include<stdlib.h>	//tdlib.h里面定义了五种类型、一些宏和通用工具函数。 类型例如size_t,宏例如EXIT_FAILURE,常用的函数如malloc()、atoi()
#include<string.h>				
#include<sys/socket.h>			
#include<netinet/in.h>	//socketaddr_in 结构体 htons系统调用
#include<arpa/inet.h>	//htonl, htons之类 int_addr	
#include<sys/wait.h>  	//使用wait()和waitpid()函数时需要


void Request(int client_fd,struct sockaddr_in* client_addr)
{
	 char buf[1024] = {0};
	 while(1)
	 {
		 ssize_t size = read(client_fd,buf,sizeof(buf));
		 if(size < 0)
		 {
		 	perror("read");
			continue;
		 }

		if(strncasecmp(buf,"quit",4) == 0 || size == 0)
		{
			printf("=============client [%s] quit!===========\n",inet_ntoa(client_addr->sin_addr));
			break;
		}
//		 if(size == 0)
//		 {
//		 	printf("client:%s is exited!\n",inet_ntoa(client_addr->sin_addr));
//			close(client_fd);
//			break;
//		 }
	 	buf[size] = '\0';
		printf("client %s say:%s\n",inet_ntoa(client_addr->sin_addr),buf);
		write(client_fd,buf,strlen(buf));
	}
	 return;
}



void CreateWorker(int client_fd,struct sockaddr_in* client_addr)
{
	pid_t pid = fork();
	if(pid < 0)
	{
		perror("fork");
		return;
	}
	else if(pid == 0)
	{
		//child
		if(fork() == 0)
		{
			//grand_child
			//此处创建孙子进程使子进程退出,是因为孙子进程有init回收
			Request(client_fd,client_addr);
		}
		exit(0);
	}
	else
	{
		//father
		close(client_fd);
		waitpid(pid,NULL,0);
	}
}

int main(int argc,char* argv[])
{
	if(argc != 3)	
	{
		// ./service 192.168.1.113 88888
		printf("Usage:%s [IP] [port]\n",argv[0]);
		return 1;
	}

	struct sockaddr_in addr;
	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = inet_addr(argv[1]);
	addr.sin_port = htons(atoi(argv[2]));

	int fd = socket(AF_INET,SOCK_STREAM,0);
	if(fd < 0)
	{
		perror("socket");
		return 2;
	}
	
	if(bind(fd,(struct sockaddr*)&addr,sizeof(addr)) < 0)
	{
		perror("bind");
		return 3;
	}

	if(listen(fd,5) < 0)
	{
		perror("listen");
		return 4;
	}
	
	while(1)
	{
		struct sockaddr_in client_addr;
		socklen_t len = sizeof(client_addr);
		int client_fd = accept(fd,(struct sockaddr*)&client_addr,&len);
		if(client_fd < 0)
		{
			perror("accept");
			continue;
		}
		printf("########## %s connected!##########\n",inet_ntoa(client_addr.sin_addr));

		CreateWorker(client_fd,&client_addr);	
	}
	return 0;
}

Makefile:

.PHONY:all
all:service client
service:service.c
	gcc -o $@ $^
client:client.c
	gcc -o $@ $^ -static
.PHONY:clean
clean:
	rm -f service client

多进程服务器运行截图:

    

如图可见有2个客户端连接到服务器。


客户端可以输入quit退出连接。


编写TCP多线程服务器:

服务器代码:    

#include<stdio.h>
#include<unistd.h>				
#include<stdlib.h>
#include<string.h>				
#include<sys/socket.h>			
#include<netinet/in.h>			
#include<arpa/inet.h>			
#include<sys/wait.h>  			


void Request(int client_fd,struct sockaddr_in* client_addr)
{
	 char buf[1024] = {0};
	 while(1)
	 {
		 ssize_t size = read(client_fd,buf,sizeof(buf));
		 if(size < 0)
		 {
		 	perror("read");
			continue;
		 }

		if(strncasecmp(buf,"quit",4) == 0 || size == 0)
		{
			printf("=============client [%s] quit!===========\n",inet_ntoa(client_addr->sin_addr));
			break;
		}
	 	buf[size] = '\0';
		printf("client %s say:%s\n",inet_ntoa(client_addr->sin_addr),buf);
		write(client_fd,buf,strlen(buf));
	}
	 return;
}

typedef struct Arg
{
	int fd;
	struct sockaddr_in addr;
}Arg;

void* CreateWorker(void* ptr)
{
	Arg* arg = (Arg*)ptr;
	Request(arg->fd,&arg->addr);
	free(arg);
}

int main(int argc,char* argv[])
{
	if(argc != 3)	
	{
		// ./server 192.168.1.113 88888
		printf("Usage:%s [IP] [port]\n",argv[0]);
		return 1;
	}

	struct sockaddr_in addr;
	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = inet_addr(argv[1]);
	addr.sin_port = htons(atoi(argv[2]));

	int fd = socket(AF_INET,SOCK_STREAM,0);
	if(fd < 0)
	{
		perror("socket");
		return 2;
	}
	
	if(bind(fd,(struct sockaddr*)&addr,sizeof(addr)) < 0)
	{
		perror("bind");
		return 3;
	}

	if(listen(fd,5) < 0)
	{
		perror("listen");
		return 4;
	}

	
	while(1)
	{
		struct sockaddr_in client_addr;
		socklen_t len = sizeof(client_addr);
		int client_fd = accept(fd,(struct sockaddr*)&client_addr,&len);
		if(client_fd < 0)
		{
			perror("accept");
			continue;
		}
		printf("########## %s connected!##########\n",inet_ntoa(client_addr.sin_addr));

		pthread_t tid = 0;
		Arg* arg = (Arg*)malloc(sizeof(Arg));
		arg->fd = client_fd;
		arg->addr = client_addr;
		pthread_create(&tid,NULL,CreateWorker,(void*)arg);
		pthread_detach(tid); //分离线程
	}
	return 0;
}

Makefile:多线程的makefile切记要加-lpthread选项。

.PHONY:all
all:server client
server:server.c
	gcc -o $@ $^ -lpthread
client:client.c
	gcc -o $@ $^ -static 
.PHONY:clean
clean:
	rm -f server client




多进程与多线程客户端代码相同,如下:

#include<stdio.h>
#include<unistd.h>				
#include<stdlib.h>				
#include<string.h>				
#include<sys/socket.h>			
#include<netinet/in.h>
#include<arpa/inet.h>				
#include<sys/wait.h>  			



int main(int argc,char* argv[])
{
	if(argc != 3)
	{
		printf("Usage: %s [IP] [port]\n",argv[0]);
		return 1;
	}
	char buf[1024];
	memset(buf,'\0',sizeof(buf));

	struct sockaddr_in service_addr;
	int sock = socket(AF_INET,SOCK_STREAM,0);
	bzero(&service_addr,sizeof(service_addr));
	service_addr.sin_family = AF_INET;
	inet_pton(AF_INET,argv[1],&service_addr.sin_addr);	
	service_addr.sin_port = htons(atoi(argv[2]));


	int ret = connect(sock,(struct sockaddr*)&service_addr,sizeof(service_addr));
	if(ret < 0)
	{
		printf("connect failed...\n");
		return 1;
	}
	printf("connect success...\n");
	while(1)
	{
		printf("client#:");
		fgets(buf,sizeof(buf),stdin);
		buf[strlen(buf) - 1] = 0;
		write(sock,buf,sizeof(buf));	
		//用来比较s1与s2前n个字符大小,若字符串相同则返回0
		if(strncasecmp(buf,"quit",4) == 0)
		{
			printf("quit!\n");
			break;
		}
		printf("please wait...\n");
		read(sock,buf,sizeof(buf));
		printf("service#:%s\n",buf);
	}
	close(sock);

	return 0;
}


总结两种服务器优缺点:

多进程服务器缺点:

  • 客户连接后才创建进程
  • 多进程服务器吃资源,只能服务有限个客户
  • 多进程服务器跟随进程增多cpu压力增大,性能下降

多进程服务器优点:

  • 可处理多个客户
  • 编写周期短,代码简单
  • 稳定性强,一个进程挂掉不会影响其他进程

多线程服务器缺点:

  • 客户连接后才创建进程
  • 多进程服务器吃资源,只能服务有限个客户
  • 多进程服务器跟随进程增多cpu压力增大,性能下降
  • 多线程服务器稳定性较差,一个线程挂掉,全部挂掉

多线程服务器优点:

  • 可处理多个客户
  • 编写周期短,代码简单

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: TCP多进程并发服务器多进程客户端是一种网络编程模型,用于实现高并发的网络通信。在这种模型中,服务器和客户端都使用多个进程来处理并发连接请求,从而提高系统的处理能力和响应速度。 TCP多进程并发服务器通常采用多线程多进程的方式来处理客户端连接请求,每个线程或进程负责处理一个客户端连接。当有新的连接请求到达时,服务器会创建一个新的线程或进程来处理该连接,从而实现并发处理多个连接的能力。 多进程客户端也是类似的,它可以同时与多个服务器建立连接,并使用多个进程来处理这些连接。每个进程负责处理一个连接,当连接结束时,进程会被释放,从而释放系统资源。 总的来说,TCP多进程并发服务器多进程客户端是一种高效的网络编程模型,可以提高系统的处理能力和响应速度,适用于需要处理大量并发连接的网络应用场景。 ### 回答2: TCP多进程并发服务器是指服务器可以同时处理多个客户端的请求,服务器不会阻塞在单个请求上。每当一个客户端连接到服务器服务器会启动一个新的进程来处理该客户端的请求。这些进程是独立的,可以同时运行,每个进程只负责处理与其连接的客户端请求,从而实现了服务器的并行处理。 多进程客户端是指客户端同时启动多个子进程或线程进行数据请求,每个子进程或线程各自独立工作,可以同时向服务器发送请求,提高了客户端的处理能力和请求效率。 TCP多进程并发服务器多进程客户端都是为了提高请求处理的效率,特别是在高并发情况下。但是,两者的实现方式有所不同。多进程并发服务器更加复杂,需要合理分配进程资源,防止进程过多导致系统负载过高;而多进程客户端相对简单,只需要注意并发请求的并发数限制就可以了。 此外,在实际应用过程中还需要注意以下几点: 1.多进程并发服务器多进程客户端需要考虑进程的创建、销毁和通信机制。 2.在高并发情况下,需要考虑进程和线程的竞争和锁机制。 3.需要合理分配资源,避免进程资源浪费和系统负载过高。 4.需要优化网络传输和数据处理算法,提升效率。 总之,TCP多进程并发服务器多进程客户端都是应对高并发请求的有效手段,但是在实际应用中需要考虑多方面因素,避免出现不必要的问题。 ### 回答3: TCP多进程并发服务器多进程客户端是在网络编程中广泛使用的技术,主要是为了解决服务器和客户端在处理多个连接请求时的效率和并发性问题。下面简要介绍这两种技术的概念、实现和优缺点。 TCP多进程并发服务器是指在一个主进程中创建多个子进程,每个子进程负责处理一个客户端的连接请求。当有多个客户端同时连接到服务器时,每个客户端连接都会创建一个子进程来处理其请求。子进程之间是独立的,相互之间不会干扰,从而保证了服务器的并发处理能力和效率。同时,由于每个子进程只处理一个客户端连接请求,从而降低了进程间的通信开销,提高了响应速度。 TCP多进程客户端是指在一个主进程中创建多个进程,每个进程负责向服务器发起连接请求,进行数据交换。当多个客户端同时连接到服务器时,它们之间是独立的,相互之间不会干扰,从而保证了客户端的并发处理能力和效率。 对于TCP多进程并发服务器多进程客户端,其实现可以使用fork系统调用或者并发编程框架如Python的multiprocessing模块等实现。在编写程序时,需要注意进程间通信和数据传输的问题,如进程间的互斥、同步和共享资源问题等。 优点: 1. 服务器端和客户端都具有良好的并发处理能力,可以同时处理多个连接请求。 2. 多进程之间相互独立,不会相互干扰,保证了系统的稳定性和可靠性。 3. TCP协议本身具有可靠性和稳定性等优点,保证了数据传输的准确性和完整性。 缺点: 1. 多进程之间的切换和数据交换会增加系统的开销和负担,可能会影响系统性能。 2. 程序的编写复杂度较高,需要考虑进程间通信、同步和共享资源等问题。 3. 程序的调试和维护较为困难,需要考虑多个进程之间的相互影响。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值