Linux学习之网络编程3(高并发服务器)

写在前面

Linux网络编程我是看视频学的,Linux网络编程,看完这个视频大概网络编程的基础差不多就掌握了。这个系列是我看这个Linux网络编程视频写的笔记总结。


高并发服务器

问题:

根据上一个笔记,我们可以写出一个简单的服务端和客户端通信,但是我们发现一个问题——服务器只能连接一个客户端。然而在实际生活中,我们发现一个服务器连接的客户端远远不止一个,所以我们就要做一个高并发服务器。

解决方法:

回看之前的代码,之所以只能一对一通信,是因为服务器只有一次执行accept的机会,一旦建立连接成功,就会去进行通信处理业务,而其他想要建立连接的服务器就没办法建立连接。因此我们想到在Linux系统编程中学的进程和线程,我们可以让父进程(主线程)去监听,一定有客户端请求建立连接,我们就创建子进程(其他线程)去和客户端建立连接进行通信,父进程(主线程)继续监听。


多进程并发服务器

思路(步骤):

  1. 前期准备工作:
    • 先用socket()生成一个套接字lfd用来监听
    • bind()对第一步生成的套接字绑定地址结构(绑的是服务器的地址结构)
    • listen()函数设置lfd的监听上限,最大是128.
  2. 进入循环,accept与客户端建立连接,得到用于通信的套接字的文件描述符cfd
  3. fork()创建子进程
  4. 对于父进程,由于父进程只是监听,不需要与客户端进行通信,所以我们就关闭cfd,注册信号捕捉函数,用来回收子进程,然后一直循环监听。
  5. 对于子进程,由于子进程只是进行通信,不需要监听,所以我们就关闭lfd,然后就与客户端进行通信,处理业务。

源代码:

#include
#include
#include
#include
#include
#include
#include	
#include

#define PORT 6666

void sys_err(char* str)
{
	perror(str);
	exit(-1);
}

void wait_child(int signum)		//信号捕捉,回收子进程
{
	while((waitpid(0,NULL,WNOHANG))>0);
//	if(waitpid(0,NULL,0)!=-1)
//		printf("disconnect a client successfully\n");
	return;
}

int main()
{
	struct sockaddr_in addr_s,addr_c;
	socklen_t addr_c_len=sizeof addr_c;
	int lfd,cfd,res,n;
	pid_t pid;
	struct sigaction act;
	char buf[BUFSIZ],client_IP[1024];
	
	lfd=socket(AF_INET,SOCK_STREAM,0);
	if(lfd<0)
		sys_err("socket error");
		
	addr_s.sin_family=AF_INET;
	addr_s.sin_port=htons(PORT);
	addr_s.sin_addr.s_addr=htonl(INADDR_ANY);
	res=bind(lfd,(struct sockaddr*)&addr_s,sizeof addr_s);
	if(res==-1)
		sys_err("bind error");
		
	res=listen(lfd,128);
	if(res==-1)
		sys_err("listen error");
		
	while(1)
	{
		cfd=accept(lfd,(struct socket*)&addr_c,&addr_c_len);
		
		pid=fork();			//创建子进程
		
		if(pid==0)			//子进程
		{
			close(lfd);		//打印客户端的IP和端口号,可以省略
			printf("connect successfully,client IP:%s,port:%d\n",inet_ntop(AF_INET,&addr_c.sin_addr.s_addr,&client_IP,sizeof client_IP),ntohs(addr_c.sin_port));
			break;
		}
		
		else if(pid>0)
		{
			close(cfd);
			act.sa_handler=wait_child;
			sigemptyset(&act.sa_mask);
			act.sa_flags=0;
			sigaction(SIGCHLD,&act,NULL);
			continue;
		}
	}
	
	if(pid==0)		//父进程
	{
		while(1)
		{
			n=read(cfd,buf,sizeof buf);
			for(int i=0;i<n;i++)
				buf[i]=toupper(buf[i]);
			write(cfd,buf,n);
			write(STDOUT_FILENO,buf,n);
		}
	}
	
	close(cfd);
	close(lfd);
	
	return 0;
}

效果

Linux学习之网络编程3(高并发服务器)_第1张图片


多线程并发服务器

思路(步骤):

  1. 前期准备工作:
    • 先用socket()生成一个套接字lfd用来监听
    • bind()对第一步生成的套接字绑定地址结构(绑的是服务器的地址结构)
    • listen()函数设置lfd的监听上限,最大是128.
  2. 进入循环,accept与客户端建立连接,得到用于通信的套接字的文件描述符cfd
  3. 创建子线程
  4. 对于子线程,由于子线程只是进行通信,不需要监听,所以我们就关闭lfd,然后就与客户端进行通信,处理业务。
  5. 对于父进程,由于父线程只是监听,不需要与客户端进行通信,所以我们就关闭cfd,然后设置线程pthread_detach()分离或者使用pthread_join()回收子线程。

