流程图:

select会定时的查询socket查询有没有新的网络连接,有没有新的数据需要读,有没有新的请求需要处理,一旦有新的数据需要处理,select就会返回,然后我们就可以处理相应的数据,select一旦没有数据,我们就可以处理其他数据,使用select可以进行阻塞掉网络数据,还可以将服务端解放出来,处理其他事情。

服务端代码:

#include<WinSock2.h>
#include<Windows.h>
#include<vector>
#include<stdio.h>
#include<iostream> #pragma comment(lib,"ws2_32.lib") enum CMD { CMD_Login, CMD_Login_Result, CMD_Logout, CMD_Logout_Result, CMD_ERROR }; //包头
struct DataHeader
{
short dataLength;
short cmd;
};
//包体
struct Login:public DataHeader
{
Login()
{
dataLength = sizeof(Login);
cmd = CMD_Login;
}
char username[];
char password[];
}; struct LoginResult :public DataHeader
{
LoginResult()
{
dataLength = sizeof(LoginResult);
cmd = CMD_Login_Result;
result = ;
}
int result;
}; struct Logout :public DataHeader
{
Logout()
{
dataLength = sizeof(Logout);
cmd = CMD_Logout;
}
char username[];
}; struct LogoutResult :public DataHeader
{
LogoutResult()
{
dataLength = sizeof(LogoutResult);
cmd = CMD_Logout_Result;
result = ;
}
int result;
}; std::vector<SOCKET> g_client; int process_solve(SOCKET _cSOCK)
{
//增加一个缓冲区
char szRecv[] = {};
//5.接收客户端新数据
int nLen = recv(_cSOCK, szRecv, sizeof(DataHeader), );
DataHeader *header = (DataHeader*)szRecv; if (nLen <= )
{
printf("客户端已退出!任务结束!");
return -;
}
switch (header->cmd){
case CMD_Login:
{
recv(_cSOCK, szRecv + sizeof(DataHeader), header->dataLength - sizeof(DataHeader), );
Login *login = (Login*)szRecv;
printf("收到命令:CMD_Login,数据长度:%d\nUserName:%s\nPassWord:%s\n", login->dataLength, login->username, login->password);
//忽略判断用户密码是否正确的过程
LoginResult ret;
send(_cSOCK, (char *)&ret, sizeof(LoginResult), ); //再发消息体 }
case CMD_Logout:
{ recv(_cSOCK, szRecv + sizeof(DataHeader), header->dataLength - sizeof(DataHeader), );
Logout* logout = (Logout*)szRecv;
printf("收到命令:CMD_Logout,数据长度:%d\nUserName:%s\n", logout->dataLength, logout->username); //忽略判断用户密码是否正确的过程
LogoutResult let;
send(_cSOCK, (char *)&let, sizeof(let), ); //再发消息体
}
break;
default:
{
DataHeader header = { };
send(_cSOCK, (char *)&header.cmd, sizeof(header), );
} break;
}
} int main()
{ WORD ver = MAKEWORD(, );
WSADATA dat;
//WinSocket启动
WSAStartup(ver, &dat); //1、建立一个socket
SOCKET _sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); //AF_INET创建一个IPV4的套接字,SOCK_STREAM面向数据流的,IPPROTO_TCP TCP
if (INVALID_SOCKET == _sock)
{
printf("ERROR:建立失败!\n");
}
//2.绑定
sockaddr_in _sin = {}; //创建网络地址
_sin.sin_family = AF_INET;
_sin.sin_port = htons(); //Host to Network Short
_sin.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); // IP地址
if (bind(_sock, (sockaddr *)&_sin, sizeof(_sin)) == SOCKET_ERROR)
{
printf("ERROR:绑定失败!\n");
}
else
{
printf("服务器端绑定成功......\n");
}
//3.监听网络端口
if (listen(_sock, ) == SOCKET_ERROR)//第二个参数为最大等待多少人可以同时连接
{
printf("ERROR:监听失败!\n");
}
else
{
printf("服务器端监听成功......\n");
} while ()
{
//伯克利 socket
fd_set fd_Read;
fd_set fd_Write;
fd_set fd_Exp; FD_ZERO(&fd_Read);//FD_ZERO 清空集合里的数据
FD_ZERO(&fd_Write);
FD_ZERO(&fd_Exp); FD_SET(_sock, &fd_Read);//FD_SET 可以进行操作的宏
FD_SET(_sock, &fd_Write);
FD_SET(_sock, &fd_Exp); for (int n = g_client.size() - ; n >= ; n--)
{
FD_SET(g_client[n], &fd_Read);
} /*
select(
_In_ int nfds,
_Inout_opt_ fd_set FAR * readfds,
_Inout_opt_ fd_set FAR * writefds,
_Inout_opt_ fd_set FAR * exceptfds,
_In_opt_ const struct timeval FAR * timeout
);
*/ //nfds是一个整数值,是指fd_set集合所有的描述符(select里的第一个参数)的范围(而不是数量)
//既是所有文件描述符最大值+1
int ret = select(_sock + , &fd_Read, &fd_Write, &fd_Exp, NULL);
if (ret < )
{
printf("select任务结束!\n");
break;
}
if (FD_ISSET(_sock, &fd_Read))
{
FD_CLR(_sock, &fd_Read);
//4.等待接收客户端连接
sockaddr_in clientAddr = {};
int nAddrLen = sizeof(sockaddr_in);
SOCKET _cSOCK = INVALID_SOCKET; _cSOCK = accept(_sock, (sockaddr *)&clientAddr, &nAddrLen);
if (_cSOCK == INVALID_SOCKET)
{
printf("ERROR:无效客户端SOCKET!\n");
}
g_client.push_back(_cSOCK);
printf("新客户端加入:Socket=%d,IP = %s\n", (int)_cSOCK, inet_ntoa(clientAddr.sin_addr));//inet_ntoa(clientAddr.sin_addr)将接收到的IP地址转化为字符串 } for (int n = ; n < fd_Read.fd_count; n++)
{
if (process_solve(fd_Read.fd_array[n]) == -)
{
auto iter = find(g_client.begin(), g_client.end(), process_solve(fd_Read.fd_array[n]));
if (iter != g_client.end())
{
g_client.erase(iter);
}
}
}
} for (int n = g_client.size(); n >= ; n--)
{
//8.关闭自身的socket
closesocket(g_client[n]);
} //8.关闭自身的socket
closesocket(_sock); //WinSocket关闭
WSACleanup(); system("pause");
return ;
}

