Linux网络编程之高级并发服务器

在上一节,我们介绍了Linux简单的并发服务器,通过在服务器端建立多个子进程,来接收客户端的请求,实现并发处理,但这种方式明显有缺陷,服务器并不知道客户端请求的数量,所以事先建立的进程数不好确定。所以,这里介绍三种高级并发服务器模式。第一种是服务器端统一accept,接收客户端的到来,然后为每个客户端分配一个进程去处理. 第二种是统一accept接收请求,然后为每个客户端分配一个线程去处理。第三种建立多个线程去处理客户端请求,每个线程独自监听客户端的请求。显然,第一种方案解决了简单服务器的并发问题。第二种方案其实是对第一种方案的改进,因为线程切换的开销明显要小于进程切换的开销。第三种方案就是原来用进程去处理每个请求,现在换成用线程去处理,个人认为改进不是很大.

2. 高级并发服务器算法流程

(1)统一accept,多进程

socket(…);

bind(…);

listen(…);

while(1){

accept(…);

fork(…);//子进程

}

close(…);//关闭服务器套接字

子进程:

recv(…);

process(…);

send(…);

close(…);//关闭客户端

(2)统一accept,多线程

socket(…);

bind(…);

listen(…);

while(1){

accept(…);

pthread_create(….);

}

close(…);//关闭服务器

线程1:

recv(….);

process(….);

send(…);

close(…);//关闭客户端

(3)accept放入每个线程

socket(…);

bind(…);

listen(…);

pthread_create(…);

pthread_join(…);//等待线程结束

close(…);//关闭服务器

线程1:

Mutex_lock//互斥锁

accept(…);

Mutex_unlock(…);

recv(…);

process(…);

send(…);

close(…);//客户端

3. 相关例子

TCP服务器:

(1)统一accept多进程

服务器;

#include <stdio.h>#include <string.h>#include <stdlib.h>#include <sys/socket.h>#include <sys/types.h>#include <netinet/in.h>#include <time.h>/**高级并发服务器TCP统一accept当有客户端到来时,为每个客户端建立进程,然后每个进程处理客户端的请求,动态的建立进程**/#define PORT 8888#define BUFFERSIZE 1024#define BACKLOG 2static void handle(int sc){//处理客户端的请求 char buffer[BUFFERSIZE]; time_t now; int size; memset(buffer,0,BUFFERSIZE); size=recv(sc,buffer,BUFFERSIZE,0); if(size>0&&!strncmp(buffer,"TIME",4)){//时间服务器,当客户端请求时间就把时间发送给客户端 memset(buffer,0,BUFFERSIZE); now=time(NULL); sprintf(buffer,"%24s\r\n",ctime(&now)); send(sc,buffer,strlen(buffer),0);}close(sc);}int main(int argc,char*argv[]){ int ret; int s; int sc;//用于服务器与客户端进行数据传输的套接字 struct sockaddr_in server_addr; struct sockaddr_in client_addr; int len; len=sizeof(client_addr); //建立流式套接字 s=socket(AF_INET,SOCK_STREAM,0); if(s<0){ perror("socket error"); return -1; }//将地址结构绑定到套接字描述符上去memset(&server_addr,0,sizeof(server_addr));server_addr.sin_family=AF_INET;server_addr.sin_port=htons(PORT);server_addr.sin_addr.s_addr=htonl(INADDR_ANY);ret=bind(s,(struct sockaddr*)&server_addr,sizeof(server_addr));if(ret==-1){ perror("bind error"); return -1;}ret=listen(s,BACKLOG);if(ret<0){ perror("listen error"); return -1;}while(1){ sc=accept(s,(struct sockaddr*)&client_addr,&len); if(sc<0){ continue; } if(fork()==0){//子进程 handle(sc); close(s);//子进程关闭用于监听的套接字 }else{ close(sc);//父进程关闭客户端套接字}}}客户端:

