前面介绍了关于连接linux服务端方式,可是服务端的资源是有限的,所以我们通常须要又一次思考,设计一套server模型来处理相应的client的请求。
第一种:并发server。通过主进程统一处理client的连接。当client连接过后。暂时fork()进程,由子进程处理client请求,将连接请求和业务进行了分离。
server.c
#include#include #include #include #include #include #include #define BUFFLEN 1024#define SERVER_PORT 8888#define BACKLOG 5static void handle_request(int s_c){ time_t now; /*时间*/ char buff[BUFFLEN]; /*收发数据缓冲区*/ int n = 0; memset(buff, 0, BUFFLEN); /*清零*/ n = recv(s_c, buff, BUFFLEN,0); /*接收发送方数据*/ if(n > 0 && !strncmp(buff, "TIME", 4)) /*推断是否合法接收数据*/ { memset(buff, 0, BUFFLEN); /*清零*/ now = time(NULL); /*当前时间*/ sprintf(buff, "%24s\r\n",ctime(&now)); /*将时间复制入缓冲区*/ send(s_c, buff, strlen(buff),0); /*发送数据*/ } /*关闭client*/ close(s_c); }static int handle_connect(int s_s){ int s_c; /*client套接字文件描写叙述符*/ struct sockaddr_in from; /*client地址*/ socklen_t len = sizeof(from); /*主处理过程*/ while(1) { /*接收client连接*/ s_c = accept(s_s, (struct sockaddr*)&from, &len); if(s_c > 0) /*client成功连接*/ { /*创建进程进行数据处理*/ if(fork() > 0){ /*父进程*/ close(s_c); /*关闭父进程的client连接套接字*/ }else{ handle_request(s_c); /*处理连接请求*/ return(0); } } } }int main(int argc, char *argv[]){ int s_s; /*server套接字文件描写叙述符*/ struct sockaddr_in local; /*本地地址*/ /*建立TCP套接字*/ s_s = socket(AF_INET, SOCK_STREAM, 0); /*初始化地址*/ memset(&local, 0, sizeof(local)); /*清零*/ local.sin_family = AF_INET; /*AF_INET协议族*/ local.sin_addr.s_addr = htonl(INADDR_ANY); /*随意本地地址*/ local.sin_port = htons(SERVER_PORT); /*serverport*/ /*将套接字文件描写叙述符绑定到本地地址和port*/ bind(s_s, (struct sockaddr*)&local, sizeof(local)); listen(s_s, BACKLOG); /*侦听*/ /*处理client连接*/ handle_connect(s_s); close(s_s); return 0;}
代码比較具体,easy理解。
以下介绍client代码,后面的client代码都是一样的。
client.c
#include#include #include #include #include #include #define BUFFLEN 1024#define SERVER_PORT 8888int main(int argc, char *argv[]){ int s; /*server套接字文件描写叙述符*/ struct sockaddr_in server; /*本地地址*/ char buff[BUFFLEN]; /*收发数据缓冲区*/ int n = 0; /*接收字符串长度*/ /*建立TCP套接字*/ s = socket(AF_INET, SOCK_STREAM, 0); /*初始化地址*/ memset(&server, 0, sizeof(server)); /*清零*/ server.sin_family = AF_INET; /*AF_INET协议族*/ server.sin_addr.s_addr = htonl(INADDR_ANY);/*随意本地地址*/ server.sin_port = htons(SERVER_PORT); /*serverport*/ /*连接server*/ connect(s, (struct sockaddr*)&server,sizeof(server)); memset(buff, 0, BUFFLEN); /*清零*/ strcpy(buff, "TIME"); /*复制发送字符串*/ /*发送数据*/ send(s, buff, strlen(buff), 0); memset(buff, 0, BUFFLEN); /*清零*/ /*接收数据*/ n = recv(s, buff, BUFFLEN, 0); /*打印消息*/ if(n >0){ printf("TIME:%s",buff); } close(s); return 0;}
另外一种模型:通过线程来处理,线程比进程占用资源少,效率高,数据共享。通过pthread_create()建立一个连接请求处理。线程处理函数为handle_request().
server.c#include#include #include #include #include #include #include #include #define BUFFLEN 1024#define SERVER_PORT 8888#define BACKLOG 5static void handle_request(void *argv){ int s_c = *((int*)argv); time_t now; /*时间*/ char buff[BUFFLEN]; /*收发数据缓冲区*/ int n = 0; memset(buff, 0, BUFFLEN); /*清零*/ n = recv(s_c, buff, BUFFLEN,0); /*接收发送方数据*/ if(n > 0 && !strncmp(buff, "TIME", 4)) /*推断是否合法接收数据*/ { memset(buff, 0, BUFFLEN); /*清零*/ now = time(NULL); /*当前时间*/ sprintf(buff, "%24s\r\n",ctime(&now)); /*将时间复制入缓冲区*/ send(s_c, buff, strlen(buff),0); /*发送数据*/ } /*关闭client*/ close(s_c); }static void handle_connect(int s_s){ int s_c; /*client套接字文件描写叙述符*/ struct sockaddr_in from; /*client地址*/ socklen_t len = sizeof(from); pthread_t thread_do; /*主处理过程*/ while(1) { /*接收client连接*/ s_c = accept(s_s, (struct sockaddr*)&from, &len); if(s_c > 0) /*client成功连接*/ { /*创建线程处理连接*/ pthread_create(&thread_do, NULL, (void*)handle_request, &s_c); } } }int main(int argc, char *argv[]){ int s_s; /*server套接字文件描写叙述符*/ struct sockaddr_in local; /*本地地址*/ /*建立TCP套接字*/ s_s = socket(AF_INET, SOCK_STREAM, 0); /*初始化地址和port*/ memset(&local, 0, sizeof(local)); /*清零*/ local.sin_family = AF_INET; /*AF_INET协议族*/ local.sin_addr.s_addr = htonl(INADDR_ANY); /*随意本地地址*/ local.sin_port = htons(SERVER_PORT); /*serverport*/ /*将套接字文件描写叙述符绑定到本地地址和port*/ bind(s_s, (struct sockaddr*)&local, sizeof(local)); listen(s_s, BACKLOG); /*侦听*/ /*处理client连接*/ handle_connect(s_s); close(s_s); return 0; }
第三种:服务端各线程独自accept()。使用相互排斥锁,使用pthread_create()建立多个线程组成的线程池,主线程等待程序结束。各个线程独自接收clientaccept。以及后面数据处理。
server.c#include#include #include #include #include #include #include #include #define BUFFLEN 1024#define SERVER_PORT 8888#define BACKLOG 5#define CLIENTNUM 2/*相互排斥量*/pthread_mutex_t ALOCK = PTHREAD_MUTEX_INITIALIZER;static void *handle_request(void *argv){ int s_s = *((int*)argv); int s_c; /*client套接字文件描写叙述符*/ struct sockaddr_in from; /*client地址*/ socklen_t len = sizeof(from); for(;;) { time_t now; /*时间*/ char buff[BUFFLEN]; /*收发数据缓冲区*/ int n = 0; pthread_mutex_lock(&ALOCK); /*进入相互排斥区*/ s_c = accept(s_s, (struct sockaddr*)&from, &len); /*接收client的请求*/ pthread_mutex_unlock(&ALOCK); /*离开相互排斥区*/ memset(buff, 0, BUFFLEN); /*清零*/ n = recv(s_c, buff, BUFFLEN,0); /*接收发送方数据*/ if(n > 0 && !strncmp(buff, "TIME", 4)) /*推断是否合法接收数据*/ { memset(buff, 0, BUFFLEN); /*清零*/ now = time(NULL); /*当前时间*/ sprintf(buff, "%24s\r\n",ctime(&now)); /*将时间复制入缓冲区*/ send(s_c, buff, strlen(buff),0); /*发送数据*/ } /*关闭client*/ close(s_c); } return NULL;}static void handle_connect(int s){ int s_s = s; pthread_t thread_do[CLIENTNUM]; /*线程ID*/ int i = 0; for(i=0;i
第四种:IO复用server。并发serverclient越多,对server造成的压力越大,所以还有第四种模型。IO复用函数用select来做。
server.c#include#include #include #include #include #include #include #include #include #include #define BUFFLEN 1024#define SERVER_PORT 8888#define BACKLOG 5#define CLIENTNUM 1024 /*最大支持client数量*//*可连接client的文件描写叙述符数组*/int connect_host[CLIENTNUM];int connect_number = 0;static void *handle_request(void *argv){ time_t now; /*时间*/ char buff[BUFFLEN]; /*收发数据缓冲区*/ int n = 0; int maxfd = -1; /*最大侦听文件描写叙述符*/ fd_set scanfd; /*侦听描写叙述符集合*/ struct timeval timeout; /*超时*/ timeout.tv_sec = 1; /* 堵塞1s后超时返回 */ timeout.tv_usec = 0; int i = 0; int err = -1; for(;;) { /*最大文件描写叙述符值初始化为-1*/ maxfd = -1; FD_ZERO(&scanfd); /*清零文件描写叙述符集合*/ for(i=0;i 0 && !strncmp(buff, "TIME", 4)) /*推断是否合法接收数据*/ { memset(buff, 0, BUFFLEN); /*清零*/ now = time(NULL); /*当前时间*/ sprintf(buff, "%24s\r\n",ctime(&now)); /*将时间复制入缓冲区*/ send(connect_host[i], buff, strlen(buff),0); /*发送数据*/ } /*更新文件描写叙述符在数组中的值*/ connect_host[i] = -1; connect_number --; /*client计数器减1*/ /*关闭client*/ close(connect_host[i]); } } break; } } return NULL;}static void *handle_connect(void *argv){ int s_s = *((int*)argv) ; /*获得server侦听套接字文件描写叙述符*/ struct sockaddr_in from; socklen_t len = sizeof(from); /*接收client连接*/ for(;;) { int i = 0; int s_c = accept(s_s, (struct sockaddr*)&from, &len); /*接收client的请求*/ printf("a client connect, from:%s\n",inet_ntoa(from.sin_addr)); /*查找合适位置,将client的文件描写叙述符放入*/ for(i=0;i
选择合适的server模型十分重要。对于编程有非常大的影响。