客户端代码:

#include<WinSock2.h>
#include<Windows.h>
#include<stdio.h> #pragma comment(lib,"ws2_32.lib") enum CMD { CMD_Login, CMD_Login_Result, CMD_Logout, CMD_Logout_Result, CMD_ERROR }; //包头
struct DataHeader
{
short dataLength;
short cmd;
};
//包体
struct Login :public DataHeader
{
Login()
{
dataLength = sizeof(Login);
cmd = CMD_Login;
}
char username[];
char password[];
}; struct LoginResult :public DataHeader
{
LoginResult()
{
dataLength = sizeof(LoginResult);
cmd = CMD_Login_Result;
result = ;
}
int result;
}; struct Logout :public DataHeader
{
Logout()
{
dataLength = sizeof(Logout);
cmd = CMD_Logout;
}
char username[];
}; struct LogoutResult :public DataHeader
{
LogoutResult()
{
dataLength = sizeof(LogoutResult);
cmd = CMD_Logout_Result;
result = ;
}
int result;
}; int main()
{
WORD ver = MAKEWORD(, );
WSADATA dat;
WSAStartup(ver, &dat); //1.建立一个socket
SOCKET _sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (INVALID_SOCKET == _sock)
{
printf("ERROR:建立失败!\n");
}
else{
printf("客户端绑定成功......\n");
}
//2.连接服务器
sockaddr_in _sin = {}; //创建网络地址
_sin.sin_family = AF_INET;
_sin.sin_port = htons(); //Host to Network Short
_sin.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");//inet_addr("127.0.0.1"); // IP地址
int ret = connect(_sock, (sockaddr *)&_sin, sizeof(sockaddr_in));
if (SOCKET_ERROR == ret)
{
printf("ERROR:连接失败!\n");
}
else
{
printf("客户端连接成功......\n");
} while (true)
{
//3.输入请求命令
char cmdBuff[] = {};
scanf("%s", cmdBuff);
//4.处理请求命令
if ( == strcmp(cmdBuff, "exit"))
{
printf("收到exit命令,已退出1");
break;
}
else if ( == strcmp(cmdBuff, "login")){
Login login;
strcpy(login.username, "sutaoyu01");
strcpy(login.password, "sutaoyu01"); //5.向服务器发送请求命令
send(_sock, (const char*)&login, sizeof(login), ); //接收服务器返回的数据
LoginResult resulrtRet = {};
recv(_sock, (char*)&resulrtRet, sizeof(resulrtRet), );
printf("LoginResult:%d\n", resulrtRet.result);
}
else if ( == strcmp(cmdBuff, "logout")){
Logout logout;
strcpy(logout.username, "sutaoyu01");
//5.向服务器发送请求命令
send(_sock, (const char*)&logout, sizeof(logout), );
//接收服务器返回的数据
LogoutResult resultRet = {};
recv(_sock, (char*)&resultRet, sizeof(resultRet), );
printf("LogoutResult:%d\n", resultRet.result);
}
else{
printf("不支持的命令,请重新输入!");
}
} //7.关闭套接字
closesocket(_sock); //WinSocket启动
WSAStartup(ver, &dat); //WinSocket关闭
WSACleanup();
printf("已退出!");
getchar();
return ;
}

