本例采用Socket实现局域网通信。
开发环境:XP+VS2005+MFC
关键API:WSACreateEvent、WSAEventSelect、WSACloseEvent、WSAWaitForMultipleEvents、WSAEnumNetworkEvents。
参考:http://blog.youkuaiyun.com/mlite/article/details/699340。
示例程序:Win32程序,lib中依赖ws2_32.lib。简单模型,服务端与客户端一对一通话。
服务端代码
IMEventServer.cpp
// IMEventServer.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <WinSock2.h>
#include <stdio.h>
#include <stdlib.h>
const UINT DEFAULT_PORT = 5150;
const UINT DEFAULT_BUFFER = 4096;
const UINT LISTEN_LEN = 8;
//处理执行线程
void ExecThreadMng(SOCKET sAccept);
//Socket监听客户端消息线程
DWORD WINAPI SocketClientThread(LPVOID lpParam);
//服务端定时发送消息线程
DWORD WINAPI SocketSendThread(LPVOID lpParam);
WSAEVENT g_nAcceptEvent;
bool g_bAcceptClose = false;
int _tmain(int argc, _TCHAR* argv[])
{
WSADATA wsd;
if (WSAStartup(MAKEWORD(2,2), &wsd) != 0)
{
printf("failed to load winsock!\n");
return 1;
}
//Create our listening socket
SOCKET sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
if (sListen == SOCKET_ERROR)
{
printf("socket() failed:%d\n", WSAGetLastError());
return 1;
}
//Select the local interface, and bind to it
struct sockaddr_in local;
//INADDR_ANY是所有网卡地址
local.sin_addr.s_addr = htonl(INADDR_ANY);
local.sin_family = AF_INET;
local.sin_port = htons(DEFAULT_PORT);
//绑定监听网卡地址和端口
if (bind(sListen, (struct sockaddr*)&local, sizeof(local)) == SOCKET_ERROR)
{
printf("bind() failed:%d\n", WSAGetLastError());
return 1;
}
//开始监听客户端
listen(sListen, LISTEN_LEN);
printf("listening on port %d\n", DEFAULT_PORT);
//简单模型,只接受一个客户端
//进行一对一通话
SOCKET sAccept = accept(sListen, NULL, NULL);
printf("socket %d connected\n", sAccept);
g_nAcceptEvent = WSACreateEvent();
WSAEventSelect(sAccept, g_nAcceptEvent, FD_READ | FD_CLOSE);
//创建执行线程
ExecThreadMng(sAccept);
closesocket(sListen);
WSACleanup();
system("pause");
return 0;
}
void ExecThreadMng(SOCKET sAccept)
{
HANDLE hClientThread = CreateThread(NULL, 0, SocketClientThread,
(LPVOID)sAccept, 0, NULL);
HANDLE hSendThread = CreateThread(NULL, 0, SocketSendThread,
(LPVOID)sAccept, 0, NULL);
if ( NULL == hClientThread
|| NULL == hSendThread)
{
printf("CreateThread() failed:%d\n", GetLastError());
return;
}
HANDLE arrayWaitHandle[2];
arrayWaitHandle[0] = hClientThread;
arrayWaitHandle[1] = hSendThread;
//等待线程执行完毕
WaitForMultipleObjects(2, arrayWaitHandle, TRUE, INFINITE);
CloseHandle(hClientThread);
CloseHandle(hSendThread);
}
//Socket监听客户端消息线程
DWORD WINAPI SocketClientThread(LPVOID lpParam)
{
SOCKET sAccept = (SOCKET)lpParam;
while (true)
{
WSAEVENT arrayEvent[1];
arrayEvent[0] = g_nAcceptEvent;
DWORD nEventIndex = WSAWaitForMultipleEvents(1, arrayEvent,
FALSE, WSA_INFINITE, FALSE);
DWORD nArrayIndex = nEventIndex - WSA_WAIT_EVENT_0;
WSANETWORKEVENTS networkEvents;
WSAEnumNetworkEvents(sAccept, g_nAcceptEvent, &networkEvents);
if (networkEvents.lNetworkEvents & FD_READ)
{
//获取客户端消息
int nErrorCode = networkEvents.iErrorCode[FD_READ_BIT];
if (nErrorCode != 0)
{
printf("FD_READ failed with error %d\n", nErrorCode);
continue;
}
//Read data from the socket
char szBuff[DEFAULT_BUFFER];
int ret = recv(sAccept, szBuff, DEFAULT_BUFFER, 0);
if (SOCKET_ERROR == ret)
{
printf("recv() failed\n");
continue;
}
szBuff[ret] = '\0';
printf("recv from client:%s\n", szBuff);
}
else if (networkEvents.lNetworkEvents & FD_CLOSE)
{
//客户端连接关闭
g_bAcceptClose = true;
int nErrorCode = networkEvents.iErrorCode[FD_CLOSE_BIT];
if (nErrorCode != 0)
{
printf("FD_CLOSE failed with error %d\n", nErrorCode);
}
else
{
printf("FD_CLOSE success\n");
}
closesocket(sAccept);
WSACloseEvent(g_nAcceptEvent);
break;
}
}
return 1;
}
//服务端定时呼叫客户端
DWORD WINAPI SocketSendThread(LPVOID lpParam)
{
SOCKET sAccept = (SOCKET)lpParam;
char szMessage[64] = {0};
strcpy_s(szMessage, "server timing call");
while (true)
{
Sleep(3000);
if (g_bAcceptClose)
{
//连接断开,则退出
break;
}
int nRet = send(sAccept, szMessage, (int)strlen(szMessage), 0);
if (nRet == SOCKET_ERROR)
{
printf("send() failed:%d\n", WSAGetLastError());
}
else
{
printf("Server send:%s\n", szMessage);
}
}
return 1;
}
客户端代码
IMEventClient.cpp
// IMEventClient.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <WinSock2.h>
#include <stdio.h>
#include <stdlib.h>
#define DEFAULT_PORT 5150
#define DEFAULT_BUFFER 4096
//处理执行线程
void ExecThreadMng(SOCKET sClient);
//客户端接受消息线程
DWORD WINAPI SocketRecvThread(LPVOID lpParam);
//客户端发送消息线程
DWORD WINAPI SocketSendThread(LPVOID lpParam);
//客户端响应事件
WSAEVENT g_nClientEvent;
bool g_bClientClose = false;
int _tmain(int argc, _TCHAR* argv[])
{
WSADATA wsd;
if (WSAStartup(MAKEWORD(2,2), &wsd) != 0)
{
printf("failed to load Winsock!\n");
return 1;
}
//Create the socket, and attempt to connect to the server
SOCKET sClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sClient == INVALID_SOCKET)
{
printf("socket() failed:%d\n", WSAGetLastError());
return 1;
}
struct sockaddr_in server;
server.sin_family = AF_INET;
server.sin_port = htons(DEFAULT_PORT);
server.sin_addr.s_addr = inet_addr("127.0.0.1");
if (connect(sClient, (struct sockaddr*)&server, sizeof(server))
== SOCKET_ERROR)
{
printf("connect() failed:%d\n", WSAGetLastError());
return 1;
}
printf("connect() success\n");
g_nClientEvent = WSACreateEvent();
WSAEventSelect(sClient, g_nClientEvent, FD_READ | FD_CLOSE);
ExecThreadMng(sClient);
WSACleanup();
system("pause");
return 0;
}
void ExecThreadMng(SOCKET sClient)
{
HANDLE hRecvThread = CreateThread(NULL, 0, SocketRecvThread,
(LPVOID)sClient, 0, NULL);
HANDLE hSendThread = CreateThread(NULL, 0, SocketSendThread,
(LPVOID)sClient, 0, NULL);
if ( NULL == hRecvThread
|| NULL == hSendThread)
{
printf("CreateThread() failed:%d\n", GetLastError());
return;
}
HANDLE arrayWaitHandle[3];
arrayWaitHandle[0] = hRecvThread;
arrayWaitHandle[1] = hSendThread;
//等待线程执行完毕
WaitForMultipleObjects(2, arrayWaitHandle, TRUE, INFINITE);
CloseHandle(hRecvThread);
CloseHandle(hSendThread);
}
//客户端接受消息线程
DWORD WINAPI SocketRecvThread(LPVOID lpParam)
{
SOCKET sClient = (SOCKET)lpParam;
WSAEVENT arrayEvent[1];
arrayEvent[0] = g_nClientEvent;
while (true)
{
DWORD nEventIndex = WSAWaitForMultipleEvents(1, arrayEvent,
FALSE, WSA_INFINITE, FALSE);
DWORD nArrayIndex = nEventIndex - WSA_WAIT_EVENT_0;
WSANETWORKEVENTS networkEvents;
WSAEnumNetworkEvents(sClient, g_nClientEvent, &networkEvents);
if (networkEvents.lNetworkEvents & FD_READ)
{
//获取客户端消息
int nErrorCode = networkEvents.iErrorCode[FD_READ_BIT];
if (nErrorCode != 0)
{
printf("FD_READ failed with error %d\n", nErrorCode);
break;
}
if (!g_bClientClose)
{
//Read data from the socket
char szBuff[DEFAULT_BUFFER];
int ret = recv(sClient, szBuff, DEFAULT_BUFFER, 0);
if (SOCKET_ERROR == ret)
{
printf("recv() failed\n");
}
else
{
szBuff[ret] = '\0';
printf("recv from server:%s\n", szBuff);
}
}
else
{
printf("FD_READ quit\n");
break;
}
}
else if (networkEvents.lNetworkEvents & FD_CLOSE)
{
//客户端连接关闭
int nErrorCode = networkEvents.iErrorCode[FD_CLOSE_BIT];
if (nErrorCode != 0)
{
printf("FD_CLOSE failed with error %d\n", nErrorCode);
}
printf("FD_CLOSE close socket\n");
break;
}
else
{
printf("FD_NULL quit\n");
break;
}
}
return 1;
}
//客户端每3秒钟,定时发消息到服务端
DWORD WINAPI SocketSendThread(LPVOID lpParam)
{
SOCKET sClient = (SOCKET)lpParam;
char szMessage[64] = {0};
strcpy_s(szMessage, "client timing call");
//客户端发送3次则退出
for (int i = 0; i < 3; i++)
{
Sleep(1000);
int nRet = send(sClient, szMessage, (int)strlen(szMessage), 0);
if (nRet == SOCKET_ERROR)
{
printf("send() failed:%d\n", WSAGetLastError());
}
else
{
printf("Client send:%s\n", szMessage);
}
}
g_bClientClose = true;
//关闭连接
closesocket(sClient);
//关闭事件
WSACloseEvent(g_nClientEvent);
return 1;
}