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);
服务端尽可能使用SO_REUSEADDR,在绑定之前尽可能调用setsockopt来设置SO_REUSEADDR套接字选项。该选项可以使得server不必等待TIME_WAIT状态消失就可以重启服务器(对于TIME_WAIT状态会在后面续有叙述).
可以在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服务器最大的缺点就是无法支持多客户连接,即使客户端能够连接到服务器上(client端connect时并没有出错返回), 服务器也不为该客户做服务,(直接没什么反应),虽然链接是有的(也就是说,客户端是已经连接到服务器上的了,但是服务器就是不搭理你....), 我们提出的改进方案是process-per-connection(一条连接一个进程, 我们在多线程那一章中曾经提出过一条连接一个线程, 这种方案相比较而言能够比多进程拥有更高的并发量);
/** 示例: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"); }
完整代码实现:
http://download.csdn.net/detail/hanqing280441589/8458053
3. P2P聊天程序设计与实现
server端与client都有两个进程:
(1)父进程负责从socket中读取数据将其写至终端, 由于父进程使用的是read系统调用的阻塞版本, 因此如果socket中没有数据的话, 父进程会一直阻塞; 如果read返回0, 表示对端连接关闭, 则父进程会发送SIGUSR1信号给子进程, 通知其退出;
(2)子进程负责从键盘读取数据将其写入socket, 如果键盘没有数据的话, 则fgets调用会一直阻塞;
//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 -> terminal else 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 -> socket else 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"); //填写服务器端口号与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 -> terminal else 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 -> socket else 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); }
完整代码实现:
http://download.csdn.net/detail/hanqing280441589/8460013
Socket编程实践(4) --多进程并发server的更多相关文章
- Socket编程实践(10) --select的限制与poll的使用
select的限制 用select实现的并发服务器,能达到的并发数一般受两方面限制: 1)一个进程能打开的最大文件描述符限制.这可以通过调整内核参数.可以通过ulimit -n(number)来调整或 ...
- C# socket编程实践
C# socket编程实践——支持广播的简单socket服务器 在上篇博客简单理解socket写完之后我就希望写出一个websocket的服务器了,但是一路困难重重,还是从基础开始吧,先搞定C# ...
- Socket编程实践(6) --TCP服务端注意事项
僵尸进程处理 1)通过忽略SIGCHLD信号,避免僵尸进程 在server端代码中添加 signal(SIGCHLD, SIG_IGN); 2)通过wait/waitpid方法,解决僵尸进程 sign ...
- Socket编程实践(6) --TCPNotes服务器
僵尸进程过程 1)通过忽略SIGCHLD信号,避免僵尸进程 在server端代码中加入 signal(SIGCHLD, SIG_IGN); 2)通过wait/waitpid方法.解决僵尸进程 sign ...
- Socket编程实践(12) --UDP编程基础
UDP特点 无连接,面向数据报(基于消息,不会粘包)的传输数据服务; 不可靠(可能会丢包, 乱序, 反复), 但因此普通情况下UDP更加高效; UDP客户/服务器模型 UDP-API使用 #inclu ...
- C# socket编程实践——支持广播的简单socket服务器
在上篇博客简单理解socket写完之后我就希望写出一个websocket的服务器了,但是一路困难重重,还是从基础开始吧,先搞定C# socket编程基本知识,写一个支持广播的简单server/clie ...
- Socket编程实践(2) Socket API 与 简单例程
在本篇文章中,先介绍一下Socket编程的一些API,然后利用这些API实现一个客户端-服务器模型的一个简单通信例程.该例子中,服务器接收到客户端的信息后,将信息重新发送给客户端. socket()函 ...
- Socket编程实践(1) 基本概念
1. 什么是socket socket可以看成是用户进程与内核网络协议栈的编程接口.TCP/IP协议的底层部分已经被内核实现了,而应用层是用户需要实现的,这部分程序工作在用户空间.用户空间的程序需要通 ...
- Socket编程实践(4) --更复杂的过程server
1.Socket地址复用 int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen); in ...
随机推荐
- IOI2016Day2. Messy
题目链接:http://uoj.ac/problem/239 题目大意: 这是一道交互题,交互库维护了一个数据结构,可以存储n为二进制串,一开始你可以向空的数据结构中插入若干二进制串, 接下来这个数据 ...
- 网站用户身份识别俩大招之cookie
导航: 原理介绍 代码实现 过程分析 追踪Cookie 原理介绍 众所周知,http协议是无状态的协议,简单理解是用户的前一步操作和后一步操作之间没有关系,互相不知道,不干扰.而在很多场景下,浏览网页 ...
- Noip2017 普及 T3 Chess
神奇的场上原码 #include<iostream> #include<cstdio> #include<cstring> #include<queue> ...
- 南京邮电大学java程序设计作业在线编程第五次作业
王利国的"Java语言程序设计第5次作业(2018)"详细 主页 我的作业列表 作业结果详细 总分:100 选择题得分:50 1. 以下哪一个工具是Java的编译器?( ) A. ...
- delphi 线程教学第五节:多个线程同时执行相同的任务
第五节:多个线程同时执行相同的任务 1.锁 设,有一个房间 X ,X为全局变量,它有两个函数 X.Lock 与 X.UnLock; 有如下代码: X.Lock; 访问资源 P; ...
- vmware 12中安装MAC OS X Lion 10.7
下载并安装vmware. 下载并安装MAC补丁. 创建虚拟机. 设置ISO文件. 开启虚拟机. 安装vmware tools. 1. 下载并安装vmware.我是直接在腾 ...
- “ML学分计划”说明书
计划的由来 我们是一群对机器学习感兴趣的小伙伴,对于神奇的机器学习经常有"一探究竟"的冲动,却因为孤身一人学习的寂寞.亦或繁忙考试工作之余的小小拖延症,而没有持续这份对知识的渴求和 ...
- Android反编译(未混淆的apk)
Android反编译(未混淆的apk) 工具 dex2jar 下载地址:我的CSDN 或者 官网 jd-gui 下载地址:我的CSDN 或者 官网 反编译步骤 1. 将APK解压缩,获取classes ...
- Programming In Scala笔记-第十九章、类型参数,协变逆变,上界下界
本章主要讲Scala中的类型参数化.本章主要分成三个部分,第一部分实现一个函数式队列的数据结构,第二部分实现该结构的内部细节,最后一个部分解释其中的关键知识点.接下来的实例中将该函数式队列命名为Que ...
- 判断&数学&生活
作者:黄永刚 初次接触<概率论与数理统计>这门课的时候,脑袋中只有三个词:黑球.白球.袋子,所有的课程内容就是先取,后取,接触一月之后成功的被放趴下了,因此对于这门课程是没有什么好感的,考 ...