Linux网络编程:TCP并发服务器实现

目录

1、前言

2、多进程代码实现

2.1 创建新的进程

2.2 客户端接收响应函数

2.3 僵尸进程处理

2.4 完整代码

2.5 代码测试  

3、多线程代码实现

3.1 创建新的线程 

3.2 线程函数定义

3.3 完整代码

3.4 代码测试

4、总结


1、前言

前面实现了基本的TCP编程,Linux网络编程:TCP编程实现-CSDN博客,但是存在多个客户端去连接同一个服务器的情况,这时之前编写的基础TCP服务器连接一个客户端后就无法再与其他客户端建立连接,这是就需要考虑并发设计。

2、多进程代码实现

2.1 创建新的进程

若返回的pid小于0,则创建失败退出;

若返回的pid等于0,则为子进程,关闭服务器绑定socket文件描述符;

若返回的pid大于0,则为父进程,关闭客户端绑定socket文件描述符。

if((pid = fork())<0)
{
    perror("accept");
    exit(0);
}
else if(pid == 0)
{
    close(fd);
    ClientHandle(newfd);
    exit(0);
}
else if(pid > 0)
{
    close(newfd);
}

2.2 客户端接收响应函数

每次创建子进程后进行客户端接收,读取客户端绑定socket文件描述符的buffer,随后进行关闭客户端绑定socket文件描述符。

void ClientHandle(int newfd)
{
	int ret;
	char buf[BUFSIZ] = {};//BUFSIZ 8142
	while(1)
	{
		memset(buf,0,BUFSIZ);
		ret = read(newfd,buf,BUFSIZ);
		if(ret < 0 )
		{
			perror("read");
			exit(0);
		}
		else if(ret == 0)
			break;
		printf("buf = %s\n",buf);
	}
	close(newfd);
}

2.3 僵尸进程处理

 当客户端与服务器连接后,终止客户端进程后,服务器的子进程会变成僵尸进程,所以要进行僵尸进程的回收。

子进程终止时会向父进程发送SIGCHLD信号,告知父进程回收自己,但该信号的默认处理动作为忽略,因此父进程仍然不会去回收子进程,需要捕捉处理实现子进程的回收;

进行信号机制的绑定,进行子进程终止信号的接收

signal(SIGCHLD,SigHandle);

实现僵尸进程接收函数

void SigHandle(int sig)
{
	if(sig == SIGCHLD)
	{
		printf("Client exited\n");
		wait(NULL);
	}
}

2.4 完整代码

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

#define BACKLOG 5
void SigHandle(int sig)
{
	if(sig == SIGCHLD)
	{
		printf("Client exited\n");
		wait(NULL);
	}
}
void ClientHandle(int newfd);
int main(int argc,char *argv[])
{
	int fd,newfd;
	struct sockaddr_in addr,client_addr;
	socklen_t addrlen = sizeof(client_addr);

	signal(SIGCHLD,SigHandle);
	pid_t pid;

	if(argc < 3)
	{
		printf("%s<addr><port>\n",argv[0]);
		exit(0);
	}

	/*创建套接字*/
	fd = socket(AF_INET,SOCK_STREAM,0);
	if(fd < 0)
	{
		perror("socket");
		exit(0);
	}
	addr.sin_family = AF_INET;
	addr.sin_port = htons(atoi(argv[2]));
	if(inet_aton(argv[1],&addr.sin_addr)==0)
	{
		fprintf(stderr,"Invalid address\n");
		exit(0);
	}
	/*地址快速重用*/
	int flag = 1,len = sizeof(int);
	if(setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&flag,len)==-1)
	{
		perror("setsockopt");
		exit(1);
	}
	/*绑定通信结构体*/
	if(bind(fd,(struct sockaddr *)&addr,sizeof(addr)) == -1)
	{
		perror("bind");
		exit(0);
	}
	/*设置套接字为侦听模式*/
	if(listen(fd,BACKLOG) == -1)
	{
		perror("listen");
		exit(0);
	}

	while(1)
	{
		/*接受客户端的连接请求,生成新的用于和客户端通信的套接字*/
		newfd = accept(fd,(struct sockaddr *)&client_addr,&addrlen);
		if(newfd < 0)
		{
			perror("accept");
			exit(0);
		}
		printf("addr:%s port:%d\n",inet_ntoa(client_addr.sin_addr),ntohs(client_addr.sin_port));
		if((pid = fork())<0)
		{
			perror("accept");
			exit(0);
		}
		else if(pid == 0)
		{
			close(fd);
			ClientHandle(newfd);
			exit(0);
		}
		else if(pid > 0)
		{
			close(newfd);
		}
	}
	close(fd);
	return 0;
}
void ClientHandle(int newfd)
{
	int ret;
	char buf[BUFSIZ] = {};//BUFSIZ 8142
	while(1)
	{
		memset(buf,0,BUFSIZ);
		ret = read(newfd,buf,BUFSIZ);
		if(ret < 0 )
		{
			perror("read");
			exit(0);
		}
		else if(ret == 0)
			break;
		printf("buf = %s\n",buf);
	}
	close(newfd);
}

