Linux 網路程式設計 全解(四)--------多程序併發伺服器和多執行緒併發伺服器
阿新 • • 發佈:2019-01-28
寫在前面:這個系列也是停滯了20多天了,從今天開始再次步入正軌,以後每個週末都會陸陸續續的更新,這個系列預計完結的時間還會在大約一個月左右,今天靜下心來多整理幾篇。QQ:993650814
正文:
一、多程序併發伺服器
設計思路:當有新的客戶端連線到伺服器時,伺服器會呼叫accept函式與客戶端建立連線,並建立子程序與連線上來的客戶端進行通訊。
程式碼如下:
#include "stdio.h" #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <unistd.h> #include <string.h> #include <signal.h> #include <sys/wait.h> #define SER_PORT (9999) void do_sigchild(int param) { printf("%s\n",__FUNCTION__); while(waitpid(0,NULL,WNOHANG)>0); } int main(void) { int lfd = -1; int cfd = -1; struct sockaddr_in ser_addr; struct sockaddr_in cli_addr; pid_t pid = -1; char buf[1024] = {0x0}; int read_size = 0; struct sigaction newact; lfd = socket(AF_INET, SOCK_STREAM, 0); memset(&ser_addr,0,sizeof(struct sockaddr_in)); ser_addr.sin_family = AF_INET; ser_addr.sin_port = htons(SER_PORT); ser_addr.sin_addr.s_addr = htonl(INADDR_ANY); bind(lfd,(struct sockaddr *)&ser_addr,sizeof(ser_addr)); memset(&newact,0,sizeof(struct sigaction)); newact.sa_handler = do_sigchild; newact.sa_flags = 0; sigemptyset(&newact.sa_mask); //避免出現殭屍程序的情況:指定父程序回收子程序 //sigaction(SIGCHLD, &newact,NULL); listen(lfd, 128); while(1) { socklen_t cli_addr_len = sizeof(cli_addr); cfd = accept(lfd, (struct sockaddr *)&cli_addr, &cli_addr_len); printf("client connect server\n"); pid = fork(); if(0 == pid) { //子程序 close(lfd); while(1) { read_size = read(cfd, buf, sizeof(buf)); if(read_size == 0) { printf("client has been closed\n"); break; } printf("read content:%s\n",buf); memset(buf,0,sizeof(buf)); } printf("child pcb return \n"); close(cfd); return 0; }else if(pid > 0) { //父程序 close(cfd); continue; }else { printf("fork error\n"); } } close(lfd); return 0; }
測試結果:
二、多執行緒併發伺服器
設計思路:多執行緒伺服器跟多程序伺服器的思路其實一樣,當有client發生連線時,伺服器會呼叫accept跟client發生連線,並建立子執行緒來跟client通訊。
程式碼如下:
#include "stdio.h" #include <sys/types.h> #include <sys/socket.h> #include <pthread.h> #include <arpa/inet.h> #include <netinet/in.h> #include <netinet/ip.h> #include <stdlib.h> #include <string.h> #define SERVER_PORT (8888) typedef struct { struct sockaddr_in cli_addr; int cfd; }cliInfo_t; void *task(void * arg) { cliInfo_t * pCliInfo = (cliInfo_t *)arg; char ipAddr[10] = {0}; int cli_port = 0; int read_size = 0; char buf[1024] = {0}; inet_ntop(AF_INET, &(pCliInfo ->cli_addr.sin_addr), ipAddr, sizeof(ipAddr)); printf("client ip addr : %s\n",ipAddr); cli_port = ntohs(pCliInfo ->cli_addr.sin_port); printf("client port : %d\n",cli_port); //子執行緒分離,防止產生殭屍執行緒 pthread_detach(pthread_self()); while(1) { read_size = read(pCliInfo ->cfd, buf, sizeof(buf)); if(read_size == 0) { printf("client closed\n"); break; } printf("receive data from client:%s",buf); memset(buf,0,sizeof(buf)); } close(pCliInfo ->cfd); return NULL; } int main(void) { int lfd = -1; int cfd = -1; struct sockaddr_in ser_sock_addr, cli_sock_addr; socklen_t cli_addr_len = sizeof(cli_sock_addr); pthread_t tid; cliInfo_t cliInfo[100]; int i = 0; int ret = -1; lfd = socket(AF_INET,SOCK_STREAM,0); ser_sock_addr.sin_family = AF_INET; ser_sock_addr.sin_port = htons(SERVER_PORT); ser_sock_addr.sin_addr.s_addr = htonl(INADDR_ANY); ret = bind(lfd, (struct sockaddr *)&ser_sock_addr,sizeof(ser_sock_addr)); if(ret < 0) { printf("bind error \n"); return -1; } listen(lfd, 30); while(1) { cfd = accept(lfd, (struct sockaddr *)&cli_sock_addr, &cli_addr_len); cliInfo[i].cli_addr = cli_sock_addr; cliInfo[i].cfd = cfd; //當有客戶端跟伺服器發生連線時,建立子執行緒跟客戶端資料通訊 pthread_create(&tid, NULL,task, (void *)&cliInfo[i]); i++; } return 0; }
測試結果:
現象其實跟多程序伺服器的現象是一樣的。