流程图:

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. CORS,jsonp解决跨域问题

    同源和跨域 同源策略(Same origin policy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响.可以说Web是构建在同源策略基础之上 ...

  2. WMPageController设置menuView的左右视图

    效果图如下: 绿色的是自定义的emenuView的rightView哟!!! 代码实现如下: // // CategoryVC.m // JSHui // // Created by Apple on ...

  3. Django之contenttypes组件

    目录 一. 介绍 二. 应用场景 1. 表结构设计的演变 2. GenericForeignKey和GenericRelation 3. 测试 一. 介绍 Django包含一个contenttypes ...

  4. STS中AOP的实现

    1. 在pom.xml中加入aop依赖: <groupId>org.springframework.boot</groupId> <artifactId>sprin ...

  5. Tei-Wei Kuo

    一. A Commitment-based Management Strategy for the Performance and Reliability Enhancement of Flash-m ...

  6. visio给任意图形进行填充

    使用visio给图形填充颜色的条件:图形必须是封闭的!!! 但是很多人不明白:我明明画的图形是封闭的啊!可是无法填充颜色!这是因为,你画了一个看上去是封闭的图形,但是VISIO认为你画的不是封闭图形! ...

  7. 18.linux日志收集数据到hdfs上面

    先创建一个目录 在这个job目录下创建upload.sh文件 [hadoop@node1 ~]$ pwd /home/hadoop [hadoop@node1 ~]$ mkdir job [hadoo ...

  8. svn服务器端的更新操作

    svn up需要在 非svn库中进行操作 ,一般在网站www目录下操作

  9. Min swaps to sort array

    Given an array with distinct numbers, return an integer indicating the minimum number of swap operat ...

  10. SQLite基础-8.子句(二)

    目录 SQLite子句(二) 1. GROUP BY子句 2. HAVING子句 3. LIMIT 子句 4. IF EXISTS 和 IF NOT EXISTS 子句 SQLite子句(二) 1. ...