#include <stdio.h>#include <string.h>#include <stdlib.h>#include <sys/socket.h>#include <sys/types.h>#include <time.h>#include <netinet/in.h>#define PORT 8888#define BUFFERSIZE 1024int main(int argc,char*argv[]){int s;int ret;int size;struct sockaddr_in server_addr;char buffer[BUFFERSIZE];s=socket(AF_INET,SOCK_STREAM,0);if(s<0){ perror("socket error"); return -1;}bzero(&server_addr,sizeof(server_addr));//将地址结构绑定到套接字server_addr.sin_family=AF_INET;server_addr.sin_port=htons(PORT);server_addr.sin_addr.s_addr=htonl(INADDR_ANY);//连接服务器ret=connect(s,(struct sockaddr*)&server_addr,sizeof(server_addr));if(ret==-1){ perror("connect error"); return -1;}memset(buffer,0,BUFFERSIZE);strcpy(buffer,"TIME");size=send(s,buffer,strlen(buffer),0);if(size<0){ perror("send error"); return -1;}memset(buffer,0,BUFFERSIZE);size=recv(s,buffer,BUFFERSIZE,0);if(size<0){ perror("recv error"); return;}printf("%s",buffer);close(s);return 0;}

(2)统一accept多线程

服务器:

#include <stdio.h>#include <string.h>#include <stdlib.h>#include <sys/socket.h>#include <sys/types.h>#include <netinet/in.h>#include <time.h>/**TCP并发服务器,采用多线程,每次客户端发送请求,主线程建立一个子线程,用于处理客户端的请求线程具有速度快,占用资源少,数据可以共享等优点 统一accept**/#define PORT 8888#define BUFFERSIZE 1024#define BACKLOG 2static void handle(void* sc1){int sc;time_t now;char buffer[BUFFERSIZE];int size; sc=*((int*)sc1);//转换成int指针,然后取值,sc1本身就是一个指针memset(buffer,0,BUFFERSIZE);size=recv(sc,buffer,BUFFERSIZE,0);if(size>0&&!strncmp(buffer,"TIME",4)){//请求服务器的时间 memset(buffer,0,BUFFERSIZE);//清0 now=time(NULL); sprintf(buffer,"%24s\r\n",ctime(&now)); send(sc,buffer,strlen(buffer),0);//向客户端发送数据}close(sc);//关闭客户端}int main(int argc,char*argv[]){ int ret; int s; int sc; int len; pthread_t thread1;//定义线程名 struct sockaddr_in server_addr,client_addr; len=sizeof(client_addr); //建立流式套接字 s=socket(AF_INET,SOCK_STREAM,0); if(s<0){ perror("socket error"); return -1; } //将服务器端的地址结构绑定到套接字描述符 server_addr.sin_family=AF_INET; server_addr.sin_addr.s_addr=htonl(INADDR_ANY); server_addr.sin_port=htons(PORT); ret=bind(s,(struct sockaddr*)&server_addr,sizeof(struct sockaddr_in)); if(ret<0){ perror("bind error"); return -1; }//监听 ret=listen(s,BACKLOG); if(ret<0){ perror("listen error"); return -1; }//接收客户端的请求for(;;){ sc=accept(s,(struct sockaddr*)&client_addr,&len); if(sc<0){ continue; } else { pthread_create(&thread1,NULL,handle,(void*)&sc);//建立线程,让线程去处理,最后一个字段是传递给线程处理函数handle的参数 }}close(s);}

客户端:

#include <stdio.h>#include <string.h>#include <stdlib.h>#include <sys/socket.h>#include <sys/types.h>#include <time.h>#include <netinet/in.h>#define PORT 8888#define BUFFERSIZE 1024int main(int argc,char*argv[]){int s;int ret;int size;struct sockaddr_in server_addr;char buffer[BUFFERSIZE];s=socket(AF_INET,SOCK_STREAM,0);if(s<0){ perror("socket error"); return -1;}bzero(&server_addr,sizeof(server_addr));//将地址结构绑定到套接字server_addr.sin_family=AF_INET;server_addr.sin_port=htons(PORT);server_addr.sin_addr.s_addr=htonl(INADDR_ANY);//连接服务器ret=connect(s,(struct sockaddr*)&server_addr,sizeof(server_addr));if(ret==-1){ perror("connect error"); return -1;}memset(buffer,0,BUFFERSIZE);strcpy(buffer,"TIME");size=send(s,buffer,strlen(buffer),0);if(size<0){ perror("send error"); return -1;}memset(buffer,0,BUFFERSIZE);size=recv(s,buffer,BUFFERSIZE,0);if(size<0){ perror("recv error"); return;}printf("%s",buffer);close(s);return 0;}

(3)单独线程accept

服务器:

#include <stdio.h>#include <string.h>#include <stdlib.h>#include <sys/socket.h>#include <sys/types.h>#include <netinet/in.h>#include <time.h>#include <pthread.h>/**多线程TCP并发服务器主线程创建多个线程,然后每个线程独立的accept和进行数据的发送与接收多线程,独立accept**/#define PORT 8888#define BUFFERSIZE 1024#define BACKLOG 2#define CLIENTNUM 3static void *handle(void* s1){int s;int len;int sc;pthread_mutex_t alock=PTHREAD_MUTEX_INITIALIZER;char buffer[BUFFERSIZE];int size;struct sockaddr_in client_addr;s=*((int*)s1);//得到服务器端的套接字描述符//等待客户端连接len=sizeof(client_addr);for(;;){//不停的循环等待客户端的连接 time_t now; //进入互斥区,每次一个线程处理客户端pthread_mutex_lock(&alock);sc=accept(s,(struct sockaddr*)&client_addr,&len);pthread_mutex_unlock(&alock);memset(buffer,0,BUFFERSIZE);size=recv(sc,buffer,BUFFERSIZE,0);if(size>0&&!strncmp(buffer,"TIME",4)){ memset(buffer,0,BUFFERSIZE); now=time(NULL); sprintf(buffer,"%24s\r\n",ctime(&now)); send(sc,buffer,strlen(buffer),0);}close(sc);//关闭客户端}}int main(int argc,char*argv[]){ int ret; int s; int len; int i; pthread_t thread[CLIENTNUM]; struct sockaddr_in server_addr; //建立流式套接字 s=socket(AF_INET,SOCK_STREAM,0); if(s<0){ perror("socket error"); return -1; }//将地址结构绑定到套接字上 server_addr.sin_family=AF_INET; server_addr.sin_addr.s_addr=htonl(INADDR_ANY); server_addr.sin_port=htons(PORT); ret=bind(s,(struct sockaddr*)&server_addr,sizeof(struct sockaddr_in)); if(ret==-1){ perror("bind error"); return -1; }//监听 ret=listen(s,BACKLOG); if(ret==-1){ perror("listen error"); return -1;}//建立3个线程,每个线程独立的accept for(i=0;i<CLIENTNUM;i++){ pthread_create(&thread[i],NULL,handle,(void*)&s);//线程的处理函数为handle,传递的参数为套接字描述符s }//while(1);//等待线程结束for(i=0;i<CLIENTNUM;i++){ pthread_join(thread[i],NULL);}//关闭套接字close(s);return 0;}客户端:

#include <stdio.h>#include <string.h>#include <stdlib.h>#include <sys/socket.h>#include <sys/types.h>#include <time.h>#include <netinet/in.h>#define PORT 8888#define BUFFERSIZE 1024int main(int argc,char*argv[]){int s;int ret;int size;struct sockaddr_in server_addr;char buffer[BUFFERSIZE];s=socket(AF_INET,SOCK_STREAM,0);if(s<0){ perror("socket error"); return -1;}bzero(&server_addr,sizeof(server_addr));//将地址结构绑定到套接字server_addr.sin_family=AF_INET;server_addr.sin_port=htons(PORT);server_addr.sin_addr.s_addr=htonl(INADDR_ANY);//连接服务器ret=connect(s,(struct sockaddr*)&server_addr,sizeof(server_addr));if(ret==-1){ perror("connect error"); return -1;}memset(buffer,0,BUFFERSIZE);strcpy(buffer,"TIME");size=send(s,buffer,strlen(buffer),0);if(size<0){ perror("send error"); return -1;}memset(buffer,0,BUFFERSIZE);size=recv(s,buffer,BUFFERSIZE,0);if(size<0){ perror("recv error"); return;}printf("%s",buffer);close(s);return 0;}

总结:

统一accept,多进程服务器是对简单并发服务器的改进,而由于进程的切换开销比较大,所以又有了统一accept,多线程的并发服务器。而单独线程的accept是完全用线程来处理请求。这些都是TCP服务器,由于UDP是突发的数据流,没有三次握手,所以服务器不能检测到客户端什么时候发送数据。以上三种高级并发服务器仍然存在着性能问题,下一节介绍的I/O复用的循环服务器是对这三种高级并发服务器的改进。

放下一处烦恼,收获一个惊喜;放下一种偏见,收获一种幸福;

Linux网络编程之高级并发服务器

相关文章:

你感兴趣的文章:

标签云: