文章目录

  • 1 客户端升级为select模型
    • 1.1 概述
    • 1.2 客户端代码实现
    • 1.3 服务端代码实现

1 客户端升级为select模型

1.1 概述

这里我们为了让客户端有时间去处理其它业务逻辑,因此我们需要在客户端也引入select模型。

1.2 客户端代码实现

#define WIN32_LEAN_AND_MEAN#include<windows.h>
#include<WinSock2.h>
#include<stdio.h>#pragma comment(lib,"ws2_32.lib")enum CMD
{CMD_LOGIN,CMD_LOGIN_RESULT,CMD_LOGOUT,CMD_LOGOUT_RESULT,CMD_NEW_USER_JOIN,CMD_ERROR
};struct DataHeader
{short dataLength;short cmd;
};//DataPackage
struct Login : public DataHeader
{Login(){dataLength = sizeof(Login);cmd = CMD_LOGIN;}char userName[32];char PassWord[32];
};struct LoginResult : public DataHeader
{LoginResult(){dataLength = sizeof(LoginResult);cmd = CMD_LOGIN_RESULT;result = 0;}int result;
};struct Logout : public DataHeader
{Logout(){dataLength = sizeof(Logout);cmd = CMD_LOGOUT;}char userName[32];
};struct LogoutResult : public DataHeader
{LogoutResult(){dataLength = sizeof(LogoutResult);cmd = CMD_LOGOUT_RESULT;result = 0;}int result;
};struct NewUserJoin : public DataHeader
{NewUserJoin(){dataLength = sizeof(NewUserJoin);cmd = CMD_NEW_USER_JOIN;scok = 0;}int scok;
};int processor(SOCKET _cSock)
{//缓冲区char szRecv[4096] = {};// 5 接收客户端数据int nLen = recv(_cSock, szRecv, sizeof(DataHeader), 0);DataHeader* header = (DataHeader*)szRecv;if (nLen <= 0){printf("与服务器断开连接,任务结束。\n", _cSock);return -1;}switch (header->cmd){case CMD_LOGIN_RESULT:{recv(_cSock, szRecv + sizeof(DataHeader), header->dataLength - sizeof(DataHeader), 0);LoginResult* login = (LoginResult*)szRecv;printf("收到服务端消息:CMD_LOGIN_RESULT,数据长度:%d\n", _cSock, login->dataLength);}break;case CMD_LOGOUT_RESULT:{recv(_cSock, szRecv + sizeof(DataHeader), header->dataLength - sizeof(DataHeader), 0);LogoutResult* logout = (LogoutResult*)szRecv;printf("收到服务端消息:CMD_LOGOUT_RESULT,数据长度:%d\n", _cSock, logout->dataLength);}break;case CMD_NEW_USER_JOIN:{recv(_cSock, szRecv + sizeof(DataHeader), header->dataLength - sizeof(DataHeader), 0);NewUserJoin* userJoin = (NewUserJoin*)szRecv;printf("收到服务端消息:CMD_NEW_USER_JOIN,数据长度:%d\n", _cSock, userJoin->dataLength);}break;}
}int main()
{//启动Windows socket 2.x环境WORD ver = MAKEWORD(2, 2);WSADATA dat;WSAStartup(ver, &dat);//------------//-- 用Socket API建立简易TCP客户端// 1 建立一个socketSOCKET _sock = socket(AF_INET, SOCK_STREAM, 0);if (INVALID_SOCKET == _sock){printf("错误,建立Socket失败...\n");}else {printf("建立Socket成功...\n");}// 2 连接服务器 connectsockaddr_in _sin = {};_sin.sin_family = AF_INET;_sin.sin_port = htons(4567);_sin.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");int ret  = connect(_sock, (sockaddr*)&_sin, sizeof(sockaddr_in));if (SOCKET_ERROR == ret){printf("错误,连接服务器失败...\n");}else {printf("连接服务器成功...\n");}while (true){fd_set fdReads;FD_ZERO(&fdReads);FD_SET(_sock, &fdReads);timeval t = {0,0};int ret = select(_sock, &fdReads, 0, 0, &t);if (ret < 0){printf("select任务结束1\n");break;}if (FD_ISSET(_sock, &fdReads)){FD_CLR(_sock, &fdReads);if (-1 == processor(_sock)){printf("select任务结束2\n");break;}}printf("空闲时间处理其它业务..\n");Login login;strcpy(login.userName, "lyd");strcpy(login.PassWord, "lyd");send(_sock, (const char*)&login, sizeof(Login), 0);//Sleep(1000);}// 7 关闭套节字closesocketclosesocket(_sock);//清除Windows socket环境WSACleanup();printf("已退出。\n");getchar();return 0;
}

1.3 服务端代码实现

服务端的代码做出微小改动:

