Select网络模型

Select模型属于IO多路复用模型的一种,select使用一个socket数组来存放连接的socket。select会监视数组中所有的socket,可以知道哪些socket可读或者可写,从而避免无效操作(例如去读取不可读的socket,而在select中可以先查询该socket是否可读)。这样多个socket的IO操作能在一个线程内并发交替完成,这就是IO多路复用(复用同一个线程)。

使用select模型实现的简单服务端与客户端

服务端代码

#define WIN32_LEAN_AND_MEAN
//#define _WINSOCK_DEPRECATED_NO_WARNINGS#include<windows.h>
#include<WinSock2.h>
#include<stdio.h>
#include<WS2tcpip.h>
#include<vector>#pragma comment(lib, "ws2_32.lib")//windows下调用动态库
using std::vector;enum CMD
{CMD_LOGIN,CMD_LOGIN_RESULT,CMD_LOGOUT,CMD_LOGOUT_RESULT,CMD_NEW_USER_JOIN,CMD_ERROR
};struct DataHeader
{short dataLen;short cmd;
};struct Login : public DataHeader
{Login() {dataLen = sizeof(Login);cmd = CMD_LOGIN;}char userName[32];char password[32];
};
struct LoginResult : public DataHeader
{LoginResult() {dataLen = sizeof(LoginResult);cmd = CMD_LOGIN_RESULT;}int result = 1;
};struct LogOut : public DataHeader
{LogOut() {dataLen = sizeof(LogOut);cmd = CMD_LOGOUT;}char userName[32];
};
struct LogOutResult : public DataHeader
{LogOutResult() {dataLen = sizeof(LogOutResult);cmd = CMD_LOGOUT_RESULT;}int result = 1;
};
struct NewUserJoin : public DataHeader
{NewUserJoin() {dataLen = sizeof(NewUserJoin);cmd = CMD_NEW_USER_JOIN;}int result = 1;int sockID;
};void sendToAll(vector<SOCKET> clients, NewUserJoin newUser) {for (auto item : clients) {send(item, (const char*)&newUser, sizeof(NewUserJoin), 0);}
}int processCmd(SOCKET _cSock) {DataHeader header = {};//5.接收请求int nLen = recv(_cSock, (char*)&header, sizeof(DataHeader), 0);if (nLen <= 0) {printf("客户端%d已退出\n", int(_cSock));return -1;}//6.处理请求,7.send 向客户端发送数据if (header.cmd == CMD_LOGIN) {Login login = {};recv(_cSock, (char*)&login + sizeof(DataHeader), sizeof(Login) - sizeof(DataHeader), 0);printf("接收客户端=%d到命令: CMD_LOGIN 数据长度: %d\n", int(_cSock), login.dataLen);printf("客户端=%d用户%s登录成功\n", int(_cSock), login.userName);LoginResult loginRet;send(_cSock, (char*)&loginRet, sizeof(LoginResult), 0);}else if (header.cmd == CMD_LOGOUT) {LogOut logOut = {};recv(_cSock, (char*)&logOut + sizeof(DataHeader), sizeof(LogOut) - sizeof(DataHeader), 0);printf("接收到客户端=%d命令: CMD_LOGOUT 数据长度: %d\n", int(_cSock), logOut.dataLen);printf("客户端=%d用户%s退出成功\n", int(_cSock), logOut.userName);LogOutResult logOutRet;send(_cSock, (char*)&logOutRet, sizeof(LogOutResult), 0);}else {header.cmd = CMD_ERROR;header.dataLen = 0;send(_cSock, (char*)&header, sizeof(DataHeader), 0);}return 0;
}int main() {//存放客户端socketstd::vector<SOCKET> clients;//启动windows网络环境WORD ver = MAKEWORD(2, 2);WSADATA dat;WSAStartup(ver, &dat);////1.建立socketSOCKET _sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);sockaddr_in _sin = {};_sin.sin_family = AF_INET;_sin.sin_port = htons(8888);//_sin.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); //INADDR_ANY默认地址char strIp[] = "127.0.0.1";inet_pton(AF_INET, strIp, &_sin.sin_addr); //使用inet_pton替代inet_addr//2.bind绑定用于客户端连接的网络端口if (SOCKET_ERROR == bind(_sock, (sockaddr*)&_sin, sizeof(_sin))){printf("绑定失败\n");}else {printf("绑定成功\n");}//3.listen 监听绑定的端口if (SOCKET_ERROR == listen(_sock, 5)){printf("监听失败\n");}else {printf("监听成功\n");}while (true){//伯克利socket//fd_set 存放socket的集合fd_set fdsRead, fdsWrite, fdsExp;//清空FD_ZERO(&fdsRead);FD_ZERO(&fdsWrite);FD_ZERO(&fdsExp);//放入FD_SET(_sock, &fdsRead);FD_SET(_sock, &fdsWrite);FD_SET(_sock, &fdsExp);for (auto item : clients) {FD_SET(item, &fdsRead);}//nfds是指fd_set集合中所有socket的范围,不是数量。Windows中该参数可写0。timeval time = { 1, 0 };//int ret = select(_sock+1, &fdsRead, &fdsWrite, &fdsExp, NULL);//非阻塞模式int ret = select(_sock+1, &fdsRead, &fdsWrite, &fdsExp, &time);if (ret < 0) {printf("select结束\n");break;}//判断socket是否在set中if (FD_ISSET(_sock, &fdsRead)) {FD_CLR(_sock, &fdsRead);//4.accept 等待连接sockaddr_in clientAddr = {};char IPdotdec[20];//存放点分十进制IP地址int nAddrLen = sizeof(sockaddr_in);SOCKET _cSock = INVALID_SOCKET;_cSock = accept(_sock, (sockaddr*)&clientAddr, &nAddrLen);if (_cSock == INVALID_SOCKET){printf("接收到无效客户端SOCKET\n");}else{NewUserJoin newUser;newUser.sockID = int(_cSock);//向其他已有客户端发消息sendToAll(clients, newUser);clients.push_back(_cSock);printf("有客户端连接到服务器:Sock = %d,IP = %s \n", (int)_cSock, inet_ntop(AF_INET, &clientAddr.sin_addr, IPdotdec, 16));//使用inet_ntop替代ntoa}}printf("服务端空闲中\n");for (size_t n = 0; n < fdsRead.fd_count; ++n) {if (-1 == processCmd(fdsRead.fd_array[n])) {auto iter = find(clients.begin(), clients.end(), fdsRead.fd_array[n]);if (iter != clients.end()) {clients.erase(iter);}}}}//8.关闭socketfor (auto item : clients) {closesocket(item);}closesocket(_sock);//WSACleanup();printf("Sever程序结束");getchar();return 0;
}

客户端代码

#define WIN32_LEAN_AND_MEAN
//#define _WINSOCK_DEPRECATED_NO_WARNINGS#include<windows.h>
#include<WinSock2.h>
#include<stdio.h>
#include<WS2tcpip.h>#pragma comment(lib, "ws2_32.lib")//windows下调用动态库enum CMD
{CMD_LOGIN,CMD_LOGIN_RESULT,CMD_LOGOUT,CMD_LOGOUT_RESULT,CMD_NEW_USER_JOIN,CMD_ERROR
};struct DataHeader
{short dataLen;short cmd;
};struct Login : public DataHeader
{Login() {dataLen = sizeof(Login);cmd = CMD_LOGIN;}char userName[32];char password[32];
};
struct LoginResult : public DataHeader
{LoginResult() {dataLen = sizeof(LoginResult);cmd = CMD_LOGIN_RESULT;}int result = 1;
};struct LogOut : public DataHeader
{LogOut() {dataLen = sizeof(LogOut);cmd = CMD_LOGOUT;}char userName[32];
};
struct LogOutResult : public DataHeader
{LogOutResult() {dataLen = sizeof(LogOutResult);cmd = CMD_LOGOUT_RESULT;}int result = 1;
};
struct NewUserJoin : public DataHeader
{NewUserJoin() {dataLen = sizeof(NewUserJoin);cmd = CMD_NEW_USER_JOIN;}int result = 1;int sockID;
};
int processCmd(SOCKET _cSock) {DataHeader header = {};//5.接收请求int nLen = recv(_cSock, (char*)&header, sizeof(DataHeader), 0);if (nLen <= 0) {printf("与服务器断开连接,processCmd任务结束\n");return -1;}//6.处理请求,7.send 向客户端发送数据if (header.cmd == CMD_NEW_USER_JOIN) {NewUserJoin newUser = {};recv(_cSock, (char*)&newUser + sizeof(DataHeader), sizeof(NewUserJoin) - sizeof(DataHeader), 0);printf("接收到服务器数据长度: %d,新客户端%d已连接\n", newUser.dataLen, newUser.sockID);}else if (header.cmd == CMD_LOGIN_RESULT) {LoginResult loginResult = {};recv(_cSock, (char*)&loginResult + sizeof(DataHeader), sizeof(loginResult) - sizeof(DataHeader), 0);printf("接收到服务端命令: CMD_LOGIN_RESULT数据长度: %d\n", loginResult.dataLen);}else if (header.cmd == CMD_LOGOUT_RESULT) {LogOutResult logOutResult = {};recv(_cSock, (char*)&logOutResult + sizeof(DataHeader), sizeof(logOutResult) - sizeof(DataHeader), 0);printf("接收到服务端命令: CMD_LOGOUT_RESULT数据长度: %d\n", logOutResult.dataLen);}else {header.cmd = CMD_ERROR;header.dataLen = 0;send(_cSock, (char*)&header, sizeof(DataHeader), 0);}return 0;
}int main() {bool loginSignal = true;//启动windows网络环境WORD ver = MAKEWORD(2, 2);WSADATA dat;WSAStartup(ver, &dat);////1.建立socketSOCKET _sock = socket(AF_INET, SOCK_STREAM, 0);if (INVALID_SOCKET == _sock) {printf("error, failed to build socket\n");}else {printf("successfully build socket\n");}sockaddr_in _sin = {};_sin.sin_family = AF_INET;_sin.sin_port = htons(8888);//_sin.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); //INADDR_ANY默认地址char strIp[] = "127.0.0.1";inet_pton(AF_INET, strIp, &_sin.sin_addr); //使用inet_pton替代inet_addr//2.连接服务器int connectRet = connect(_sock, (sockaddr*)&_sin, sizeof(sockaddr_in));if (INVALID_SOCKET == connectRet) {printf("连接服务器失败\n");}else {printf("连接服务器成功\n");}char cmdBuf[128] = {};while (true) {//伯克利socket//fd_set 存放socket的集合fd_set fdsRead;//清空FD_ZERO(&fdsRead);//放入FD_SET(_sock, &fdsRead);timeval time = { 1, 0 };int ret = select(_sock, &fdsRead, 0, 0, &time);if (ret < 0) {printf("select任务结束");break;}if (FD_ISSET(_sock, &fdsRead)) {FD_CLR(_sock, &fdsRead);if (-1 == processCmd(_sock)) {printf("select结束\n");break;}}printf("客户端空闲中\n");if (loginSignal) {Login login;strcpy_s(login.userName, "zhangwei");strcpy_s(login.password, "123");send(_sock, (const char*)&login, sizeof(login), 0);loginSignal = false;}Sleep(1000);}//7.关闭socketclosesocket(_sock);//WSACleanup();printf("Client程序结束");getchar();return 0;
}

个人博客

Select网络模型下的简单CS实例相关推荐

  1. 如何去掉超链接下划线用三个简单的实例来说明

    去掉超链接的下划线,需要用样式表CSS来控制,如果你暂时不想深入了解CSS的概念,下面将举三个简单的实例来说明如何控制超链接的下划线.用记事本打开网页源代码(也可以先用IE打开网页,然后点击IE菜单栏 ...

  2. 小甲鱼 OllyDbg 教程系列 (二) :从一个简单的实例来了解PE文件

    小甲鱼视频讲解: https://www.bilibili.com/video/av6889190?p=6 https://www.bilibili.com/video/av6889190?p=7 从 ...

  3. nginx Win下实现简单的负载均衡(2)站点共享Session

    快速目录: 一.nginx Win下实现简单的负载均衡(1)nginx搭建部署 二.nginx Win下实现简单的负载均衡(2)站点共享Session 三.nginx Win下实现简单的负载均衡(3) ...

  4. 修改linux下全局数据库名,linux/unix下修改oracle数据库实例名的方法

    linux/unix下修改oracle数据库实例名的方法 2018年12月10日 | 萬仟网IT编程 | 我要评论 linux/unix下修改oracle实例名的方法 1.检查原来的数据库实例名 $ ...

  5. 《Abaqus GUI程序开发指南(Python语言)》——第一章 概述1.1 简单插件实例——创建带孔板有限元模型...

    本节书摘来自异步社区<Abaqus GUI程序开发指南(Python语言)>一书中的第1章,第1.1节,作者: 贾利勇 , 富琛阳子 , 贺高 , 周正光 更多章节内容可以访问云栖社区&q ...

  6. Linux TCP server系列(6)-select模式下的多线程server

    目标: 修改上一篇的select模式下的server,让它使用多线程来处理客户端请求(多进程的模式已经在上篇中加了注释). 思路: (1)服务器 我们已经在之前的客户端模型多个并发用户的过程中使用过多 ...

  7. Linux TCP server系列(5)-select模式下的单进程server

    目标:  让服务器退化为单进程模式,但是利用select来提升性能 思路:     (1)服务器        传统的单进程服务器一旦accept了客户端的TCP连接后,就转入客户请求的处理,处理完成 ...

  8. MyEclipse下XFire开发Webservice实例

    最近在研究JAVA开发Webservice,发现网络上比较流行的几种选择AXIS.XFire.CFX(XFire的下一代),前几天转了几篇关于这三种选择的比较的文章,对它们已经有了些概念.决定自己实践 ...

  9. Bootstrap完美select标签下拉菜单实现

    Bootstrap <select>下拉菜单实现 初级者使用bootstrap框架时,大部分对官方提供的下拉菜单dropdown组件不是很感冒! 所以,这里就提供简单原生下拉标签<s ...

最新文章

  1. execute、executeQuery和executeUpdate之间的区别
  2. 清华《人工智能之认知图谱》:中美高水平学者数量领跑全球
  3. SAP QM 源检验(Source Inspection)功能展示
  4. spring boot处理请求返回值的格式(自定义消息转换器)
  5. Docker 实战教程之从入门到提高 (四)
  6. Java学习进阶—高级编程
  7. 方法超出 android,Android工程方法数超过64k,The number of method references in a .dex file cannot exceed 64K....
  8. 软件GUI测试中的关注点
  9. java实现扫雷小游戏【完整版】
  10. 使用ssh连接虚拟机保姆级教程
  11. 有一种友谊可以美的让人心颤——CHANDLER和JOEY 转贴 来自friends论坛
  12. 工业相机镜头的参数与选型
  13. TypeScript 类型声明文件.d.ts
  14. 机器学习环境配置(Tesla K80安装PyTorch的全过程)
  15. java引用另一个程序图像,java – 在Android应用程序上显示图像的另一种方法
  16. Mina2框架--服务端与客户端通信
  17. 宇视大屏蓝屏排查步骤
  18. Blinn-Phong反射模型
  19. 商城-3 查询商品详情页信息
  20. 给我说说你对Java GC机制的理解?

热门文章

  1. 在阿里云ECS上安装Docker、Tomcat并部署官网(无后端交互)
  2. 数据结构基础(4) --快速排序
  3. Date动态获取时间
  4. 利用Layer组件弹出多个对话框(非嵌套)与关闭及刷新
  5. 算法积累之处理数组数据
  6. SSL *** vs IPSEC ***
  7. ASP.NET 学习笔记_06 Httphandler
  8. list接口中的常用方法例子
  9. GetManifestResourceStream得到的Stream是null的解决
  10. 挑战NPC(洛谷-P4258)