网络编程中并发服务器的设计模式
Sailor_forever [email protected] 转载请注明
http://blog.csdn.net/sailor_8318/archive/2008/12/30/3658912.aspx
并发服务器有三种设计模式:
多进程:每个进程服务一个客户端。优势是有各自独立的地址空间,可靠性高,但进程调度开销大,无法资源共享,进程间通信机制复杂。
多线程:每个线程服务一个客户端。优势是开销小,通信机制简单,可共享内存。但共享地址空间,可靠性低,一个服务器出现问题时可能导致系统崩溃,同时全局共享可能带来竞争,共享资源需要互斥,对编程要求高。
单进程:占有的进程及线程资源少,通信机制简单。但监听服务器及各个子服务器揉和在一起,程序结构复杂不清晰,编程麻烦。
1 |
// File: mult-pr-tcp-server.c /* 多进程并发服务器。该程序等候客户连接,一旦连接则显示客户的地址, 接着接收该客户的名字并显示。然后接收来自该客户的信息(字符串)。 每当收到一个字符串,则显示该字符串,并将字符串反转,再将反转的字 符发回客户。之后,继续等待接收该客户的信息直至该客户关闭连接。服 务器具有同时处理多客户的能力。 */ #include <stdio.h> /* These are the usual header files */ #include <strings.h> /* for bzero() */ #include <unistd.h> /* for close() */ #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h>
#define PORT 1234 /* Port that will be opened */ #define BACKLOG 2 /* Number of allowed connections */ #define MAXDATASIZE 1000 void process_cli(int connectfd, struct sockaddr_in client);
main() { int listenfd, connectfd; /* socket descriptors */ pid_t pid; struct sockaddr_in server; /* server's address information */ struct sockaddr_in client; /* client's address information */ int sin_size;
/* Create TCP socket */ if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { /* handle exception */ perror("Creating socket failed."); exit(1); }
int opt = SO_REUSEADDR; setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
bzero(&server,sizeof(server)); server.sin_family=AF_INET; server.sin_port=htons(PORT); server.sin_addr.s_addr = htonl (INADDR_ANY); if (bind(listenfd, (struct sockaddr *)&server, sizeof(struct sockaddr)) == -1) { /* handle exception */ perror("Bind error."); exit(1); }
if(listen(listenfd,BACKLOG) == -1){ /* calls listen() */ perror("listen() error/n"); exit(1); }
sin_size=sizeof(struct sockaddr_in);
while(1) { /*accept connection.what causes the acceptance? */ if ((connectfd = accept(listenfd,(struct sockaddr *)&client,&sin_size))==-1) { perror("accept() error/n"); exit(1); } /* Create child process to service client */ if ((pid=fork())>0) { /* parent process */ close(connectfd); continue; } else if (pid==0) { /*child process*/ close(listenfd); process_cli(connectfd, client); exit(0); } else { printf("fork error/n"); exit(0); } } close(listenfd); /* close listenfd */ }
void process_cli(int connectfd, struct sockaddr_in client) { int num; char recvbuf[MAXDATASIZE], sendbuf[MAXDATASIZE], cli_name[MAXDATASIZE];
printf("You got a connection from %s. ",inet_ntoa(client.sin_addr) ); /* prints client's IP */ /* Get client's name from client */ num = recv(connectfd, cli_name, MAXDATASIZE,0); if (num == 0) { close(connectfd); printf("Client disconnected./n"); return; } cli_name[num - 1] = '/0'; printf("Client's name is %s./n",cli_name);
while (num = recv(connectfd, recvbuf, MAXDATASIZE,0)) { int i = 0; recvbuf[num] = '/0'; printf("Received client( %s ) message: %s",cli_name, recvbuf); for (i = 0; i < num - 1; i++) { sendbuf[i] = recvbuf[num - i -2]; } sendbuf[num - 1] = '/0';
send(connectfd,sendbuf,strlen(sendbuf),0); /* send to the client welcome message */ } close(connectfd); /* close connectfd */ }
|
1 |
// File: mult-thread-tcp-server.c /* 多线程并发服务器。该程序实现多线程并发服务器 */
#include <stdio.h> /* These are the usual header files */ //#include <strings.h> /* for bzero() only <strings.h> can not be compiled for memcpy in c++*/ #include <string.h> /* for bzero() */ #include <unistd.h> /* for close() */ #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <pthread.h> #include <stdlib.h> /* for exit in c++(.C/.cc) ; no need for c ??*/
#define PORT 1234 /* Port that will be opened */ #define BACKLOG 5 /* Number of allowed connections */ #define MAXDATASIZE 1000
//void process_cli(int connectfd, sockaddr_in client); // c only supports struct sockaddr_in, but c++ support sockaddr_in void process_cli(int connectfd, struct sockaddr_in client); /* function to be executed by the new thread */ void* start_routine(void* arg); typedef struct _ARG { int connfd; struct sockaddr_in client; }ARG; // it's better to use typedef struct main() { int listenfd, connectfd; /* socket descriptors */ pthread_t thread; //struct ARG *arg; // when no typedef,there should be struct for c code; no need for c++ ARG *arg; struct sockaddr_in server; /* server's address information */ struct sockaddr_in client; /* client's address information */ int sin_size;
/* Create TCP socket */ if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { /* handle exception */ perror("Creating socket failed."); exit(1); }
int opt = SO_REUSEADDR; setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
bzero(&server,sizeof(server)); server.sin_family=AF_INET; server.sin_port=htons(PORT); server.sin_addr.s_addr = htonl (INADDR_ANY); if (bind(listenfd, (struct sockaddr *)&server, sizeof(struct sockaddr)) == -1) { /* handle exception */ perror("Bind error."); exit(1); }
if(listen(listenfd,BACKLOG) == -1){ /* calls listen() */ perror("listen() error/n"); exit(1); }
sin_size=sizeof(struct sockaddr_in); while(1) { /* Accept connection */ // if ((connectfd = accept(listenfd,(struct sockaddr *)&client,&sin_size))==-1) {// no problem for c if ((connectfd = accept(listenfd,(struct sockaddr *)&client,(socklen_t *)&sin_size))==-1) { perror("accept() error/n"); exit(1); } /* Create thread*/
arg = new ARG; arg->connfd = connectfd; //memcpy((void *)&arg->client, &client, sizeof(client)); // both ok! memcpy(&arg->client, &client, sizeof(client));
if (pthread_create(&thread, NULL, start_routine, (void*)arg)) { /* handle exception */ perror("Pthread_create() error"); exit(1); } } close(listenfd); /* close listenfd */ }
void process_cli(int connectfd, sockaddr_in client) { int num; char recvbuf[MAXDATASIZE], sendbuf[MAXDATASIZE], cli_name[MAXDATASIZE];
printf("You got a connection from %s. ",inet_ntoa(client.sin_addr) ); /* Get client's name from client */ num = recv(connectfd, cli_name, MAXDATASIZE,0); if (num == 0) { close(connectfd); printf("Client disconnected./n"); return; } cli_name[num - 1] = '/0'; printf("Client's name is %s./n",cli_name);
while (num = recv(connectfd, recvbuf, MAXDATASIZE,0)) { recvbuf[num] = '/0'; printf("Received client( %s ) message: %s",cli_name, recvbuf); for (int i = 0; i < num - 1; i++) { sendbuf[i] = recvbuf[num - i -2]; } sendbuf[num - 1] = '/0'; send(connectfd,sendbuf,strlen(sendbuf),0); } close(connectfd); /* close connectfd */ }
void* start_routine(void* arg) { ARG *info; info = (ARG *)arg;
/* handle client's requirement */ process_cli(info->connfd, info->client);
////delete arg will cause warning!the type for deleting should be the same as new allocated delete info; pthread_exit(NULL); } |
1 |
// File:singlethread-mult-tcp-server.C /*单线程并发服务器实例。该程序采用单线程并发服务器算法实现的。*/ #include <stdio.h> /* These are the usual header files */ #include <string.h> /* for bzero() */ #include <unistd.h> /* for close() */ #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/time.h> #include <stdlib.h>
#define PORT 1234 /* Port that will be opened */ #define BACKLOG 5 /* Number of allowed connections simutaniously*/ #define MAXDATASIZE 1000 typedef struct _CLIENT{ int fd; char* name; struct sockaddr_in addr; /* client's address information */ char* data; } CLIENT; void process_cli(CLIENT *client, char* recvbuf, int len); void savedata(char* recvbuf, int len, char* data);
main() { int i, maxi, maxfd,sockfd; int nready; ssize_t n; fd_set rset, allset; int listenfd, connectfd; /* socket descriptors */ struct sockaddr_in server; /* server's address information */ /* client's information */ CLIENT client[FD_SETSIZE]; char recvbuf[MAXDATASIZE]; int sin_size;
/* Create TCP socket */ if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { /* handle exception */ perror("Creating socket failed."); exit(1); }
int opt = SO_REUSEADDR; setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
bzero(&server,sizeof(server)); server.sin_family=AF_INET; server.sin_port=htons(PORT); server.sin_addr.s_addr = htonl (INADDR_ANY); if (bind(listenfd, (struct sockaddr *)&server, sizeof(struct sockaddr)) == -1) { /* handle exception */ perror("Bind error."); exit(1); }
if(listen(listenfd,BACKLOG) == -1){ /* calls listen() */ perror("listen() error/n"); exit(1); }
sin_size=sizeof(struct sockaddr_in); /*initialize for select */ maxfd = listenfd; maxi = -1; for (i = 0; i < FD_SETSIZE; i++) { client[i].fd = -1; } FD_ZERO(&allset); FD_SET(listenfd, &allset);
while(1) { struct sockaddr_in addr; rset = allset; nready = select(maxfd+1, &rset, NULL, NULL, NULL); printf("select saw rset actions and the readfset num is %d. /n",nready );
if (FD_ISSET(listenfd, &rset)) { /* new client connection */ /* Accept connection */ printf("accept a connection./n"); if ((connectfd = accept(listenfd,(struct sockaddr *)&addr,(socklen_t *)&sin_size))==-1) { perror("accept() error/n"); continue; }
/* Put new fd to client */ for (i = 0; i < FD_SETSIZE; i++) if (client[i].fd < 0) { client[i].fd = connectfd; /* save descriptor */ client[i].name = new char[MAXDATASIZE]; client[i].addr = addr; client[i].data = new char[MAXDATASIZE]; client[i].name[0] = '/0'; client[i].data[0] = '/0'; printf("You got a connection from %s. ",inet_ntoa(client[i].addr.sin_addr) ); break; }
printf("add new connect fd./n"); if (i == FD_SETSIZE) printf("too many clients/n"); FD_SET(connectfd, &allset); /* add new descriptor to set */ if (connectfd > maxfd) maxfd = connectfd; if (i > maxi) maxi = i; if (--nready <= 0) continue; /* no more readable descriptors */ }
for (i = 0; i <= maxi; i++) { /* check all clients for data */ if ( (sockfd = client[i].fd) < 0) continue; /* no more connected clients*/
if (FD_ISSET(sockfd, &rset)) { printf("recv occured for connect fd[%d]./n",i); if ( (n = recv(sockfd, recvbuf, MAXDATASIZE,0)) == 0) { /*connection closed by client */ close(sockfd); printf("Client( %s ) closed connection. User's data: %s/n",client[i].name,client[i].data); FD_CLR(sockfd, &allset); client[i].fd = -1; delete client[i].name; delete client[i].data; } else process_cli(&client[i], recvbuf, n); if (--nready <= 0) break; /* no more readable descriptors */ } } } close(listenfd); /* close listenfd */ }
void process_cli(CLIENT *client, char* recvbuf, int len) { char sendbuf[MAXDATASIZE];
recvbuf[len-1] = '/0'; if (strlen(client->name) == 0) { /* Got client's name from client */ memcpy(client->name,recvbuf, len); printf("Client's name is %s./n",client->name); return; }
/* save client's data */ printf("Received client( %s ) message: %s/n",client->name, recvbuf); /* save user's data */ savedata(recvbuf,len, client->data); /* reverse usr's data */ for (int i1 = 0; i1 < len - 1; i1++) { sendbuf[i1] = recvbuf[len - i1 -2]; } sendbuf[len - 1] = '/0';
send(client->fd,sendbuf,strlen(sendbuf),0); }
void savedata(char* recvbuf, int len, char* data) { int start = strlen(data); for (int i = 0; i < len; i++) { data[start + i] = recvbuf[i]; } } |