Linux网络编程-3.TCP并发服务器

3.1 TCP多进程并发服务器

共享:

  • 读时共享,写时复制
  • 文件描述符
  • 内存映射区

父进程:

  • 等待接受客户端连接
    • 有连接
      – 创建一个子进程
  • 关闭通信的文件描述符

子进程:

  • 通信
  • 关闭监听的文件描述符

子进程资源回收:

  • wait()/waitpid()
  • signal
    • signal()
    • sigaction()
    • SIGCHLD

限制:

  • 硬件
  • 文件描述符上限(1024)

3.1.1 多进程伪代码

void recyle(int num)
{
	whiel(waitpid(-1, NULL, WNOHANG) > 0);
} 

int main()
{
	// 创建
	int lfd = sock();
	// 绑定
	bind();
	// 设置监听
	listen();

	// 信号回收子进程
	struct sigaction act;
	act.sa_handler = recyle;
	act.sa_flags = 0;
	sigemptyset(&act.sa_mask);
	sigaction(SIGCHLD, &act, NULL);
	
	// 父进程
	while(1)
	{
		int cfd = accept();
		// 创建子进程
		pid_t pid = fork();
		if(pid == 0)
		{
			// 子进程
			close(lfd);
			// 通信
			while(1)
			{
				int len = read();
				if(len == -1)
					exit(1);
				else if(len == 0)
				{
					close(cfd);
					break;
				}
				else
				{
					write();
				}
			}
			// 退出子进程
			return 0;
		}
		else
		{
			// 父进程
			close(cfd);
		}
	}
}

3.1.2 多进程实现代码

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <signal.h>
#include <sys/wait.h>
#include <errno.h>

void recyle(int num)
{
	pid_t pid;
	while((pid = waitpid(-1, NULL, WNOHANG)) > 0)
	{
		printf("child die, pid = %d\n", pid);
	}
} 

int main(int argc, const char * argv[])
{
	if(argc < 2)
	{
		printf("eg: ./server port\n");
		exit(1);
	}
	struct sockaddr_in serv_addr;
	socklen_t serv_len = sizeof(serv_addr);
	int port = atoi(argv[1]);

	// 创建套接字
	int lfd = socket(AF_INET, SOCK_STREAM, 0);
	// 初始化服务器 sockaddr_in
	memset(&serv_addr, 0x00, serv_len);
	serv_addr.sin_family = AF_INET;
	serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	serv_addr.sin_port = htons(port);
	// 绑定IP和端口
	bind(lfd, (struct sockaddr*)&serv_addr, serv_len);
	// 设置监听
	listen(lfd, 128);
	
	// 使用信号回收子进程
	struct sigaction act;
	act.sa_handler = recyle;
	act.sa_flags = 0;
	sigemptyset(&act.sa_mask);
	sigaction(SIGCHLD, &act, NULL);
	
	struct sockaddr_in client_addr;
	socklen_t cli_len = sizeof(client_addr);
	
	printf("Start accept ...\n");

	while(1)
	{
		// 父进程接受连接请求
		int cfd = accept(lfd, (struct sockaddr *)&client_addr, &cli_len);
		if( cfd == -1)
		{
			if(errno == EINTR)
			{
				continue;
			}
			perror("accept error");
			exit(-1);
		}
		// 创建子进程
		pid_t pid = fork();
		if(pid == 0)
		{
			// 子进程
			close(lfd);
			// 通信
			while(1)
			{
				char ip[64] = { 0 };
				printf("client IP: %s, port: %d\n", inet_ntop(AF_INET, &client_addr.sin_addr.s_addr, ip, sizeof(ip)), ntohs(client_addr.sin_port));
				char buf[1024] = { 0 };
				int len = read(cfd, buf, sizeof(buf));
				if(len == -1)
				{
					perror("read error");
					exit(1);
				}
				else if(len == 0)
				{
					printf("客户端断开了连接\n");
					close(cfd);
					break;
				}
				else
				{
					printf("recv buf: %s\n", buf);
					write(cfd, buf, len);
				}
			}
			return 0;
		}
		else if(pid > 0)
		{
			// 父进程
			close(cfd);
		}
	}
	close(lfd);
	return 0;
}

3.2 TCP多线程并发服务器

3.2.1 多线程伪代码

typedef struct sockinfo
{
	pthread_t id;
	int fd;
	struct sockaddr_in addr;
}SockInfo;

void *worker(void *args)
{
	while(1)
	{
		// 打印客户端IP和端口
		read();
		write();
	}
}

int main(int argc, const char* argv[])
{
	// 创建
	int lfd = sock();
	// 绑定
	bind();
	// 设置监听
	listen();
	
	// 父线程
	SockInfo sock[128] = { 0 };
	int i = 0;
	while(1)
	{
		sock[i].fd = accept(lfd, &client, &len);
		// 创建子线程
		pthread_create(&sock[i].id, NULL, worker, sock + i);
		pthread_detach(sock[i].id);
		i++;
	}
}

3.2.2 实现代码

#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <ctype.h>
#include <pthread.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>

typedef struct SockInfo
{
	pthread_t id;
	int fd;
	struct sockaddr_in addr;
}SockInfo;

void *worker(void *args)
{
	SockInfo* info = (SockInfo*)args;
	char ip[64] = { 0 };
	char buf[1024] = { 0 };
	while(1)
	{
		// 打印客户端IP和端口
		printf("Clinet IP: %s, port: %d\n", inet_ntop(AF_INET, &info->addr.sin_addr.s_addr, ip, sizeof(ip)), ntohs(info->addr.sin_port));
		int len = read(info->fd, buf, sizeof(buf));
		if(len == -1)
		{
			perror("read error");
			pthread_exit(NULL);
		}
		else if(len == 0)
		{
			printf("客户端断开了连接\n");
			close(info->fd);
			info->fd = -1;
			break;
		}
		else
		{
			printf("recv buf:%s\n", buf);
		}
		write(info->fd, buf, sizeof(buf));
	}
	return NULL;
}

int main(int argc, const char * argv[])
{
	if(argc < 2)
	{
		printf("eg: ./server port\n");
		exit(1);
	}
	struct sockaddr_in serv_addr;
	socklen_t serv_len = sizeof(serv_addr);
	int port = atoi(argv[1]);

	// 创建套接字
	int lfd = socket(AF_INET, SOCK_STREAM, 0);
	// 初始化服务器 sockaddr_in
	memset(&serv_addr, 0x00, serv_len);
	serv_addr.sin_family = AF_INET;
	serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	serv_addr.sin_port = htons(port);
	// 绑定IP和端口
	bind(lfd, (struct sockaddr*)&serv_addr, serv_len);
	// 设置监听
	listen(lfd, 128);
	
	socklen_t cli_len = sizeof(struct sockaddr_in);
	SockInfo info[128] = { 0 };
	int i = 0;
	for(i = 0;i < sizeof(info)/sizeof(info[0]); ++i)
	{
		info[i].fd = -1;
	}
	printf("Start accept ...\n");
	
	// 父线程
	while(1)
	{
		for(i = 0;i < sizeof(info)/sizeof(info[0]); ++i)
		{
			if(info[i].fd == -1)
				break;
		}
		if(i == 128)
			break;
		// 主线程等待接受连接请求
		info[i].fd = accept(lfd, (struct sockaddr *)&info[i].addr, &cli_len);
		// 创建子线程
		pthread_create(&info[i].id, NULL, worker, info + i);
		// 设置线程分离
		pthread_detach(info[i].id);
	}
	close(lfd);
	// 只退出主线程
	pthread_exit(NULL);
}
  • 8
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IT灰猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值