Socket编程实践(4) --多进程并发server
1.Socket地址復用
int getsockopt(int sockfd, int level, int optname,void *optval, socklen_t *optlen); int setsockopt(int sockfd, int level, int optname,const void *optval, socklen_t optlen);服務(wù)端盡可能使用SO_REUSEADDR,在綁定之前盡可能調(diào)用setsockopt來設(shè)置SO_REUSEADDR套接字選項。該選項可以使得server不必等待TIME_WAIT狀態(tài)消失就可以重啟服務(wù)器(對于TIME_WAIT狀態(tài)會在后面續(xù)有敘述).
可以在bind之前添加代碼(完整代碼請參照博文最后):
int on = 1;if (setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)) == -1)err_exit("setsockopt SO_REUSEADDR error");用以支持地址復用.
?
2.process-per-connecton
我們的echo服務(wù)器最大的缺點就是無法支持多客戶連接,即使客戶端能夠連接到服務(wù)器上(client端connect時并沒有出錯返回),?服務(wù)器也不為該客戶做服務(wù),(直接沒什么反應(yīng)),雖然鏈接是有的(也就是說,客戶端是已經(jīng)連接到服務(wù)器上的了,但是服務(wù)器就是不搭理你....),?我們提出的改進方案是process-per-connection(一條連接一個進程,?我們在多線程那一章中曾經(jīng)提出過一條連接一個線程,?這種方案相比較而言能夠比多進程擁有更高的并發(fā)量);
/** 示例:echo server改進, 多進程模型(client并未更改)**/ void echo(int clientfd); int main() {int listenfd = socket(AF_INET, SOCK_STREAM, 0);if (listenfd == -1)err_exit("socket error");int on = 1;if (setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)) == -1)err_exit("setsockopt SO_REUSEADDR error");struct sockaddr_in addr;addr.sin_family = AF_INET;addr.sin_port = htons(8001);addr.sin_addr.s_addr = htonl(INADDR_ANY);if (bind(listenfd, (const struct sockaddr *)&addr, sizeof(addr)) == -1)err_exit("bind error");if (listen(listenfd, SOMAXCONN) == -1)err_exit("listen error");struct sockaddr_in clientAddr;//謹記: 此處一定要初始化socklen_t addrLen = sizeof(clientAddr);while (true){int clientfd = accept(listenfd, (struct sockaddr *)&clientAddr, &addrLen);if (clientfd == -1)err_exit("accept error");//打印客戶IP地址與端口號cout << "Client information: " << inet_ntoa(clientAddr.sin_addr)<< ", " << ntohs(clientAddr.sin_port) << endl;pid_t pid = fork();if (pid == -1)err_exit("fork error");else if (pid > 0)close(clientfd);//子進程處理鏈接else if (pid == 0){close(listenfd);echo(clientfd);//子進程一定要exit, 否則的話, 該子進程也會回到accept處exit(EXIT_SUCCESS);}}close(listenfd); } void echo(int clientfd) {char buf[512] = {0};int readBytes;while ((readBytes = read(clientfd, buf, sizeof(buf))) > 0){cout << buf;if (write(clientfd, buf, readBytes) == -1)err_exit("write socket error");memset(buf, 0, sizeof(buf));}if (readBytes == 0){cerr << "client connect closed..." << endl;close(clientfd);}else if (readBytes == -1)err_exit("read socket error"); }完整代碼實現(xiàn):
http://download.csdn.net/detail/hanqing280441589/8458053
?
3.?P2P聊天程序設(shè)計與實現(xiàn)
server端與client都有兩個進程:
? ? (1)父進程負責從socket中讀取數(shù)據(jù)將其寫至終端,?由于父進程使用的是read系統(tǒng)調(diào)用的阻塞版本,?因此如果socket中沒有數(shù)據(jù)的話,?父進程會一直阻塞;?如果read返回0,?表示對端連接關(guān)閉,?則父進程會發(fā)送SIGUSR1信號給子進程,?通知其退出;
? ? (2)子進程負責從鍵盤讀取數(shù)據(jù)將其寫入socket,?如果鍵盤沒有數(shù)據(jù)的話,?則fgets調(diào)用會一直阻塞;
//serever端代碼與說明 int main() {int listenfd = socket(AF_INET, SOCK_STREAM, 0);if (listenfd == -1)err_exit("socket error");int on = 1;if (setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)) == -1)err_exit("setsockopt SO_REUSEADDR error");struct sockaddr_in addr;addr.sin_family = AF_INET;addr.sin_port = htons(8001);addr.sin_addr.s_addr = htonl(INADDR_ANY);if (bind(listenfd, (const struct sockaddr *)&addr, sizeof(addr)) == -1)err_exit("bind error");if (listen(listenfd, SOMAXCONN) == -1)err_exit("listen error");struct sockaddr_in clientAddr;socklen_t addrLen = sizeof(clientAddr);int clientfd = accept(listenfd, (struct sockaddr *)&clientAddr, &addrLen);if (clientfd == -1)err_exit("accept error");close(listenfd);//打印客戶IP地址與端口號cout << "Client information: " << inet_ntoa(clientAddr.sin_addr)<< ", " << ntohs(clientAddr.sin_port) << endl;char buf[512] = {0};pid_t pid = fork();if (pid == -1)err_exit("fork error");//父進程: socket -> terminalelse if (pid > 0){int readBytes;while ((readBytes = read(clientfd, buf, sizeof(buf))) > 0){cout << buf;memset(buf, 0, sizeof(buf));}if (readBytes == 0)cout << "client connect closed...\nserver exiting..." << endl;else if (readBytes == -1)err_exit("read socket error");//通知子進程退出kill(pid, SIGUSR1);}//子進程: keyboard -> socketelse if (pid == 0){signal(SIGUSR1, sigHandler);while (fgets(buf, sizeof(buf), stdin) != NULL){if (write(clientfd, buf, strlen(buf)) == -1)err_exit("write socket error");memset(buf, 0, sizeof(buf));}}close(clientfd);exit(EXIT_SUCCESS); } //client端代碼與說明 int main() {int sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd == -1)err_exit("socket error");//填寫服務(wù)器端口號與IP地址struct sockaddr_in serverAddr;serverAddr.sin_family = AF_INET;serverAddr.sin_port = htons(8001);serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");if (connect(sockfd, (const struct sockaddr *)&serverAddr, sizeof(serverAddr)) == -1)err_exit("connect error");char buf[512] = {0};pid_t pid = fork();if (pid == -1)err_exit("fork error");//父進程: socket -> terminalelse if (pid > 0){int readBytes;while ((readBytes = read(sockfd, buf, sizeof(buf))) > 0){cout << buf;memset(buf, 0, sizeof(buf));}if (readBytes == 0)cout << "server connect closed...\nclient exiting..." << endl;else if (readBytes == -1)err_exit("read socket error");kill(pid, SIGUSR1);}//子進程: keyboard -> socketelse if (pid == 0){signal(SIGUSR1, sigHandler);while (fgets(buf, sizeof(buf), stdin) != NULL){if (write(sockfd, buf, strlen(buf)) == -1)err_exit("write socket error");memset(buf, 0, sizeof(buf));}}close(sockfd);exit(EXIT_SUCCESS); }完整代碼實現(xiàn):
http://download.csdn.net/detail/hanqing280441589/8460013
總結(jié)
以上是生活随笔為你收集整理的Socket编程实践(4) --多进程并发server的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 95cloud云主机管理系统 使用手册
- 下一篇: 走进心理学