Linux网络编程我是看视频学的,Linux网络编程,看完这个视频大概网络编程的基础差不多就掌握了。这个系列是我看这个Linux网络编程视频写的笔记总结。
根据上一个笔记,我们可以写出一个简单的服务端和客户端通信,但是我们发现一个问题——服务器只能连接一个客户端。然而在实际生活中,我们发现一个服务器连接的客户端远远不止一个,所以我们就要做一个高并发服务器。
回看之前的代码,之所以只能一对一通信,是因为服务器只有一次执行accept
的机会,一旦建立连接成功,就会去进行通信处理业务,而其他想要建立连接的服务器就没办法建立连接。因此我们想到在Linux系统编程中学的进程和线程,我们可以让父进程(主线程)去监听,一定有客户端请求建立连接,我们就创建子进程(其他线程)去和客户端建立连接进行通信,父进程(主线程)继续监听。
socket()
生成一个套接字lfd
用来监听bind()
对第一步生成的套接字绑定地址结构(绑的是服务器的地址结构)listen()
函数设置lfd
的监听上限,最大是128.accept
与客户端建立连接,得到用于通信的套接字的文件描述符cfd
fork()
创建子进程cfd
,注册信号捕捉函数,用来回收子进程,然后一直循环监听。lfd
,然后就与客户端进行通信,处理业务。#include<stdio.h>
#include<string.h>
#include<ctype.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<signal.h>
#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;
}
socket()
生成一个套接字lfd
用来监听bind()
对第一步生成的套接字绑定地址结构(绑的是服务器的地址结构)listen()
函数设置lfd
的监听上限,最大是128.accept
与客户端建立连接,得到用于通信的套接字的文件描述符cfd
lfd
,然后就与客户端进行通信,处理业务。cfd
,然后设置线程pthread_detach()
分离或者使用pthread_join()
回收子线程。#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<ctype.h>
#include<unistd.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<pthread.h>
#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命令,一定要自己亲手去敲。不要只是看别人敲代码,不要只是停留在眼睛看,脑袋以为自己懂了,等你实际上手去敲会发现许许多多的这样那样的问题。毕竟“实践出真知”。
如果你觉得我写的题解还不错的,请各位王子公主移步到我的其他题解看看
- 数据结构与算法部分(还在更新中):
- C++ STL总结 - 基于算法竞赛(强力推荐)
- 动态规划——01背包问题
- 动态规划——完全背包问题
- 动态规划——多重背包问题
- 动态规划——分组背包问题
- 动态规划——最长上升子序列(LIS)
- 二叉树的中序遍历(三种方法)
- 最长回文子串
- 最短路算法——Dijkstra(C++实现)
- 最短路算法———Bellman_Ford算法(C++实现)
- 最短路算法———SPFA算法(C++实现)
- 最小生成树算法———prim算法(C++实现)
- 最小生成树算法———Kruskal算法(C++实现)
- 染色法判断二分图(C++实现)
- 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模型)
“种一颗树最好的是十年前,其次就是现在”
所以,
“让我们一起努力吧,去奔赴更高更远的山海”
如果有错误?,欢迎指正哟😋
🎉如果觉得收获满满,可以动动小手,点点赞👍,支持一下哟🎉