2.5 代码测试  

3、多线程代码实现

3.1 创建新的线程 

进行线程创建后进行线程分离 

pthread_create(&tid,NULL,ClientHandle,&newfd);
pthread_detach(tid);

3.2 线程函数定义

每次创建子进程后进行客户端接收,读取客户端绑定socket文件描述符的buffer,随后进行关闭客户端绑定socket文件描述符。 

void *ClientHandle(void *arg)
{
	int ret;
	char buf[BUFSIZ] = {};//BUFSIZ 8142
	int newfd = *(int *)arg;
	while(1)
	{
		memset(buf,0,BUFSIZ);
		ret = read(newfd,buf,BUFSIZ);
		if(ret < 0 )
		{
			perror("read");
			exit(0);
		}
		else if(ret == 0)
			break;
		printf("buf = %s\n",buf);
	}
	printf("client exit\n");
	close(newfd);
	return NULL;
}

3.3 完整代码

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

#define BACKLOG 5
void *ClientHandle(void *arg);
int main(int argc,char *argv[])
{
	int fd,newfd;
	struct sockaddr_in addr,client_addr;
	pthread_t tid;
	socklen_t addrlen = sizeof(client_addr);

	if(argc < 3)
	{
		printf("%s<addr><port>\n",argv[0]);
		exit(0);
	}

	/*创建套接字*/
	fd = socket(AF_INET,SOCK_STREAM,0);
	if(fd < 0)
	{
		perror("socket");
		exit(0);
	}
	addr.sin_family = AF_INET;
	addr.sin_port = htons(atoi(argv[2]));
	if(inet_aton(argv[1],&addr.sin_addr)==0)
	{
		fprintf(stderr,"Invalid address\n");
		exit(0);
	}
	/*地址快速重用*/
	int flag = 1,len = sizeof(int);
	if(setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&flag,len)==-1)
	{
		perror("setsockopt");
		exit(1);
	}
	/*绑定通信结构体*/
	if(bind(fd,(struct sockaddr *)&addr,sizeof(addr)) == -1)
	{
		perror("bind");
		exit(0);
	}
	/*设置套接字为侦听模式*/
	if(listen(fd,BACKLOG) == -1)
	{
		perror("listen");
		exit(0);
	}

	while(1)
	{
		/*接受客户端的连接请求,生成新的用于和客户端通信的套接字*/
		newfd = accept(fd,(struct sockaddr *)&client_addr,&addrlen);
		if(newfd < 0)
		{
			perror("accept");
			exit(0);
		}
		printf("addr:%s port:%d\n",inet_ntoa(client_addr.sin_addr),ntohs(client_addr.sin_port));
		pthread_create(&tid,NULL,ClientHandle,&newfd);
		pthread_detach(tid);
	}
	close(fd);
	return 0;
}
void *ClientHandle(void *arg)
{
	int ret;
	char buf[BUFSIZ] = {};//BUFSIZ 8142
	int newfd = *(int *)arg;
	while(1)
	{
		memset(buf,0,BUFSIZ);
		ret = read(newfd,buf,BUFSIZ);
		if(ret < 0 )
		{
			perror("read");
			exit(0);
		}
		else if(ret == 0)
			break;
		printf("buf = %s\n",buf);
	}
	printf("client exit\n");
	close(newfd);
	return NULL;
}

