#include "main.h"
fd_win_set setSockets;
VOID Server_write_error()
{
}
int cteateserver(HWND hwnd)
{
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;
}
//将socket和消息绑定 并投递给消息队列管理,和事件类似
WSAAsyncSelect(socketServer, hwnd, WSAAsyncSelectMsg, FD_ACCEPT);
/*将服务器socket句柄写进结构体*/
setSockets.sockall[setSockets.count] = socketServer;
setSockets.count++;
}
int AsyncSelectMsg(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC DC = GetDC(hWnd);
//参数三存放的是响应的socket句柄
SOCKET socket = (SOCKET)wParam;
//参数四可以获取句柄信息
//if (0 != HIWORD(lParam))//高位保存错误码 ,退出也属于错误码
// return -1;
switch (LOWORD(lParam))
{
case FD_ACCEPT:
{
sockaddr_in clientMsg = { 0 };
int clientMsg_size = sizeof(clientMsg);
/*得到客户端信息并返回客户端socket*/
SOCKET socketClient = accept(socket, (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);
TextOutA(DC,0,0,"ACCEPT",strlen("ACCEPT"));
MessageBox(0,"ACCEPT","",0);
if (INVALID_SOCKET == socketClient)
break;
//将socket和消息绑定 并投递给消息队列管理,和事件类似
if (SOCKET_ERROR == WSAAsyncSelect(socketClient, hWnd, WSAAsyncSelectMsg, FD_WRITE | FD_READ | FD_CLOSE))
{
//释放stocket
closesocket(socketClient);
break;
}
/*将客户端socket句柄写进结构体*/
setSockets.sockall[setSockets.count] = socketClient;
setSockets.count++;
}
break;
case FD_WRITE:
{
//accept会触发一次,一般用于链接服务器后初始化
char sendmsg[] = "connect_success";
if (SOCKET_ERROR == send(socket, sendmsg, strlen(sendmsg), 0))
{
break;
}
}
break;
case FD_READ:
{
char recvmsg[1024] = { 0 };
if (SOCKET_ERROR == recv(socket, recvmsg, sizeof(recvmsg), 0))
{
break;
}
printf("read:%s\n", recvmsg);
char sendmsg[] = "recv_success";
if (SOCKET_ERROR == send(socket, sendmsg, strlen(sendmsg), 0))
{
break;
}
}
break;
case FD_CLOSE:
{
MessageBox(0, "FD_CLOSE", "", 0);
//释放stocket
closesocket(socket);
//关闭事件对象
WSAAsyncSelect(socket, hWnd, 0, 0);
/*将这个事件和socket移除数组,取巧方法:由于事件是无序的,把数组最后一个数据放进移除的数据的位置,并将数组大小-1*/
for (UINT i = 0; i < setSockets.count; i++)
{
if (socket == setSockets.sockall[i])
{
setSockets.sockall[i] = setSockets.sockall[setSockets.count - 1];//数组从0开始,-1才是正确位置
setSockets.count--;
break;
}
}
}
break;
}
return 1;
}
/*窗口消息回调,每次只处理一个消息*/
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WSAAsyncSelectMsg:
AsyncSelectMsg(hWnd, message, wParam, lParam);
break;
case WM_CREATE://窗口初始化,只执行一次
break;
case WM_DESTROY://关闭释放窗口
PostQuitMessage(0);//退出
break;
}
//处理我们没有处理的消息
return DefWindowProc(hWnd, message, wParam, lParam);
}
int APIENTRY WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPTSTR lpCmdLine, _In_ int nCmdShow)
{
// 注册窗口结构体
WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_VREDRAW| CS_HREDRAW, WindowProc, 0L, 0L, GetModuleHandle(NULL), NULL, NULL, NULL, NULL, TEXT("server"), NULL };
::RegisterClassEx(&wc);
//创建窗口
HWND windowshWnd = CreateWindowEx(WS_EX_TOPMOST , wc.lpszClassName, TEXT("server"), WS_OVERLAPPEDWINDOW,0,0, 500,500, NULL, NULL, wc.hInstance, NULL);
//显示窗口
ShowWindow(windowshWnd, SW_SHOWNORMAL);//SW_SHOWNORMAL 或者 SW_SHOW
//更新窗口
UpdateWindow(windowshWnd);
//创建服务器
cteateserver(windowshWnd);
// 主消息循环:
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
//将虚拟密钥消息转换为字符消息,字符消息将发布到调用线程的消息队列
TranslateMessage(&msg);
//将消息发送到窗口回调处理
DispatchMessage(&msg);
}
// /*释放整个结构体,可能有些事件和socket已经被释放过了,不影响*/
for (UINT i = 0; i < setSockets.count; i++)
{
//取消窗口消息和socket的绑定
WSAAsyncSelect(setSockets.sockall[i], windowshWnd, 0, 0);
//释放stocket
closesocket(setSockets.sockall[i]);
}
//清理网络库
WSACleanup();
return (int)msg.wParam;
}
#pragma once
#define _WINSOCK_DEPRECATED_NO_WARNINGS
//#define FD_SETSIZE 1024
#include
#include
#include
#include
#pragma comment(lib,"ws2_32.lib")
using namespace std;
//自定义的消息值不能和系统消息冲突
#define WSAAsyncSelectMsg WM_USER +1
struct fd_es_set
{
UINT count;
SOCKET sockall[1024];
HANDLE eventall[1024];
};
struct fd_win_set
{
UINT count;
SOCKET sockall[1024];
};
利用windows的消息队列函数,对比事件消息,好处是消息接收更合理,按客户端发送消息的顺序会依次处理,但是由于send accept recv这些函数天生存在缺陷,是同步处理的,这些函数必须处理完一个消息才能往下处理另一个消息,会造成堵塞,故windows设计了了新的套接字函数WSAsocket AccpetEx WSAaccept WSAsend,这些函数采用异步方式,解决了堵塞