服务器端升级为select模型处理多客户端的更多相关文章

  1. 基于select模型的udp客户端实现超时机制

    参考:http://www.cnblogs.com/chenshuyi/p/3539949.html 多路选择I/O — select模型 其思想在于使用一个集合,该集合中包含需要进行读写的fd,通过 ...

  2. 基于Select模型的Windows TCP服务端和客户端程序示例

    最近跟着刘远东老师的<C++百万并发网络通信引擎架构与实现(服务端.客户端.跨平台)>,Bilibili视频地址为C++百万并发网络通信引擎架构与实现(服务端.客户端.跨平台),重新复习下 ...

  3. socket select模型

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

  4. select模型

    在Windows中所有的socket函数都是阻塞类型的,也就是说只有网络中有特定的事件发生时才会返回,在没有发生事件时会一直等待,虽说我们将它们设置为非阻塞状态,但是在对于服务器段而言,肯定会一直等待 ...

  5. 基于Select模型通信程序的编写,编译和执行

    任务目标 编写Win32程序模拟实现基于Select模型的两台计算机之间的通信,要求编程实现服务器端与客户端之间双向数据传递.客户端向服务器端发送"计算从1到100的奇数和",服务 ...

  6. windows socket编程select模型使用

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

  7. socket编程的select模型

    在掌握了socket相关的一些函数后,套接字编程还是比较简单的,日常工作中碰到很多的问题就是客户端/服务器模型中,如何让服务端在同一时间高效的处理多个客户端的连接,我们的处理办法可能会是在服务端不停的 ...

  8. linux下多路复用模型之Select模型

    Linux关于并发网络分为Apache模型(Process per Connection (进程连接) ) 和TPC , 还有select模型,以及poll模型(一般是Epoll模型) Select模 ...

  9. Winsock IO模型之select模型

    之所以称其为select模型是因为它主要是使用select函数来管理I/O的.这个模型的设计源于UNIX系统,目的是允许那些想要避免在套接字调用上阻塞的应用程序有能力管理多个套接字. int sele ...

随机推荐

  1. 最近忙科研立项 & 对博客的优化

    最近一直在忙科研立项.... 立项书 & 答辩 ... 接下来,将对博客进行优化... (1) 依据书来学习的[需要大量截图],将用微软的 OneNote 写笔记,然后打包成pdf,上传到我的 ...

  2. kubeadm安装集群系列-5.其他操作

    常用的一些操作记录 imagePullSecrets kubectl -n [namespace] create secret docker-registry regsecret --docker-s ...

  3. C#ModBus Tcp

    C#ModBus Tcp 报文解析   上一篇博客已经完成 C#ModBus Tcp Master的实现 本篇主要对不同的功能码所发出的报文进行解析(包括请求报文及响应报文) 读操作 功能码 0x01 ...

  4. 页面元素定位-----android真机

    一.移动终端开发者选项启用USB调试模式 二.USB线连接移动终端,CTRL+R启动DOS窗口,输入:adb devices,出现以下信息,说明连接成功 三.启动appium,配置appium相关信息 ...

  5. 2019牛客暑期多校训练营(第六场)-E Androgynos (构造题)

    题目链接:https://ac.nowcoder.com/acm/contest/886/E 题意:给出n,求能否构造一个含n个顶点的简单无向图,且它的补图与它同构. 思路:首先要满足与其补图同构,同 ...

  6. 从ftp服务器进行批量下载,处理文件名保存时重名的问题,更改重名文件名方式为给后面加1、2、3等数字,保持后缀不变

    公司最近有一个从ftp批量下载文件的需求,但是文件名重复总会报错 没办法,自己下班后写了一个小算法 仿照桶排序的原理,实现了这个小功能,直接上代码: String[] test = {"ha ...

  7. Redis(1.14)Redis日常管理与维护

    [1]持久化 如果不做持久化,用replication去保证可用性,另外最后可以通过引用从数据库同步最新数据. 因此注释掉所有的持久化策略,添加一条带空字符串参数的save指令,也能移除之前所有配置的 ...

  8. Hadoop集群搭建-03编译安装hadoop

    Hadoop集群搭建-05安装配置YARN Hadoop集群搭建-04安装配置HDFS  Hadoop集群搭建-03编译安装hadoop Hadoop集群搭建-02安装配置Zookeeper Hado ...

  9. PAT B1026.程序运行时间

    AC代码 #include <cstdio> #define CLK_TCK 100 int main() { int C1, C2, C3; scanf("%d%d" ...

  10. selenium的使用与chromedriver的下载配置

    Selenium是一个web自动化测试工具,最初是为网站自动化测试而开发的,Selenium可以直接运行在浏览器上,它支持所有主流的浏览器,可以接受指令,让浏览器自动加载页面,获得需要的数据,甚至页面 ...