#define WIN32_LEAN_AND_MEAN
#define _WINSOCK_DEPRECATED_NO_WARNINGS#include<windows.h>
#include<WinSock2.h>
#include<stdio.h>#include <vector>#pragma comment(lib,"ws2_32.lib")enum CMD
{CMD_LOGIN,CMD_LOGIN_RESULT,CMD_LOGOUT,CMD_LOGOUT_RESULT,CMD_NEW_USER_JOIN,CMD_ERROR
};struct DataHeader
{short dataLength;short cmd;
};//DataPackage
struct Login: public DataHeader
{Login(){dataLength = sizeof(Login);cmd = CMD_LOGIN;}char userName[32];char PassWord[32];
};struct LoginResult : public DataHeader
{LoginResult(){dataLength = sizeof(LoginResult);cmd = CMD_LOGIN_RESULT;result = 0;}int result;
};struct Logout : public DataHeader
{Logout(){dataLength = sizeof(Logout);cmd = CMD_LOGOUT;}char userName[32];
};struct LogoutResult : public DataHeader
{LogoutResult(){dataLength = sizeof(LogoutResult);cmd = CMD_LOGOUT_RESULT;result = 0;}int result;
};struct NewUserJoin : public DataHeader
{NewUserJoin(){dataLength = sizeof(NewUserJoin);cmd = CMD_NEW_USER_JOIN;scok = 0;}int scok;
};std::vector<SOCKET> g_clients;int processor(SOCKET _cSock)
{//缓冲区char szRecv[4096] = {};// 5 接收客户端数据int nLen = recv(_cSock, szRecv, sizeof(DataHeader), 0);DataHeader* header = (DataHeader*)szRecv;if (nLen <= 0){printf("客户端<Socket=%d>已退出,任务结束。\n", _cSock);return -1;}switch (header->cmd){case CMD_LOGIN:{recv(_cSock, szRecv + sizeof(DataHeader), header->dataLength - sizeof(DataHeader), 0);Login* login = (Login*)szRecv;printf("收到客户端<Socket=%d>请求:CMD_LOGIN,数据长度:%d,userName=%s PassWord=%s\n", _cSock, login->dataLength, login->userName, login->PassWord);//忽略判断用户密码是否正确的过程LoginResult ret;send(_cSock, (char*)&ret, sizeof(LoginResult), 0);}break;case CMD_LOGOUT:{recv(_cSock, szRecv + sizeof(DataHeader), header->dataLength - sizeof(DataHeader), 0);Logout* logout = (Logout*)szRecv;printf("收到客户端<Socket=%d>请求:CMD_LOGOUT,数据长度:%d,userName=%s \n", _cSock, logout->dataLength, logout->userName);//忽略判断用户密码是否正确的过程LogoutResult ret;send(_cSock, (char*)&ret, sizeof(ret), 0);}break;default:{DataHeader header = { 0,CMD_ERROR };send(_cSock, (char*)&header, sizeof(header), 0);}break;}
}int main()
{//启动Windows socket 2.x环境WORD ver = MAKEWORD(2, 2);WSADATA dat;WSAStartup(ver, &dat);//------------//-- 用Socket API建立简易TCP服务端// 1 建立一个socket 套接字SOCKET _sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);// 2 bind 绑定用于接受客户端连接的网络端口sockaddr_in _sin = {};_sin.sin_family = AF_INET;_sin.sin_port = htons(4567);//host to net unsigned short_sin.sin_addr.S_un.S_addr = INADDR_ANY;//inet_addr("127.0.0.1");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){//伯克利套接字 BSD socketfd_set fdRead;//描述符(socket) 集合  fd_set fdWrite;fd_set fdExp;//清理集合FD_ZERO(&fdRead);FD_ZERO(&fdWrite);FD_ZERO(&fdExp);//将描述符(socket)加入集合  FD_SET(_sock, &fdRead);FD_SET(_sock, &fdWrite);FD_SET(_sock, &fdExp);for (int n = (int)g_clients.size()-1; n >= 0 ; n--){FD_SET(g_clients[n], &fdRead);}///nfds 是一个整数值 是指fd_set集合中所有描述符(socket)的范围,而不是数量///既是所有文件描述符最大值+1 在Windows中这个参数可以写0timeval t = {0,0};int ret = select(_sock + 1, &fdRead, &fdWrite, &fdExp, &t);if (ret < 0){printf("select任务结束。\n");break;}//判断描述符(socket)是否在集合中  if (FD_ISSET(_sock, &fdRead)){FD_CLR(_sock, &fdRead);// 4 accept 等待接受客户端连接sockaddr_in clientAddr = {};int nAddrLen = sizeof(sockaddr_in);SOCKET _cSock = INVALID_SOCKET;_cSock = accept(_sock, (sockaddr*)&clientAddr, &nAddrLen);if (INVALID_SOCKET == _cSock){printf("错误,接受到无效客户端SOCKET...\n");}else {for (int n = (int)g_clients.size() - 1; n >= 0; n--){NewUserJoin userJoin;send(g_clients[n], (const char*)&userJoin, sizeof(NewUserJoin), 0);}g_clients.push_back(_cSock);printf("新客户端加入:socket = %d,IP = %s \n", (int)_cSock, inet_ntoa(clientAddr.sin_addr));}}for (size_t n = 0; n < fdRead.fd_count; n++){if (-1 == processor(fdRead.fd_array[n])){auto iter = find(g_clients.begin(), g_clients.end(), fdRead.fd_array[n]);if (iter != g_clients.end()){g_clients.erase(iter);}}}printf("空闲时间处理其它业务..\n");}for (size_t n = g_clients.size() - 1; n >= 0; n--){closesocket(g_clients[n]);}// 8 关闭套节字closesocketclosesocket(_sock);//------------//清除Windows socket环境WSACleanup();printf("已退出。\n");getchar();return 0;
}

