#include "main.h"
VOID Server_write_error()
{
}
/*1.打开网络库
* 2.校验网络库版本
* 3.创建SOCKET
* 4.绑定IP地址和端口
* 5.开始监听
* 6.创建客户端socket/接受链接
* 7.与客户端收发消息
* 8.(6.7)两步的函数accept,send,recv 有堵塞,可以用select解决,这种函数可以处理小型网络
*/
int create(const char* IpAdress)
{
WORD wVersionRequested;
WSADATA wsaData;
int err;
/* 使用Windef.h中声明的MAKEWORD(低字节、高字节)宏 */
wVersionRequested = MAKEWORD(2, 2);
/*启用网络链接库,调用的封装库命令*/
err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0) {
/* Tell the user that we could not find a usable */
/* Winsock DLL. */
printf("WSAStartup failed with error: %d\n", err);
return -1;
}
/*确认WinSock DLL支持2.2*/
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
/* Tell the user that we could not find a usable */
/* WinSock DLL. */
printf("Could not find a usable version of Winsock.dll\n");
//清理网络库
WSACleanup();
return -1;
}
//创建套接字。 创建网络类型 tcp或者upd
SOCKET socketServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (INVALID_SOCKET == socketServer)
{
string ret = to_string(WSAGetLastError());
MessageBoxA(0, "error_socket",ret.c_str(), 0);
//清理网络库
WSACleanup();
return -1;
}
//设置sockaddr结构
sockaddr_in saServer;
saServer.sin_family = AF_INET;
saServer.sin_addr.s_addr = INADDR_ANY;
saServer.sin_port = htons(9999);
// 绑定本机(服务器)IP和端口
//sockaddr结构中的信息
if (SOCKET_ERROR == bind(socketServer, (SOCKADDR*)&saServer, sizeof(saServer)))
{
string ret = to_string(WSAGetLastError());
MessageBoxA(0, "error_bind", ret.c_str(), 0);
//释放stocket
closesocket(socketServer);
//清理网络库
WSACleanup();
return -1;
}
/*监听本机(服务器)的套接字*/
if (SOCKET_ERROR == listen(socketServer, SOMAXCONN))
{
string ret = to_string(WSAGetLastError());
MessageBoxA(0, "error_listen", ret.c_str(), 0);
//释放stocket
closesocket(socketServer);
//清理网络库
WSACleanup();
return -1;
}
fd_es_set setSockets;
memset(&setSockets,0,sizeof(setSockets));
/*创建一个套接字 事件*/
HANDLE eventSerevr = WSACreateEvent();
if (WSA_INVALID_EVENT == eventSerevr)
{
string ret = to_string(WSAGetLastError());
MessageBoxA(0, "error_WSACreateEventServer", ret.c_str(), 0);
//释放stocket
closesocket(socketServer);
//清理网络库
WSACleanup();
return -1;
}
/*将服务器socket和事件绑定*/
if (SOCKET_ERROR == WSAEventSelect(socketServer, eventSerevr, FD_ACCEPT))
{
string ret = to_string(WSAGetLastError());
MessageBoxA(0, "error_WSAEventSelecttServer", ret.c_str(), 0);
//释放stocket
closesocket(socketServer);
//清理网络库
WSACleanup();
return -1;
}
/*将服务器socket和事件对象句柄写进结构体*/
setSockets.sockall[setSockets.count] = socketServer;
setSockets.eventall[setSockets.count] = eventSerevr;
setSockets.count++;
while (true)
{
/*为了防止一个用户死循环访问服务器,造成事件一直处理同一个客户端,进行优化,循环遍历单个数组询问是否有事件信号*/
/*同时WSAWaitForMultipleEvents函数参数一每次只能处理64个事件数组,而我们改成每次处理1个,循环处理变相能处理无穷多的事件了*/
DWORD RET = 0;
for (DWORD Index = 0; Index <setSockets.count; Index++)
{
/*等待事件:有事件产生后返回对应事件的下标*/
RET = WSAWaitForMultipleEvents(1, &setSockets.eventall[Index], false, 0, false);
/*返回错误*/
if (WSA_WAIT_FAILED == RET)
{
string ret = to_string(WSAGetLastError());
MessageBoxA(0, "error_WSAWaitForMultipleEvents", ret.c_str(), 0);
continue;
}
/*等待超时检测*/
if (WSA_WAIT_TIMEOUT == RET)
{
continue;
}
// DWORD Index = RET - WSA_WAIT_EVENT_0;
/*枚举事件:获取事件类型,并重置其的信号*/
WSANETWORKEVENTS GetlpNetworkEvents;
if (SOCKET_ERROR == WSAEnumNetworkEvents(setSockets.sockall[Index], setSockets.eventall[Index], &GetlpNetworkEvents))
{
string ret = to_string(WSAGetLastError());
MessageBoxA(0, "error_WSAEnumNetworkEvents", ret.c_str(), 0);
continue;
}
/******** FD_ACCEPT 可能是组合指令,故需要 & 判断*/
/*客户端connect会依次触发FD_ACCEPT 和 FD_WRITE*/
if (GetlpNetworkEvents.lNetworkEvents & FD_ACCEPT)
{
if (0 == GetlpNetworkEvents.iErrorCode[FD_ACCEPT_BIT])
{//正常处理
sockaddr_in clientMsg = { 0 };
int clientMsg_size = sizeof(clientMsg);
/*得到客户端信息并返回客户端socket*/
SOCKET socketClient = accept(setSockets.sockall[Index], (sockaddr*)&clientMsg, &clientMsg_size);
printf("%d.%d.%d.%d.%d \n", clientMsg.sin_addr.S_un.S_un_b.s_b1,
clientMsg.sin_addr.S_un.S_un_b.s_b2,
clientMsg.sin_addr.S_un.S_un_b.s_b3,
clientMsg.sin_addr.S_un.S_un_b.s_b4,
clientMsg.sin_port);
if (INVALID_SOCKET == socketClient)
continue;
/*创建一个套接字 事件*/
HANDLE eventClient = WSACreateEvent();
if (WSA_INVALID_EVENT == eventClient)
{
//释放stocket
closesocket(socketClient);
continue;
}
/*将客户端socket和事件绑定*/
if (SOCKET_ERROR == WSAEventSelect(socketClient, eventClient, FD_READ | FD_WRITE | FD_CLOSE))
{
//释放stocket
closesocket(socketClient);
//关闭事件对象
WSACloseEvent(eventClient);
continue;
}
/*完成上面步骤后将这个客户端数据放入结构体*/
setSockets.sockall[setSockets.count] = socketClient;
setSockets.eventall[setSockets.count] = eventClient;
setSockets.count++;
}
else
{
continue;
}
}
/*********FD_WRITE 可能是组合指令,故需要 & 判断*/
/*客户端connect会依次触发FD_ACCEPT 和 FD_WRITE*/
if (GetlpNetworkEvents.lNetworkEvents & FD_WRITE)
{
if (0 == GetlpNetworkEvents.iErrorCode[FD_WRITE_BIT])
{//正常处理
//accept会触发一次,一般用于链接服务器后初始化
char sendmsg[] = "connect_success";
if (SOCKET_ERROR == send(setSockets.sockall[Index], sendmsg, strlen(sendmsg), 0))
{
continue;
}
}
else
{
continue;
}
}
/*********FD_READ 可能是组合指令,故需要 & 判断*/
if (GetlpNetworkEvents.lNetworkEvents & FD_READ)
{
if (0 == GetlpNetworkEvents.iErrorCode[FD_READ_BIT])
{//正常处理
char recvmsg[1024] = { 0 };
if (SOCKET_ERROR == recv(setSockets.sockall[Index], recvmsg, sizeof(recvmsg), 0))
{
continue;
}
printf("read:%s\n", recvmsg);
char sendmsg[] = "recv_success";
if (SOCKET_ERROR == send(setSockets.sockall[Index], sendmsg, strlen(sendmsg), 0))
{
continue;
}
}
else
{
continue;
}
}
/*********FD_CLOSE 可能是组合指令,故需要 & 判断*/
if (GetlpNetworkEvents.lNetworkEvents & FD_CLOSE)
{
{//正常处理
//sockaddr_in clientMsg = { 0 };
//int clientMsg_size = sizeof(clientMsg);
//getsockname(setSockets.sockall[Index], (sockaddr*)&clientMsg, &clientMsg_size);
//printf("client下线:%d.%d.%d.%d.%d \n", clientMsg.sin_addr.S_un.S_un_b.s_b1,
// clientMsg.sin_addr.S_un.S_un_b.s_b2,
// clientMsg.sin_addr.S_un.S_un_b.s_b3,
// clientMsg.sin_addr.S_un.S_un_b.s_b4,
// clientMsg.sin_port);
printf("client下线");
//释放stocket
closesocket(setSockets.sockall[Index]);
//关闭事件对象
WSACloseEvent(setSockets.eventall[Index]);
/*将这个事件和socket移除数组,取巧方法:由于事件是无序的,把数组最后一个数据放进移除的数据的位置,并将数组大小-1*/
setSockets.sockall[Index] = setSockets.sockall[setSockets.count - 1];//数组从0开始,-1才是正确位置
setSockets.eventall[Index] = setSockets.eventall[setSockets.count - 1];//数组从0开始,-1才是正确位置
setSockets.count--;
}
}
}
}
system("pause");
/*释放整个结构体,可能有些事件和socket已经被释放过了,不影响*/
for (int i = 0; i < setSockets.count; i++)
{
//释放stocket
closesocket(setSockets.sockall[i]);
//关闭事件对象
WSACloseEvent(setSockets.eventall[i]);
}
//清理网络库
WSACleanup();
}
int main()
{
// create("127.0.0.1");
return 0;
}
利用windows的网络事件函数,系统将自动用异步的方式帮我们查看socket消息,接收到消息后,我们将无序的查看接收到的消息(比如:我们有事件[5],先后接收到45321送来的消息,事件只能接收到12345发来了消息,并不能知道先后顺序,故只能按12345的顺序处理消息),而消息机制本质和事件差不多,区别就是消息机制由系统的消息队列记录先后顺序,会按45321的顺序处理,处理更合理