并发Socket程序设计
1. 非阻塞并发模型
直接将socket设置为非阻塞, 轮询处理连接和接收。
缺点: 极大消耗CPU资源,不适合实际应用。
2. 信号驱动模型
当Socket文件描述符准备就绪后 内核会给进程发送一个 SIGIO 或 SIGPOLL信号,signal(SIGIO, fun);
实际中 并不只有套接字有输入时才会发出这些信号, 实际情况中并不能用。
3. 超时并发模型
A: 通过套接字选项设置超时
通过套接字选项SO_SNDTIMEO 和 SO_RCVTIMEO设置读写超时,但是只能设置读写超时,不能设置connect 和 accept 等连接超时,并且有的系统不支持。
B: 通过信号SIGALRM 设置超时
#include <comlib.h> static int nTimeOut = 0; void OnTimeout(int nSignal) { signal(nSignal, SIG_IGN); nTimeOut = 1; return; } int main(int argc, char *argv[]) { int nSock = -1, ret; if (argc != 3) return 1; nTimeOut = 0; signal(SIGALRM, OnTimeout); alarm(10); ret = ConnectSock(&nSock, atoi(argv[2]), argv[1]); alarm(0); signal(SIGALRM, SIG_IGN); if (nTimeOut == 1) printf("Connect Timeout.\n"); else if (ret == 0) printf("Connect Success.\n"); else printf("Connect Error!\n"); if (nSock != -1) close(nSock); return 0; }
C: 通过信号SIGALRM 与 跳转设置超时
#include <comlib.h> #include <setjmp.h> static int nTimeOut = 0; jmp_buf env; void OnTimeout(int nSignal) { signal(nSignal, SIG_IGN); nTimeOut = 1; longjmp(env, 1); return; } int main(int argc, char *argv[]) { int nSock = -1, ret; if (argc != 3) return 1; nTimeOut = 0; setjmp(env); if (nTimeOut == 1) printf("Connect Timeout.\n"); else { signal(SIGALRM, OnTimeout); alarm(10); ret = ConnectSock(&nSock, atoi(argv[2]), argv[1]); alarm(0); signal(SIGALRM, SIG_IGN); if (ret == 0) printf("Connect Success.\n"); else printf("Connect Error!\n"); } if (nSock != -1) close(nSock); return 0; }
4. 多路复用并发模型
5. 多进程并发模型
A: 不固定进程数的并发模型
比如父进程只执行函数accept等待并完成客户端连接申请,子进程执行函数recv等待客户端的信息发送。
缺陷: 客户端无限申请,服务器比爆。
B: 固定进程数的并发模型
服务器父进程在创建监听套接字(listen)后fork子进程, 由子进程等待客户端connect并 完成与客户端的通信交换等工作,父进程之后的功能只是维持子进程的数目不变。
#include<iostream> #include<string.h> #include<sys/types.h> #include<sys/socket.h> #include<netinet/in.h> #include<assert.h> #include<errno.h> #include<stdio.h> #include<arpa/inet.h> #include<stdio.h> #include<wait.h> #include<stdlib.h> #include<semaphore.h> #include<sys/ipc.h> using namespace std; int CreateSock( int *pSock, int nPort, int nMax ) { int ret, on; struct sockaddr_in addrin; struct sockaddr *paddr = (struct sockaddr *) &addrin; assert(pSock != NULL && nPort >0 && nMax > 0); memset(&addrin, 0, sizeof(addrin)); addrin.sin_family = AF_INET; addrin.sin_addr.s_addr = htonl(INADDR_ANY); addrin.sin_port = htons(nPort); assert((*pSock = socket(AF_INET, SOCK_STREAM, 0)) > 0); on=1; ret = setsockopt( *pSock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on) ); if( (bind(*pSock, paddr, sizeof(addrin)))< 0 ) { perror("bind"); //cout << "bind error" << endl; return 1; } if( (listen(*pSock, nMax)) < 0 ) { cout << "listen error" << endl; return 1; } else { cout << "create cocket successfully" << endl; return 0; } return 1; } int AcceptSock(int *pSock, int nSock) { struct sockaddr_in addrin; socklen_t lSize; assert( pSock!=NULL && nSock>0 ); while(1) { lSize = sizeof(addrin); memset(&addrin, 0, sizeof(addrin)); if( (*pSock = accept(nSock, (struct sockaddr *)&addrin, &lSize)) > 0 ) return 0; else if( errno == EINTR ) continue; else assert(0); } } int ConnectSock(int *pSock, int nPort, char* pAddr) { struct sockaddr_in addrin; long lAddr; int nSock; assert(pSock!=NULL && nPort>0 && pAddr!=NULL); assert( (nSock = socket(AF_INET, SOCK_STREAM, 0)) > 0 ); memset(&addrin, 0, sizeof(addrin)); addrin.sin_family = AF_INET; addrin.sin_addr.s_addr = inet_addr(pAddr); addrin.sin_port = htons(nPort); if( (connect(nSock, (struct sockaddr *)&addrin , sizeof(addrin))) == 0 ) { *pSock = nSock; return 0; } close(nSock); return 1; } int LocateRemoteAddr(int nSock, char *pAddr) { struct sockaddr_in addrin; socklen_t lSize; if( nSock<=0 && pAddr==NULL ) { cout << "input error" << endl; return 1; } memset(&addrin, 0, sizeof(addrin)); if( (getpeername(nSock, (struct sockaddr*)&addrin, &lSize)) == 0 ) { strcpy(pAddr, inet_ntoa(addrin.sin_addr)); return 0; } else { cout << "getpeername error " << endl; return 1; } return 1; } int main() { cout << "tcp test!" << endl; int i, bShutdown = 0, MAXNUMBER = 3; int nSock, nSock1, nLisSock; char szAddr[30]; char buf[1024]; pid_t pid, nChild; sem_t sem; //信号量 sem_init(&sem, 0, 1); //初始化信号量 CreateSock(&nLisSock, 8888, 9); for( i=0; i<MAXNUMBER; i++ ) { nChild = fork(); if(nChild == 0) break; } if( nChild > 0 ) //父进程 { cout << "in parent process: " << getpid() << endl; while( !bShutdown ) { pid = wait(NULL); //父进程等待子进程结束,并补充子进程 if( pid < 0 ) { perror("wait"); continue; } printf("catch a process %d end \n", pid); nChild = fork(); if( nChild == 0 ) break; } exit(0); } else if( nChild == 0 ) //子进程 { while(1) { //cout << "in Child process: " << getpid() << endl; sem_wait(&sem); //信号量互斥 if( (AcceptSock(&nSock, nLisSock)) == 0 ) cout << "accept successfully" << endl; memset(buf, 0, sizeof(buf)); recv(nSock, buf, sizeof(buf), 0); cout << "in process: " << getpid() << " receive: " << buf << endl; close(nSock); sem_post(&sem); } } return 0; } /* int main() { cout << "tcp test!" << endl; int nSock, nSock1; char szAddr[30]; char buf[1024]; CreateSock(&nSock, 8888, 9); if( (AcceptSock(&nSock1, nSock)) == 0 ) cout << "accept successfully" << endl; memset(buf, 0, sizeof(buf)); recv(nSock1, buf, sizeof(buf), 0); cout << "receive: " << buf << endl; cout << "input a key, send: " << endl; fgetc(stdin); send(nSock1, "world", strlen("world"), 0); cout << "send: " << "world" << endl; //LocateRemoteAddr(nSock1, szAddr); //cout << "IP--->" << szAddr << endl; close(nSock); close(nSock1); return 0; } */