网络编程day3

进程与线程实现TCP并发服务器

多进程服务器端

cpp 复制代码
#include<myhead.h>

#define  SER_PORT  8888        //服务器端口号
#define SER_IP  "192.168.109.125"   //服务器IP地址 

//定义一个信号处理函数,用于处理SIGCHLD信号
void handler(int signo)
{
	//判断是哪个信号到位
	if(signo == SIGCHLD)
	{
		//说明有子进程牺牲了
		//使用非阻塞的形式循环回收子进程资源
		while(waitpid(-1, NULL, WNOHANG) > 0);
	}
}


int main(int argc, const char *argv[])
{

	//将子进程向父进程发送的SIGCHLD信号,绑定到信号处理函数中
	if(signal(SIGCHLD, handler) == SIG_ERR)
	{
		perror("signal error");
		return -1;
	}



	//1、创建一个用于连接的套接字文件描述符
	int sfd  = socket(AF_INET, SOCK_STREAM, 0);
	if(-1 == sfd)
	{
		perror("socket  error");
		return -1;
	}

	printf("socket 成功 sfd = %d\n", sfd);          //3


	//设置套接字属性:允许端口号快速重用
	int reuse = 1;
	if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse))==-1)
	{
		perror("setsockopt error");
		return -1;
	}
	printf("端口号快速重用成功\n");

	//2、给套接字绑定ip地址和端口号
	//2.1 封装地址信息结构体变量
	struct sockaddr_in sin;        //地址信息结构体变量
	sin.sin_family = AF_INET;      //通信域地址族
	sin.sin_port = htons(SER_PORT);   //端口号网络字节序
	sin.sin_addr.s_addr = inet_addr(SER_IP);  //ip地址网络字节序
	
	//2.2  绑定操作
	if(bind(sfd, (struct sockaddr*)&sin, sizeof(sin)) ==-1)
	{
		perror("bind  error");
		return -1;
	}
	printf("bind success\n");


	//3、将套接字启动监听
	if(listen(sfd, 128) == -1)
	{
		perror("listen  error");
		return -1;
	}
	printf("listen success\n");

	//4、阻塞等等客户端的连接,如果有新客户端连接,
	//则创建一个用于通信的套接字
	struct sockaddr_in cin;            //用于接受客户端套接字信息
	socklen_t addrlen = sizeof(cin);   //用于接受客户端套接字的长度

	while(1)              //循环处理多个客户端
	{
		//接收新的客户端的连接请求
		int new_fd = accept(sfd, (struct sockaddr*)&cin, &addrlen);
		if(-1 == new_fd)
		{
			perror("accept error");
			return -1;
		}
		printf("[%s:%d]发来连接,new_fd = %d\n", inet_ntoa(cin.sin_addr),\
				ntohs(cin.sin_port) ,new_fd);

		//给当前新的套接字创建一个子进程用于跟客户端通信
		pid_t pid = fork();

		if(pid > 0)
		{
			//关闭new_fd
			close(new_fd);

		}else if(pid == 0)
		{
			//关闭sfd文件描述符
			close(sfd);

			//5、使用新套接字跟客户端进行通信
			while(1)
			{
				//从套接字中读取消息
				char rbuf[128] = "";          //存放接受消息的容器
				//int res = read(new_fd, rbuf, sizeof(rbuf)-1);
				int res = recv(new_fd, rbuf, sizeof(rbuf)-1, 0);
				if(res == 0)
				{
					printf("客户端已下线\n");
					close(new_fd);
					exit(EXIT_SUCCESS);           //退出子进程
				}
				if(res == -1)
				{
					perror("read error");
					close(new_fd);
					close(sfd);
					return -1;
				}

				//如果程序执行至此,表示数据成功读取下来,病放入rbuf中
				printf("[%s:%d] : %s\n", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), rbuf);

				//给当前消息加个笑脸还回去
				strcat(rbuf, "*_*");

				//向套接字中写入消息
				//write(new_fd, rbuf, strlen(rbuf));
				send(new_fd, rbuf, strlen(rbuf), 0);

				printf("发送成功\n");


			}

		}

		//回收子进程资源

	}

	//6、关闭监听套接字
	close(sfd);



	
	return 0;
}

多线程客户端

cpp 复制代码
#include<myhead.h>

#define  SER_PORT  8888        //服务器端口号
#define SER_IP  "192.168.109.125"   //服务器IP地址 

//定义一个结构体类型,用于主线程向分支线程传输多个数据
struct MsgInfo
{
	int new_fd;              //用于跟客户端通信的套接字文件描述符
	struct sockaddr_in cin;     //客户端套接字地址信息结构体
};