参考资料:

  1. C++ 百万并发网络通信引擎架构与实现 (服务端、客户端、跨平台) Version 1.0

客户端升级为select模型相关推荐

  1. 服务端升级为select模型处理多客户端

    文章目录 1 服务端升级为select模型处理多客户端 1.1 概述 1.2 服务端实现 1 服务端升级为select模型处理多客户端 1.1 概述 之前我们的设计是服务端与客户端1对1的阻塞模式网络 ...

  2. 糖儿飞教你学C++ Socket网络编程——28. 使用select模型实现一对多通信

    在项目10中,采用多线程技术实现了TCP协议的一对多通信,但如果客户端过多,就会导致服务器端的线程数量膨胀,使得服务器的资源占用过大.能不能让TCP程序在一个线程中同时与多个客户端进行通信呢?答案是可 ...

  3. 朴素、Select、Poll和Epoll网络编程模型实现和分析——Select模型

    在<朴素.Select.Poll和Epoll网络编程模型实现和分析--朴素模型>中我们分析了朴素模型的一个缺陷--一次只能处理一个连接.本文介绍的Select模型则可以解决这个问题.(转载 ...

  4. winsock select模型实现

    select模型的原理是在指定的socket 的数组中轮询的采集是否可写.可读.有异常的信息,然后将可以操做的套接字 保存在指定的数组中. // selectsocket.cpp : 定义控制台应用程 ...

  5. java socket编程 select_windows socket编程select模型使用

    int select( int nfds,            //忽略 fd_ser* readfds,    //指向一个套接字集合,用来检测其可读性 fd_set* writefds,   / ...

  6. WinSock学习笔记3:Select模型

    WinSock学习笔记3:Select模型 unit Unit1; interface uses   Windows, Messages, SysUtils, Variants, Classes, G ...

  7. socket select模型

    由于socket recv()方法是堵塞式的,当多个客户端连接服务器时,其中一个socket的recv调用时,会产生堵塞,使其他连接不能继续. 如果想改变这种一直等下去的焦急状态,可以多线程来实现(不 ...

  8. 转:socket select模型示例

    // selectSocketMode.cpp : Defines the entry point for the console application. // #include "std ...

  9. 基于select模型的TCP服务器

    之前的一篇博文是基于TCP的服务器和客户机程序,今天在这我要实现一个基于select模型的TCP服务器(仅实现了服务器). socket套接字编程提供了很多模型来使服务器高效的接受客户端的请求,sel ...

最新文章

  1. at( ) [ ]
  2. android 获取程序,Android获取桌面应用程序
  3. 因为瘟疫,英国诞生了一个又一个的科学家
  4. 两台邮件服务器共用一个公网地址,两个不同域邮件服务器的互通
  5. 【转】增量式PID控制算法
  6. es中的高效文件读取方式
  7. Python下载prettyloaded的swf
  8. Delphi -- 农历算法
  9. windows实用软件
  10. 《手机音频》参数与选择
  11. python实现信号预加重
  12. 左程云 Java 笔记--暴力递归--动态规划
  13. Trace-导出已有的服务器端跟踪
  14. 关于芯片、CPU的区别的简单理解
  15. 第73课内幕资料详细版 Spark SQL Thrift Server 实战 每天晚上20:00YY频道现场授课频道68917580
  16. 200以后最小质数:
  17. 公司天天开会,是不是浪费生命呢?
  18. mysql和jdbc(韩)
  19. 财管U04 资本成本 教材解读
  20. 在配置SSH免密登录时报错:/usr/bin/ssh-copy-id: ERROR: failed to open ID file '/root/.pub': 没有那个文件或目录

热门文章

  1. 【控制】遗传算法(GA,Genetic Algorithm)及 Matlab 实现 代码详细版
  2. 2.6 动量梯度下降法-深度学习第二课《改善深层神经网络》-Stanford吴恩达教授
  3. 第五章 有限脉冲响应滤波器(ba,我终于懂FIR滤波器了)
  4. android_launcher的源码详细分析和壁纸修改 .
  5. linux opendir readdir closedir 的使用
  6. Android ramdisk.img system.img userdata.img 介绍与使用
  7. 基于FPGA的bubble游戏开发
  8. uboot中添加新型号步骤以及编译方法
  9. 码code | 巧用2种方法,打破20条云开发数据库限制
  10. Java实体类对象修改日志记录