写在前面
Linux网络编程我是看视频学的,Linux网络编程,看完这个视频大概网络编程的基础差不多就掌握了。这个系列是我看这个Linux网络编程视频写的笔记总结。
高并发服务器
问题:
根据上一个笔记,我们可以写出一个简单的服务端和客户端通信,但是我们发现一个问题——服务器只能连接一个客户端。然而在实际生活中,我们发现一个服务器连接的客户端远远不止一个,所以我们就要做一个高并发服务器。
解决方法:
回看之前的代码,之所以只能一对一通信,是因为服务器只有一次执行accept的机会,一旦建立连接成功,就会去进行通信处理业务,而其他想要建立连接的服务器就没办法建立连接。因此我们想到在Linux系统编程中学的进程和线程,我们可以让父进程(主线程)去监听,一定有客户端请求建立连接,我们就创建子进程(其他线程)去和客户端建立连接进行通信,父进程(主线程)继续监听。
多进程并发服务器
思路(步骤):
前期准备工作:
先用socket()生成一个套接字lfd用来监听用bind()对第一步生成的套接字绑定地址结构(绑的是服务器的地址结构)用listen()函数设置lfd的监听上限,最大是128. 进入循环,accept与客户端建立连接,得到用于通信的套接字的文件描述符cfdfork()创建子进程对于父进程,由于父进程只是监听,不需要与客户端进行通信,所以我们就关闭cfd,注册信号捕捉函数,用来回收子进程,然后一直循环监听。对于子进程,由于子进程只是进行通信,不需要监听,所以我们就关闭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 buf[i]=toupper(buf[i]); write(cfd,buf,n); write(STDOUT_FILENO,buf,n); } } close(cfd); close(lfd); return 0; } 效果 多线程并发服务器 思路(步骤): 前期准备工作: 先用socket()生成一个套接字lfd用来监听用bind()对第一步生成的套接字绑定地址结构(绑的是服务器的地址结构)用listen()函数设置lfd的监听上限,最大是128. 进入循环,accept与客户端建立连接,得到用于通信的套接字的文件描述符cfd创建子线程对于子线程,由于子线程只是进行通信,不需要监听,所以我们就关闭lfd,然后就与客户端进行通信,处理业务。对于父进程,由于父线程只是监听,不需要与客户端进行通信,所以我们就关闭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 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命令,一定要自己亲手去敲。不要只是看别人敲代码,不要只是停留在眼睛看,脑袋以为自己懂了,等你实际上手去敲会发现许许多多的这样那样的问题。毕竟“实践出真知”。 如果你觉得我写的题解还不错的,请各位王子公主移步到我的其他题解看看 数据结构与算法部分(还在更新中): C++ STL总结 - 基于算法竞赛(强力推荐)动态规划——01背包问题动态规划——完全背包问题动态规划——多重背包问题动态规划——分组背包问题动态规划——最长上升子序列(LIS)二叉树的中序遍历(三种方法)最长回文子串最短路算法——Dijkstra(C++实现)最短路算法———Bellman_Ford算法(C++实现)最短路算法———SPFA算法(C++实现)最小生成树算法———prim算法(C++实现)最小生成树算法———Kruskal算法(C++实现)染色法判断二分图(C++实现) Linux部分(还在更新中): Linux学习之初识LinuxLinux学习之命令行基础操作Linux学习之基础命令(适合小白)Linux学习之权限管理和用户管理Linux学习之制作静态库和动态库Linux学习之makefileLinux学习之系统编程1(关于读写系统函数)Linux学习之系统编程2(关于进程及其相关的函数)Linux学习之系统编程3(进程及wait函数)Linux学习之系统编程4(进程间通信)Linux学习之系统编程5(信号)Linux学习之系统编程6(线程)Linux学习之系统编程7(线程同步/互斥锁/信号量/条件变量)Linux学习之网络编程(纯理论)Linux学习之网络编程2(socket,简单C/S模型) ✨总结 “种一颗树最好的是十年前,其次就是现在” 所以, “让我们一起努力吧,去奔赴更高更远的山海” 如果有错误❌,欢迎指正哟 如果觉得收获满满,可以动动小手,点点赞,支持一下哟 好文推荐
发表评论