Socket编程实践(4) --多进程并发server

1.Socket地址复用

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. int getsockopt(int sockfd, int level, int optname,  
  2.                void *optval, socklen_t *optlen);  
  3. int setsockopt(int sockfd, int level, int optname,  
  4.                const void *optval, socklen_t optlen);  

服务端尽可能使用SO_REUSEADDR,在绑定之前尽可能调用setsockopt来设置SO_REUSEADDR套接字选项。该选项可以使得server不必等待TIME_WAIT状态消失就可以重启服务器(对于TIME_WAIT状态会在后面续有叙述).

可以在bind之前添加代码(完整代码请参照博文最后):

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. int on = 1;  
  2. if (setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,  
  3.                &on,sizeof(on)) == -1)  
  4.     err_exit("setsockopt SO_REUSEADDR error");  

用以支持地址复用.

 

2.process-per-connecton

我们的echo服务器最大的缺点就是无法支持多客户连接,即使客户端能够连接到服务器上(client端connect时并没有出错返回), 服务器也不为该客户做服务,(直接没什么反应),虽然链接是有的(也就是说,客户端是已经连接到服务器上的了,但是服务器就是不搭理你....), 我们提出的改进方案是process-per-connection(一条连接一个进程, 我们在多线程那一章中曾经提出过一条连接一个线程, 这种方案相比较而言能够比多进程拥有更高的并发量);

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. /** 示例:echo server改进, 多进程模型(client并未更改)**/  
  2. void echo(int clientfd);  
  3. int main()  
  4. {  
  5.     int listenfd = socket(AF_INET, SOCK_STREAM, 0);  
  6.     if (listenfd == -1)  
  7.         err_exit("socket error");  
  8.     int on = 1;  
  9.     if (setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,  
  10.                    &on,sizeof(on)) == -1)  
  11.         err_exit("setsockopt SO_REUSEADDR error");  
  12.   
  13.     struct sockaddr_in addr;  
  14.     addr.sin_family = AF_INET;  
  15.     addr.sin_port = htons(8001);  
  16.     addr.sin_addr.s_addr = htonl(INADDR_ANY);  
  17.     if (bind(listenfd, (const struct sockaddr *)&addr, sizeof(addr)) == -1)  
  18.         err_exit("bind error");  
  19.     if (listen(listenfd, SOMAXCONN) == -1)  
  20.         err_exit("listen error");  
  21.   
  22.     struct sockaddr_in clientAddr;  
  23.     //谨记: 此处一定要初始化  
  24.     socklen_t addrLen = sizeof(clientAddr);  
  25.     while (true)  
  26.     {  
  27.         int clientfd = accept(listenfd, (struct sockaddr *)&clientAddr, &addrLen);  
  28.         if (clientfd == -1)  
  29.             err_exit("accept error");  
  30.         //打印客户IP地址与端口号  
  31.         cout << "Client information: " << inet_ntoa(clientAddr.sin_addr)  
  32.              << ", " << ntohs(clientAddr.sin_port) << endl;  
  33.   
  34.         pid_t pid = fork();  
  35.         if (pid == -1)  
  36.             err_exit("fork error");  
  37.         else if (pid > 0)  
  38.             close(clientfd);  
  39.         //子进程处理链接  
  40.         else if (pid == 0)  
  41.         {  
  42.             close(listenfd);  
  43.             echo(clientfd);  
  44.             //子进程一定要exit, 否则的话, 该子进程也会回到accept处  
  45.             exit(EXIT_SUCCESS);  
  46.         }  
  47.     }  
  48.     close(listenfd);  
  49. }  
  50. void echo(int clientfd)  
  51. {  
  52.     char buf[512] = {0};  
  53.     int readBytes;  
  54.     while ((readBytes = read(clientfd, buf, sizeof(buf))) > 0)  
  55.     {  
  56.         cout << buf;  
  57.         if (write(clientfd, buf, readBytes) == -1)  
  58.             err_exit("write socket error");  
  59.         memset(buf, 0, sizeof(buf));  
  60.     }  
  61.     if (readBytes == 0)  
  62.     {  
  63.         cerr << "client connect closed..." << endl;  
  64.         close(clientfd);  
  65.     }  
  66.     else if (readBytes == -1)  
  67.         err_exit("read socket error");  
  68. }  

完整代码实现:

http://download.csdn.net/detail/hanqing280441589/8458053

 

3. P2P聊天程序设计与实现

