写在前面

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模型)

✨总结

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

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

好文推荐

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: