IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> 计算机网络学习笔记9-TCP并发服务器框架 -> 正文阅读

[网络协议]计算机网络学习笔记9-TCP并发服务器框架

TCP并发服务器的注意点:
TCP服务器、提取多个客户端、开启进程或线程处理每个客户端
1、多线程(常用)
在这里插入图片描述

#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<string.h>
#include<pthread.h>
//TCP并发ECHO服务器(并发回执服务器---客户端给服务器发啥 服务器就给客户端回啥)
void* deal_client_fun(void *arg)//arg = &new_fd
{
	//并发服务器的核心服务代码(各不相同)
	//通过arg获得已连接套接字
	int fd = *(int *)arg;
	while(1)//以下语句是服务器的核心代码
	{
		//获取客户端请求
		char buf[128]="";
		int len = recv(fd,buf,sizeof(buf), 0);
		if(len == 0)
			break;
		//回应客户端
		send(fd, buf, len, 0);
	}
	
	close(fd);
}
int main()
{
	//1、创建tcp监听套接字
	int sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if(sockfd < 0)
	{
		perror("socket");
	}
	int yes = 1;
     setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes,sizeof(yes));
     
	//2、给TCP监听套接字 bind固定的IP以及端口信息
	struct sockaddr_in my_addr;
	bzero(&my_addr,sizeof(my_addr));
	my_addr.sin_family = AF_INET;
	my_addr.sin_port = htons(8000);
	my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	int ret = bind(sockfd, (struct sockaddr *)&my_addr,sizeof(my_addr));
	if(ret == -1)
	{
		perror("bind");
	}
	//3、调用listen 将sockfd主动变被动  同时创建链接队列
	listen(sockfd, 10);
	
	//4、提取完成链接的客户端 accept
	//accept调用一次 只能提取一个客户端
	while(1)
	{
		struct sockaddr_in cli_addr;
		socklen_t cli_len = sizeof(cli_addr);
		int new_fd = accept(sockfd,(struct sockaddr *)&cli_addr , &cli_len);
		
		//遍历客户端的信息ip port
		unsigned short port=ntohs(cli_addr.sin_port);
		char ip[16]="";
		inet_ntop(AF_INET,&cli_addr.sin_addr.s_addr, ip, 16);
		printf("已有客户端:%s:%hu连接上了服务器\n", ip, port);
		
		//对每一个客户端 开启一个线程 单独的服务器客户端
		pthread_t tid;
		pthread_create(&tid,NULL, deal_client_fun, (void *)&new_fd);
		//线程分离
		pthread_detach(tid);
	}	
	
	//关闭监听套接字
	close(sockfd);
	
	return 0;
}

运行结果:
在这里插入图片描述
上述代码 如果客户端 正常退出 不会有啥影响,但是如果服务器 意外退出 绑定的端口信息来不及释放,就会造成 系统临时占用服务器上次bind的端口,如果在5~6分钟之内再次运行服务器 ,会导致新运行的服务器 bind失败
在这里插入图片描述

2、解决上述问题:端口复用 仅仅是端口的复用
服务器的进程网络资源 任然被占用 一般1分钟作用释放

int yes = 1;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes,sizeof(yes));

将上面的两句话添加到socket只有 bind函数之前

3、并发服务器 多进程实现
在这里插入图片描述

#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<string.h>
#include<pthread.h>
#include<unistd.h>
//TCP并发ECHO服务器(并发回执服务器---客户端给服务器发啥 服务器就给客户端回啥)
void deal_client_fun(int fd)//fd = new_fd
{
	while(1)//以下语句是服务器的核心代码
	{
		//获取客户端请求
		char buf[128]="";
		int len = recv(fd,buf,sizeof(buf), 0);
		if(len == 0)
			break;
		//回应客户端
		send(fd, buf, len, 0);
	}
	
	return;
}
int main()
{
	//1、创建tcp监听套接字
	int sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if(sockfd < 0)
	{
		perror("socket");
	}
	//端口复用
	int yes = 1;
	setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes,sizeof(yes));
	
	//2、给TCP监听套接字 bind固定的IP以及端口信息
	struct sockaddr_in my_addr;
	bzero(&my_addr,sizeof(my_addr));
	my_addr.sin_family = AF_INET;
	my_addr.sin_port = htons(8000);
	my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	int ret = bind(sockfd, (struct sockaddr *)&my_addr,sizeof(my_addr));
	if(ret == -1)
	{
		perror("bind");
	}
	//3、调用listen 将sockfd主动变被动  同时创建链接队列
	listen(sockfd, 10);
	
	//4、提取完成链接的客户端 accept
	//accept调用一次 只能提取一个客户端
	while(1)
	{
		struct sockaddr_in cli_addr;
		socklen_t cli_len = sizeof(cli_addr);
		int new_fd = accept(sockfd,(struct sockaddr *)&cli_addr , &cli_len);
		
		//遍历客户端的信息ip port
		unsigned short port=ntohs(cli_addr.sin_port);
		char ip[16]="";
		inet_ntop(AF_INET,&cli_addr.sin_addr.s_addr, ip, 16);
		printf("已有客户端:%s:%hu连接上了服务器\n", ip, port);
		
		pid_t pid;
		if(fork() == 0)//子进程  服务器客户端 不需要监听套接字
		{
			//关闭监听套接字
			close(sockfd);
			
			//服务于客户端
			deal_client_fun(new_fd);
			
			//关闭已连接套接字
			close(new_fd);
			_exit(-1);
		}
		else//父进程
		{
			//监听新的连接到来 不需要和客户端通信 必须关闭已连接套接字new_fd
			close(new_fd);
		}
	}	
	
	//关闭监听套接字
	close(sockfd);
	
	return 0;
}

运行结果:
在这里插入图片描述
总结:
TCP并发服务器 进程版:父子进程 资源独立 某个进程结束 不会影响已有的进程 服务器更加稳定 代价多进程 会消耗很多资源。
TCP并发服务器 线程版:线程共享进程资源 资源开销小 但是一旦主进程结束 所有线程都会结束 服务器先对进程 不是那么稳定

  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2021-09-08 11:06:18  更:2021-09-08 11:06:34 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年9日历 -2024/9/24 7:13:03-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码