server端与client都有两个进程:

    (1)父进程负责从socket中读取数据将其写至终端, 由于父进程使用的是read系统调用的阻塞版本, 因此如果socket中没有数据的话, 父进程会一直阻塞; 如果read返回0, 表示对端连接关闭, 则父进程会发送SIGUSR1信号给子进程, 通知其退出;

    (2)子进程负责从键盘读取数据将其写入socket, 如果键盘没有数据的话, 则fgets调用会一直阻塞;

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. //serever端代码与说明  
  2. int main()  
  3. {  
  4.     int listenfd = socket(AF_INET, SOCK_STREAM, 0);  
  5.     if (listenfd == -1)  
  6.         err_exit("socket error");  
  7.     int on = 1;  
  8.     if (setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,  
  9.                    &on,sizeof(on)) == -1)  
  10.         err_exit("setsockopt SO_REUSEADDR error");  
  11.   
  12.     struct sockaddr_in addr;  
  13.     addr.sin_family = AF_INET;  
  14.     addr.sin_port = htons(8001);  
  15.     addr.sin_addr.s_addr = htonl(INADDR_ANY);  
  16.     if (bind(listenfd, (const struct sockaddr *)&addr, sizeof(addr)) == -1)  
  17.         err_exit("bind error");  
  18.     if (listen(listenfd, SOMAXCONN) == -1)  
  19.         err_exit("listen error");  
  20.   
  21.     struct sockaddr_in clientAddr;  
  22.     socklen_t addrLen = sizeof(clientAddr);  
  23.     int clientfd = accept(listenfd, (struct sockaddr *)&clientAddr, &addrLen);  
  24.     if (clientfd == -1)  
  25.         err_exit("accept error");  
  26.     close(listenfd);  
  27.     //打印客户IP地址与端口号  
  28.     cout << "Client information: " << inet_ntoa(clientAddr.sin_addr)  
  29.          << ", " << ntohs(clientAddr.sin_port) << endl;  
  30.   
  31.     char buf[512] = {0};  
  32.     pid_t pid = fork();  
  33.     if (pid == -1)  
  34.         err_exit("fork error");  
  35.     //父进程: socket -> terminal  
  36.     else if (pid > 0)  
  37.     {  
  38.         int readBytes;  
  39.         while ((readBytes = read(clientfd, buf, sizeof(buf))) > 0)  
  40.         {  
  41.             cout << buf;  
  42.             memset(buf, 0, sizeof(buf));  
  43.         }  
  44.         if (readBytes == 0)  
  45.             cout << "client connect closed...\nserver exiting..." << endl;  
  46.         else if (readBytes == -1)  
  47.             err_exit("read socket error");  
  48.         //通知子进程退出  
  49.         kill(pid, SIGUSR1);  
  50.     }  
  51.     //子进程: keyboard -> socket  
  52.     else if (pid == 0)  
  53.     {  
  54.         signal(SIGUSR1, sigHandler);  
  55.         while (fgets(buf, sizeof(buf), stdin) != NULL)  
  56.         {  
  57.             if (write(clientfd, buf, strlen(buf)) == -1)  
  58.                 err_exit("write socket error");  
  59.             memset(buf, 0, sizeof(buf));  
  60.         }  
  61.     }  
  62.     close(clientfd);  
  63.     exit(EXIT_SUCCESS);  
  64. }  
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. //client端代码与说明  
  2. int main()  
  3. {  
  4.     int sockfd = socket(AF_INET, SOCK_STREAM, 0);  
  5.     if (sockfd == -1)  
  6.         err_exit("socket error");  
  7.   
  8.     //填写服务器端口号与IP地址  
  9.     struct sockaddr_in serverAddr;  
  10.     serverAddr.sin_family = AF_INET;  
  11.     serverAddr.sin_port = htons(8001);  
  12.     serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");  
  13.     if (connect(sockfd, (const struct sockaddr *)&serverAddr, sizeof(serverAddr)) == -1)  
  14.         err_exit("connect error");  
  15.   
  16.     char buf[512] = {0};  
  17.     pid_t pid = fork();  
  18.     if (pid == -1)  
  19.         err_exit("fork error");  
  20.     //父进程: socket -> terminal  
  21.     else if (pid > 0)  
  22.     {  
  23.         int readBytes;  
  24.         while ((readBytes = read(sockfd, buf, sizeof(buf))) > 0)  
  25.         {  
  26.             cout << buf;  
  27.             memset(buf, 0, sizeof(buf));  
  28.         }  
  29.         if (readBytes == 0)  
  30.             cout << "server connect closed...\nclient exiting..." << endl;  
  31.         else if (readBytes == -1)  
  32.             err_exit("read socket error");  
  33.         kill(pid, SIGUSR1);  
  34.     }  
  35.     //子进程: keyboard -> socket  
  36.     else if (pid == 0)  
  37.     {  
  38.         signal(SIGUSR1, sigHandler);  
  39.         while (fgets(buf, sizeof(buf), stdin) != NULL)  
  40.         {  
  41.             if (write(sockfd, buf, strlen(buf)) == -1)  
  42.                 err_exit("write socket error");  
  43.             memset(buf, 0, sizeof(buf));  
  44.         }  
  45.     }  
  46.     close(sockfd);  
  47.     exit(EXIT_SUCCESS);  
  48. }  

完整代码实现:

http://download.csdn.net/detail/hanqing280441589/8460013

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值