Webserver(4.4)多进程/多线程实现并发服务器
目录
- 多进程实现并发服务器
- 多线程实现并发服务器
- TCP状态转换
多进程实现并发服务器
要实现TCP服务器处理并发的任务,使用多线程或者多进程来解决
一个父进程,多个子进程
父进程负责等待并接受客户端的连接
子进程:完成通信,接收一个客户端连接,就创建一个子进程用于通信
#include<stdio.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<signal.h>
#include<wait.h>
#include<errno.h>
void recyleChild(int arg){
while(1){
int ret=waitpid(-1,NULL,WNOHANG);
if(ret==-1){
//所有子进程都回收了
break;
}else if(ret==0){
//还有子进程活着
break;
}else if(ret>0){
//被回收了
printf("子进程 %d 被回收了\n",ret);
}
}
}
int main(){
struct sigaction act;
act.sa_flags=0;
sigemptyset(&act.sa_mask);
act.sa_handler=recyleChild;
//注册信号捕捉
sigaction(SIGCHLD,&act,NULL);
//创建socket
int lfd=socket(PF_INET,SOCK_STREAM,0);
if(lfd==-1){
perror("socket");
exit(-1);
}
struct sockaddr_in saddr;
saddr.sin_family=AF_INET;
saddr.sin_port=htons(9999);
saddr.sin_addr.s_addr=INADDR_ANY;
//绑定
int ret=bind(lfd,(struct sockaddr *)&saddr,sizeof(saddr));
if(ret==-1){
perror("bind");
exit(0);
}
//监听
ret=listen(lfd,128);
if(ret==-1){
perror("listen");
exit(-1);
}
//不断循环,等待客户端连接
while(1){
struct sockaddr_in cliaddr;
int len=sizeof(cliaddr);
//接受连接
int cfd=accept(lfd,(struct sockaddr*)&cliaddr,&len);
if(cfd==-1){
if(errno==EINTR){
continue;
}
perror("accept");
exit(-1);
}
//每一个连接进来,创建一个子进程跟客户端通信
pid_t pid=fork();
if(pid==0){
//子进程
//获取客户端的信息
char cliIp[16];
inet_ntop(AF_INET,&cliaddr.sin_addr.s_addr,cliIp,sizeof(cliIp));
unsigned short cliPort=ntohs(cliaddr.sin_port);
printf("client ip is:%s,port is %d\n",cliIp,cliPort);
//接收客户端发来的数据
char recvBuf[1024]={0};
while(1){
int len=read(cfd,&recvBuf,sizeof(recvBuf));
if(len==-1){
perror("read");
exit(-1);
}else if(len>0){
printf("recv client data:%s\n",recvBuf);
}else {
printf("client close...");
break;
}
write(cfd,recvBuf,strlen(recvBuf));
}
close(cfd);
exit(0);//退出当前子进程
}
}
close(lfd);
return 0;
}
#include<stdio.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
int main(){
//1.创建套接字
int fd=socket(AF_INET,SOCK_STREAM,0);
if(fd==-1){
perror("socket");
exit(-1);
}
//2.连接服务器端
struct sockaddr_in serveraddr;
serveraddr.sin_family=AF_INET;
inet_pton(AF_INET,"192.168.227.129",&serveraddr.sin_addr.s_addr);
serveraddr.sin_port=htons(9999);
int ret=connect(fd,(struct sockaddr *)&serveraddr,sizeof(serveraddr));
if(ret==-1){
perror("connect");
exit(-1);
}
char recvBuf[1024]={0};
int i=0;
//3.通信
while(1){
sprintf(recvBuf,"data:%d\n",i++);
//给服务器发送数据
write(fd,recvBuf,strlen(recvBuf));
sleep(1);
int len=read(fd,recvBuf,sizeof(recvBuf));
if(len==-1){
perror("read");
exit(-1);
}else if(len>0){
printf("recv server data:%s\n",recvBuf);
}else if(len==0){
//表示客户端断开连接
printf("server closed...");
break;
}
}
//关闭连接
close(fd);
return 0;
}
同时开启两个客户端,会交叉接收信息
这样就支持了并发,多个客户端
多线程实现并发服务器
#include<stdio.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<pthread.h>
#include<errno.h>
struct sockInfo{
int fd;//通信的文件描述符
struct sockaddr_in addr;
pthread_t tid;//线程号
};
struct sockInfo sockinfos[128];
void * working(void * arg){
//子线程和客户端通信
//获取客户端的信息
struct sockInfo * pinfo =(struct sockInfo *)arg;
char cliIp[16];
inet_ntop(AF_INET,&pinfo->addr.sin_addr.s_addr,cliIp,sizeof(cliIp));
unsigned short cliPort=ntohs(pinfo->addr.sin_port);
printf("client ip is:%s,port is %d\n",cliIp,cliPort);
//接收客户端发来的数据
char recvBuf[1024];
while(1){
int len=read(pinfo->fd,&recvBuf,sizeof(recvBuf));
if(len==-1){
perror("read");
exit(-1);
}else if(len>0){
printf("recv client data:%s\n",recvBuf);
}else if(len==0) {
printf("client closed...\n");
break;
}
write(pinfo->fd,recvBuf,strlen(recvBuf)+1);
}
close(pinfo->fd);
return NULL;
}
int main(){
//创建socket
int lfd=socket(PF_INET,SOCK_STREAM,0);
if(lfd==-1){
perror("socket");
exit(-1);
}
struct sockaddr_in saddr;
saddr.sin_family=AF_INET;
saddr.sin_port=htons(9999);
saddr.sin_addr.s_addr=INADDR_ANY;
//绑定
int ret=bind(lfd,(struct sockaddr *)&saddr,sizeof(saddr));
if(ret==-1){
perror("bind");
exit(0);
}
//监听
ret=listen(lfd,128);
if(ret==-1){
perror("listen");
exit(-1);
}
//初始化数据
int max=sizeof(sockinfos)/sizeof(sockinfos[0]);
for(int i=0;i<max;i++){
bzero(&sockinfos[i],sizeof(sockinfos[i]));
sockinfos[i].fd=-1;
sockinfos[i].tid=-1;
}
//循环等待客户端连接
while(1){
struct sockaddr_in cliaddr;
int len=sizeof(cliaddr);
//接受连接
int cfd=accept(lfd,(struct sockaddr*)&cliaddr,&len);
struct sockInfo * pinfo;
for(int i=0;i<max;i++){
//从这个数组中找到一个可以用的sockInfo元素
if(sockinfos[i].fd==-1){
pinfo=&sockinfos[i];
break;
}
if(i==max-1){
sleep(1);
i--;
}
}
pinfo->fd=cfd;
memcpy(&pinfo->addr,&cliaddr,len);
//创建子线程
pthread_create(&pinfo->tid,NULL,working,pinfo);
pthread_detach(pinfo->tid);
}
close(lfd);
return 0;
}
#include<stdio.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
int main(){
//1.创建套接字
int fd=socket(AF_INET,SOCK_STREAM,0);
if(fd==-1){
perror("socket");
exit(-1);
}
//2.连接服务器端
struct sockaddr_in serveraddr;
serveraddr.sin_family=AF_INET;
inet_pton(AF_INET,"192.168.227.129",&serveraddr.sin_addr.s_addr);
serveraddr.sin_port=htons(9999);
int ret=connect(fd,(struct sockaddr *)&serveraddr,sizeof(serveraddr));
if(ret==-1){
perror("connect");
exit(-1);
}
char recvBuf[1024]={0};
int i=0;
//3.通信
while(1){
sprintf(recvBuf,"data:%d\n",i++);
//给服务器发送数据
write(fd,recvBuf,strlen(recvBuf));
int len=read(fd,recvBuf,sizeof(recvBuf));
if(len==-1){
perror("read");
exit(-1);
}else if(len>0){
printf("recv server data:%s\n",recvBuf);
}else if(len==0){
//表示客户端断开连接
printf("server closed...\n");
break;
}
sleep(1);
}
//关闭连接
close(fd);
return 0;
}