源代码

#include
#include
#include
#include
#include
#include
#include
#include

#define PORT 6666

void sys_err(char* str)
{
	perror(str);
	exit(-1);
}

void* fun(void* arg)
{
	int cfd=(int) arg,n;
	char buf[BUFSIZ];
	while(1)
	{
		n=read(cfd,buf,sizeof buf);
		if(n==0)
		{
			printf("one client closed......\n");
			break;
		}
		for(int i=0;i<n;i++)
			buf[i]=toupper(buf[i]);
		write(cfd,buf,n);
		write(STDOUT_FILENO,buf,n);
	}
	close(cfd);
	return NULL;
}
	
	

int main()
{
	struct sockaddr_in addr_s,addr_c;
	socklen_t addr_c_len=sizeof addr_c;
	char c_IP[1024];
	int lfd,cfd,res;
	pthread_t tid;

	addr_s.sin_family=AF_INET;
	addr_s.sin_port=htons(PORT);
	addr_s.sin_addr.s_addr=htonl(INADDR_ANY);

	lfd=socket(AF_INET,SOCK_STREAM,0);
	if(lfd<0)
		sys_err("socket errro");

	res=bind(lfd,(struct sockaddr*)&addr_s,sizeof addr_s);
	if(res<0)
		sys_err("bind error");

	res=listen(lfd,128);	
	if(res<0)
		sys_err("listen error");
	
	printf("accepting connect........\n");
	while(1)
	{
		cfd=accept(lfd,(struct sockaddr*)& addr_c,&addr_c_len);
		if(cfd==-1)
			sys_err("accept error");
		printf("connect successfully,client ip:%s,port:%d\n",inet_ntop(AF_INET,&addr_c.sin_addr.s_addr,c_IP,sizeof c_IP),ntohs(addr_c.sin_port));

		res=pthread_create(&tid,NULL,fun,(void*)cfd);
		if(res!=0)
			fprintf(stderr,"pthread create error:%s",strerror(res));

		pthread_detach(tid);	
		if(res!=0)
			fprintf(stderr,"pthread create error:%s",strerror(res));
	}
	close(lfd);
	return 0;
}

效果

Linux学习之网络编程3(高并发服务器)_第2张图片


写在最后

个人亲身经验:我们学习的一系列Linux命令,一定要自己亲手去敲。不要只是看别人敲代码,不要只是停留在眼睛看,脑袋以为自己懂了,等你实际上手去敲会发现许许多多的这样那样的问题。毕竟“实践出真知”。


如果你觉得我写的题解还不错的,请各位王子公主移步到我的其他题解看看

  1. 数据结构与算法部分(还在更新中):
  • C++ STL总结 - 基于算法竞赛(强力推荐
  • 动态规划——01背包问题
  • 动态规划——完全背包问题
  • 动态规划——多重背包问题
  • 动态规划——分组背包问题
  • 动态规划——最长上升子序列(LIS)
  • 二叉树的中序遍历(三种方法)
  • 最长回文子串
  • 最短路算法——Dijkstra(C++实现)
  • 最短路算法———Bellman_Ford算法(C++实现)
  • 最短路算法———SPFA算法(C++实现)
  • 最小生成树算法———prim算法(C++实现)
  • 最小生成树算法———Kruskal算法(C++实现)
  • 染色法判断二分图(C++实现)
  1. Linux部分(还在更新中):
  • Linux学习之初识Linux
  • Linux学习之命令行基础操作
  • Linux学习之基础命令(适合小白)
  • Linux学习之权限管理和用户管理
  • Linux学习之制作静态库和动态库
  • Linux学习之makefile
  • Linux学习之系统编程1(关于读写系统函数)
  • Linux学习之系统编程2(关于进程及其相关的函数)
  • Linux学习之系统编程3(进程及wait函数)
  • Linux学习之系统编程4(进程间通信)
  • Linux学习之系统编程5(信号)
  • Linux学习之系统编程6(线程)
  • Linux学习之系统编程7(线程同步/互斥锁/信号量/条件变量)
  • Linux学习之网络编程(纯理论)
  • Linux学习之网络编程2(socket,简单C/S模型)

✨总结

“种一颗树最好的是十年前,其次就是现在”
所以,
“让我们一起努力吧,去奔赴更高更远的山海”
在这里插入图片描述
如果有错误❌,欢迎指正哟

如果觉得收获满满,可以动动小手,点点赞,支持一下哟

你可能感兴趣的:(#,Linux网络编程,Linux,服务器,linux,学习,高并发服务器,多进程高并发服务器,多线程高并发服务器)