3.4 代码测试

4、总结

本文通过多进程和多线程技术进行的TCP并发服务器的实现,在多进程方式下,解决了僵尸进程的问题。 最后通过完成代码的编写并测试,成功实现了TCP并发服务器。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/609952.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

理解机器学习中的类别不平衡问题

大家好&#xff0c;实际世界的数据集通常是杂乱的,当不同类别之间的样本分布不均匀时&#xff0c;就会出现类别不平衡。或者说&#xff0c;某些类别的样本比其他类别多得多。例如&#xff0c;考虑一个信用卡违约数据集&#xff0c;信用卡违约是一个相对较少发生的事件&#xff…

Java入门基础学习笔记2——JDK的选择下载安装

搭建Java的开发环境&#xff1a; Java的产品叫JDK&#xff08;Java Development Kit&#xff1a; Java开发者工具包&#xff09;&#xff0c;必须安装JDK才能使用Java。 JDK的发展史&#xff1a; LTS&#xff1a;Long-term Support&#xff1a;长期支持版。指的Java会对这些版…

Sass语法介绍-变量介绍

02 【Sass语法介绍-变量】 sass有两种语法格式Sass(早期的缩进格式&#xff1a;Indented Sass)和SCSS(Sassy CSS) 目前最常用的是SCSS&#xff0c;任何css文件将后缀改为scss&#xff0c;都可以直接使用Sassy CSS语法编写。 所有有效的 CSS 也同样都是有效的 SCSS。 Sass语…

javaMail快速部署——发邮件喽~

目录 功能阐述 前序步骤 &#xff08;1&#xff09;到QQ邮箱中获取到授权码 代码实现 坑 今天在写一个修改密码的功能的时候要用到邮箱的发送&#xff0c;然后因为这个项目比较老旧了&#xff0c;采用的是javaWeb和jsp的配置&#xff0c;对于我只使用过springBoot整合的ja…

京东手势验证码-YOLO姿态识别+Bézier curve轨迹拟合

这次给老铁们带来的是京东手势验证码的识别。 目标网站&#xff1a;https://plogin.m.jd.com/mreg/index 验证码如下图: 当第一眼看到这个验证码的时候&#xff0c;就头大了&#xff0c;这玩意咋识别&#xff1f;&#xff1f;&#xff1f; 静下心来细想后的一个方案&#xf…

JavaWeb中的Session和Cookie

前言 什么是会话跟踪技术 Cookie 1.什么是cookie 2.Cookie的应用 2.1 保持用户登录状态 2.2 记录用户名 3. Cookie的设置和获取 3.1 、通过HttpServletResponse.addCookie的方式设置Cookie 3.2、浏览器中查看cookie的内容 3.3、服务端获取客户端携带的cookie&#xf…

240+ Stylized Arctic Textures - Snow, Ice More

240+风格化的雪、冰、雪岩和其他雪纹理的集合,用于北极风格化/幻想/rpg风格的游戏环境。 在这个系列中,你会在风格化/幻想/rpg风格的游戏中找到大量适合北极和其他雪地环境的纹理——雪、冰、雪地岩石、雪地草、雪地砾石、雪地等等! 每个纹理都是可平铺/无缝的,并与各种不同…

C++语法|进程虚拟地址空间和函数调用栈

本文来自施磊老师的课程&#xff0c;老师讲的非常不错&#xff0c;我的笔记也是囫囵吞枣全部记下&#xff0c;但是我在这里推荐一本书&#xff0c;真的真的建议初学C或者想要进阶C的同学们看看&#xff1a;《CPU眼里的C/C》 文章目录 进程的虚拟地址空间和布局进程虚拟地址空间…

布隆过滤器和黑名单,解决Redis缓存穿透

目录 1.什么是布隆过滤器&#xff1f; 2.布隆过滤器的原理 3.空间计算 4.布隆过滤器的视线场景&#xff1a; 5.在Spring Boot中集成Redisson实现布隆过滤器 6、Redisson实现布隆过滤器 6.1导入依赖 6.2使用 布隆过滤器&#xff08;Bloom Filter&#xff09;是1970年由布…

邮件大附件系统如何进行安全、高效的大附件发送?

邮件大附件系统是一套解决传统电子邮件系统&#xff0c;在发送大文件时遇到限制的解决方案。由于传统电子邮件系统通常对附件大小有限制&#xff0c;这使得发送大文件变得困难。邮件大附件系统通过各种技术手段&#xff0c;允许用户发送超过传统限制的大文件&#xff0c;通常在…

修改latex中block中公式与block标题间隔过大的问题

修改block中公式与block间隔过大的问题 如图的block中公式出现了空白:代码见下方 \begin{proof}[证明]\begin{align*}&Z\alpha \beta _XX\beta _YY\varepsilon \rightarrow XZ\alpha X\beta _XX^2\beta _YXY\varepsilon X&\\&E\left( Z \right) \alpha \beta _XE\…

【Java】Java中栈溢出的常见情况及解决方法

人不走空 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌赋&#xff1a;斯是陋室&#xff0c;惟吾德馨 目录 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌…

同创优配正规炒股A股三大指数集体收涨 创指重回1900点关口

查查配5月9日电 周四,A股三大指数震荡上扬。截至收盘,上证指数涨0.83%,报3154.32点;深证成指涨1.55%,报9788.07点;创业板指涨1.87%,报1900.01点。总体上个股涨多跌少,全市场超4200只个股上涨。沪深两市今日成交额9011亿元,较上个交易日放量367亿元。 同创优配是AAA 级诚信经营…

Spring JdbcTemplate实现自定义动态sql拼接功能

需求描述&#xff1a; sql 需要能满足支持动态拼接&#xff0c;包含 查询字段、查询表、关联表、查询条件、关联表的查询条件、排序、分组、去重等 实现步骤&#xff1a; 1&#xff0c;创建表及导入测试数据 CREATE TABLE YES_DEV.T11 (ID BINARY_BIGINT NOT NULL,NAME VARCH…

栈结构(c语言)

1.栈的概念 栈&#xff1a;一种特殊的线性表&#xff0c;其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶&#xff0c;另一端称为栈底。栈中的数据元素遵守后进先出LIFO&#xff08;Last In First Out&#xff09;的原则。 压栈&am…

【c++线程】condition_variable的简单使用

尝试用两个线程交替打印1-100的数字&#xff0c;要求一个线程打印奇数&#xff0c;另一个线程打印偶数&#xff0c;并且打印数字从小到大依次递增。 #include <iostream> using namespace std; #include <thread> #include <mutex> #include <condition_…

前端技术交流群

欢迎来到前端筱园用户交流&#xff01;这是一个专注于前端编程技术、学习资源和行业动态的讨论平台。在这里&#xff0c;你可以分享经验、提问、回答问题&#xff0c;与其他前端开发者一起学习和成长。 &#x1f31f;亲爱的朋友们&#x1f31f; 大家好&#xff01;感谢你们一直…

SSC369G 双4K高性价比AI IPC方案

一、方案描述 SSC369G 双4K高性价比AI IPC方案采用主芯片SSC369G&#xff0c;内核为CA55四核最高主频为1.5Ghz处理器。SOC内置集成一个64位的四核RISC处理器&#xff0c;先进的图像信号处理器&#xff08;ISP&#xff09;&#xff0c;高性能的H.265/H.264/MJPEG视频编解码器&a…

Open CASCADE学习|BRepFill_Edge3DLaw

BRepFill_Edge3DLaw类继承自BRepFill_LocationLaw&#xff0c;用于在3D空间中定义边缘的几何法则。 下面是对代码中关键部分的解释&#xff1a; 文件头部&#xff1a;包含了版权信息&#xff0c;指出这个文件是OCCT软件库的一部分&#xff0c;并且根据GNU Lesser General Publi…

驾驶证OCR识别接口如何对接

驾驶证OCR识别接口也叫驾驶证文字识别OCR接口&#xff0c;指的是传入驾驶证照片&#xff0c;精准识别静态驾驶证图像上的文字信息。那么驾驶证OCR文字识别接口如何对接呢&#xff1f; 首先我们找到一家有驾驶证OCR识别接口的服务商&#xff0c;数脉API,然后注册账户&#xff0…
最新文章