//定义处理客户端消息的线程体函数
void *deal_cli_msg(void *arg)
{
	//将主线程传入的数据解析出来
	int new_fd = ((struct MsgInfo*)arg)->new_fd;
	struct sockaddr_in cin =  ((struct MsgInfo*)arg)->cin;

	//5、使用新套接字跟客户端进行通信
	while(1)
	{
		//从套接字中读取消息
		char rbuf[128] = "";          //存放接受消息的容器
		//int res = read(new_fd, rbuf, sizeof(rbuf)-1);
		int res = recv(new_fd, rbuf, sizeof(rbuf)-1, 0);
		if(res == 0)
		{
			printf("客户端已下线\n");
			close(new_fd);
			break;
		}
		if(res == -1)
		{
			perror("read error");
			close(new_fd);
			return NULL;
		}

		//如果程序执行至此,表示数据成功读取下来,病放入rbuf中
		printf("[%s:%d] : %s\n", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), rbuf);

		//给当前消息加个笑脸还回去
		strcat(rbuf, "*_*");

		//向套接字中写入消息
		//write(new_fd, rbuf, strlen(rbuf));
		send(new_fd, rbuf, strlen(rbuf), 0);

		printf("发送成功\n");

	}

	//退出线程
	pthread_exit(NULL);


}




/*****************************主线程********************/
int main(int argc, const char *argv[])
{
	//1、创建一个用于连接的套接字文件描述符
	int sfd  = socket(AF_INET, SOCK_STREAM, 0);
	if(-1 == sfd)
	{
		perror("socket  error");
		return -1;
	}

	printf("socket 成功 sfd = %d\n", sfd);          //3


	//设置套接字属性:允许端口号快速重用
	int reuse = 1;
	if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse))==-1)
	{
		perror("setsockopt error");
		return -1;
	}
	printf("端口号快速重用成功\n");

	//2、给套接字绑定ip地址和端口号
	//2.1 封装地址信息结构体变量
	struct sockaddr_in sin;        //地址信息结构体变量
	sin.sin_family = AF_INET;      //通信域地址族
	sin.sin_port = htons(SER_PORT);   //端口号网络字节序
	sin.sin_addr.s_addr = inet_addr(SER_IP);  //ip地址网络字节序
	
	//2.2  绑定操作
	if(bind(sfd, (struct sockaddr*)&sin, sizeof(sin)) ==-1)
	{
		perror("bind  error");
		return -1;
	}
	printf("bind success\n");


	//3、将套接字启动监听
	if(listen(sfd, 128) == -1)
	{
		perror("listen  error");
		return -1;
	}
	printf("listen success\n");

	//4、阻塞等等客户端的连接,如果有新客户端连接,
	//则创建一个用于通信的套接字
	struct sockaddr_in cin;            //用于接受客户端套接字信息
	socklen_t addrlen = sizeof(cin);   //用于接受客户端套接字的长度

	while(1)
	{
		//阻塞等等客户端的连接
		//对于accept函数而言,当执行到该函数时,即使没有客户端的连接
		//也会预选一个当前最小的文件描述符给新套接字
		//在阻塞过程中,即使有更小的,也不会选择,会留作下一次预选使用
		int new_fd = accept(sfd, (struct sockaddr*)&cin, &addrlen);
		if(-1 == new_fd)
		{
			perror("accept error");
			return -1;
		}
		printf("[%s:%d]发来连接,new_fd = %d\n", inet_ntoa(cin.sin_addr),\
				ntohs(cin.sin_port) ,new_fd);

		//定义一个结构体变量,存储通信的套接字文件描述符和客户端地址信息结构体
		struct MsgInfo info = {new_fd, cin};
		
		//创建一个分之现场,用于跟客户端进行通信
		pthread_t tid = -1;
		if(pthread_create(&tid, NULL, deal_cli_msg, &info)!=0)
		{
			printf("线程创建失败\n");
			return -1;
		}

		//将线程设置成分离态
		pthread_detach(tid);


	}

	//6、关闭监听套接字
	close(sfd);



	
	return 0;
}
相关推荐
箓维19 分钟前
TCP Socket的进阶实现
网络·笔记·tcp/ip
小羊没烦恼!1 小时前
关于分布式锁原理的一些学习与思考-redis分布式锁,zookeeper分布式锁
java·服务器·网络·学习·负载均衡
小Ti客栈1 小时前
Netty 零拷贝原理与实战
运维·服务器·网络
国科安芯1 小时前
商业光学遥感载荷焦平面供电链的低噪声稳压器研究综述——以国科安芯ASP7A84AS型3A低压差线性稳压器为器件级参照
大数据·网络·嵌入式硬件·平面·卫星·商业航天·抗辐射
静以修身zzz1 小时前
计算云服务
运维·网络·云计算
星恒随风1 小时前
Linux网络编程:应用层协议究竟是什么?从网络计算器到TCP Socket抽象基类完整实现
linux·网络·笔记·tcp/ip
万联WANFLOW2 小时前
异地组网速度忽快忽慢到底怎么回事?直连与中继的判断方法和处理步骤
运维·网络·网络协议·p2p
jimmyleeee2 小时前
Agent安全之六:(ASI06)记忆与上下文投毒——当 AI 把“昨天的错误”当成“今天的真理”
网络·人工智能·安全
辰辰观影2 小时前
字幕导入教程:外挂字幕一键加载方法
网络·经验分享
无糖可乐没有灵魂2 小时前
Golang ❀ Netdiag 一个Go开源的网络排障信息收集工具
网络