重点:
一、Windows网络编程之OOP思想的应用
二、用多线程实现非阻塞式
三、回调模型应用一个简单的架构。
关于Socket编程模型的资料网上能搜索到很多,我发现资料中的示例代码没有问题的资料真不多。后面我至少写五篇文章,分别详细介绍最主要的五个Socket编程模型。首先介绍简单选择模型。
阻塞模式:执行I/O操作完成前会一直进行等待,不会将控制权交给程序。套接字默认为阻塞模式。可以通过多线程技术进行处理。
非阻塞模式:执行I/O操作时,Winsock函数会返回并交出控制权。这种模式使用起来比较复杂,因为函数在没有运行完成就进行返回,会不断地返回 WSAEWOULDBLOCK错误。但功能强大。
如果你想在Windows平台上构建服务器应用,那么I/O模型是你必须考虑的。Windows操作系统提供了选择(Select)、异步选择(WSAAsyncSelect)、事件选择(WSAEventSelect)、重叠I/O(Overlapped I/O)和完成端口(Completion Port)共五种I/O模型。每一种模型均适用于一种特定的应用场景。程序员应该对自己的应用需求非常明确,而且综合考虑到程序的扩展性和可移植性等因素,作出自己的选择。
这种客户端是最好的测试式具。它不加工消息内容,而是校验发送出去的消息是否与接收到的消息一致。主要用途是与反射式服务端一起校验网络的稳定性。
工程代码非常简单,就一个Win32控制台工程。一个主要的代码文件reflect_client.cpp,代码如下:
#include "stdafx.h"
#define SERVER_IP_ADDRESS "127.0.0.1"
#define SOCKET_PORT_NUMBER 5150
#define SOCKET_BUFFER_SIZE 1024
int _tmain(int argc, _TCHAR* argv[])
{
WSADATA data;
SOCKET client;
SOCKADDR_IN server_addr;
char message[SOCKET_BUFFER_SIZE] = { 0 };
int iret=0;
WSAStartup(MAKEWORD(2,2), &data);
client = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
memset(&server_addr, 0, sizeof(SOCKADDR_IN));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.S_un.S_addr = inet_addr(SERVER_IP_ADDRESS);
server_addr.sin_port = htons(SOCKET_PORT_NUMBER);
connect(client, (struct sockaddr*)&server_addr, sizeof(SOCKADDR_IN));
while (true)
{
strcpy_s(message, "你好");
printf_s("发:%s\n",message);
send(client, message, strlen(message), 0);
ZeroMemory(message, SOCKET_BUFFER_SIZE);
iret = recv(client, message, SOCKET_BUFFER_SIZE, 0);
if (iret < 0)
{
printf("没有收到响应。\n");
}
else
{
message[iret] = 0;
printf("收:%s\n", message);
}
Sleep(1000);
}
closesocket(client);
WSACleanup();
return 0;
}
新建一个Win32项目,类型是静态库。修改工程属性,把“MFC的使用”改为“在共享DLL中使用MFC”,把“代码生成·运行库”改为“多线程调试DLL(/MDd)”。用向导增加一个类,类名称为iserver_manager,代码如下:
#pragma once
class iserver_manager
{
public:
virtual bool accept_by_crt() = 0;
virtual bool accept_by_winapi() = 0;
virtual void receive() = 0;
virtual void shutdown() = 0;
public:
iserver_manager();
virtual ~iserver_manager();
};
#pragma once
#include "../management/iserver_manager.h"
class icallback
{
public:
virtual void set_manager(iserver_manager* manager) = 0;
virtual iserver_manager* get_manager() = 0;
virtual void shutdown() = 0;
public:
icallback();
virtual ~icallback();
};
#pragma once
#include "icallback.h"
#include
class common_callback:
public icallback
{
private:
iserver_manager* pmanager;
HANDLE accept_crt_handle;
HANDLE accept_winapi_handle;
HANDLE receive_handle;
DWORD accept_crt_id;
DWORD accept_winapi_id;
DWORD receive_id;
public:
//icallback
void set_manager(iserver_manager* pmanager);
iserver_manager* get_manager();
void shutdown();
public:
void start_accept_by_crt();
void start_accept_by_winapi();
void start_receive();
public:
common_callback();
virtual ~common_callback();
};
#include "stdafx.h"
#include "common_callback.h"
#include "../privates.h"
common_callback::common_callback()
{
}
common_callback::~common_callback()
{
}
void common_callback::set_manager(iserver_manager* pmanager)
{
this->pmanager = pmanager;
}
iserver_manager* common_callback::get_manager()
{
return pmanager;
}
void common_callback::start_accept_by_crt()
{
accept_crt_handle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)accept_crt_thread, this, 0, &accept_crt_id);
}
void common_callback::start_accept_by_winapi()
{
accept_winapi_handle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)accept_winapi_thread, this, 0, &accept_winapi_id);
}
void common_callback::start_receive()
{
receive_handle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)receive_thread, this, 0, &receive_id);
}
void common_callback::shutdown()
{
TerminateThread(accept_crt_handle, 0);
TerminateThread(accept_winapi_handle, 0);
TerminateThread(receive_handle, 0);
}
DWORD CALLBACK accept_crt_thread(LPARAM param)
{
((icallback*)param)->get_manager()->accept_by_crt();
return 0;
}
DWORD CALLBACK accept_winapi_thread(LPARAM param)
{
((icallback*)param)->get_manager()->accept_by_winapi();
return 0;
}
DWORD CALLBACK receive_thread(LPARAM param)
{
((icallback*)param)->get_manager()->receive();
return 0;
}
#pragma once
#include
#include
#include
#include
class blocked_server_manager:
protected iserver_manager
{
private:
int iclient_count;
SOCKET sconnections[FD_SETSIZE];
std::vector vconnections;
common_callback callback;
BOOL brunning;
protected:
bool accept_by_crt();
bool accept_by_winapi();
void receive();
public:
bool asynchronous_accept_by_crt();
bool asynchronous_accept_by_winapi();
void asynchronous_receive();
void shutdown();
public:
blocked_server_manager();
virtual ~blocked_server_manager();
};
int CALLBACK ConditionFunc(LPWSABUF lpCallerId, LPWSABUF lpCallerData, LPQOS lpSQOS, LPQOS lpGQOS, LPWSABUF lpCalleeId, LPWSABUF lpCalleeData, GROUP FAR* g, DWORD_PTR dwCallbackData);
#include "stdafx.h"
#include "blocked_server_manager.h"
#define SOCKET_PORT 5150
#define SOCKET_MESSAGE_SIZE 1024
blocked_server_manager::blocked_server_manager()
{
callback.set_manager(this);
iclient_count = 0;
FD_ZERO(sconnections);
}
blocked_server_manager::~blocked_server_manager()
{
}
bool blocked_server_manager::accept_by_crt()
{
WSADATA data;
SOCKET slisten, sconnection;
SOCKADDR_IN alocal = { 0 }, aconnection = { 0 };
int iaddr_size = sizeof(SOCKADDR_IN);
int iret = 0;
int ibind_port = SOCKET_PORT;
DWORD_PTR wsaCallbackData = 0;
WSAStartup(MAKEWORD(2, 2), &data);
slisten = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
alocal.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
alocal.sin_family = AF_INET;
alocal.sin_port = htons(ibind_port);
do
{
iret = bind(slisten, (struct sockaddr*)&alocal, sizeof(SOCKADDR_IN));
if (iret == -1)
{
ibind_port++;
alocal.sin_port = htons(ibind_port);
}
else
{
printf("服务启动成功,监听端口是:%d\n", ibind_port);
}
} while (iret == -1);
listen(slisten, 3);
while (brunning)
{
sconnection = accept(slisten, (struct sockaddr*)&aconnection, &iaddr_size);
printf("新连接:%s:%d\n", inet_ntoa(aconnection.sin_addr), ntohs(aconnection.sin_port));
sconnections[iclient_count++] = sconnection;
}
return true;
}
bool blocked_server_manager::accept_by_winapi()
{
WSADATA data;
SOCKET slisten, sconnection;
SOCKADDR_IN alocal = { 0 }, aconnection = { 0 };
int iaddr_size = sizeof(SOCKADDR_IN);
int iret = 0;
int ibind_port = SOCKET_PORT;
WSAStartup(MAKEWORD(2, 2), &data);
slisten = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
alocal.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
alocal.sin_family = AF_INET;
alocal.sin_port = htons(ibind_port);
do
{
iret = bind(slisten, (struct sockaddr*)&alocal, sizeof(SOCKADDR_IN));
if (iret == -1)
{
ibind_port++;
alocal.sin_port = htons(ibind_port);
}
else
{
printf("服务启动成功,监听端口是:%d\n", ibind_port);
}
} while (iret == -1);
listen(slisten, 3);
while (brunning)
{
sconnection = WSAAccept(slisten, (struct sockaddr*)&aconnection, &iaddr_size, ConditionFunc, iclient_count);
if (sconnection == -1)
continue;
printf("新连接:%s:%d\n", inet_ntoa(aconnection.sin_addr), ntohs(aconnection.sin_port));
sconnections[iclient_count++] = sconnection;
}
return true;
}
void blocked_server_manager::receive()
{
int i = 0, iret = 0;
fd_set selector;
struct timeval tv = { 1, 0 };
char message[SOCKET_MESSAGE_SIZE] = { 0 };
while (brunning)
{
FD_ZERO(&selector);
for (i = 0; i < iclient_count; i++)
{
FD_SET(sconnections[i], &selector);
}
iret = select(0, &selector, NULL, NULL, &tv);
if (iret == 0)
continue;
for (i = 0; i < iclient_count; i++)
{
if (FD_ISSET(sconnections[i], &selector))
{
iret = recv(sconnections[i], message, SOCKET_MESSAGE_SIZE, 0);
if (iret == 0 || iret == SOCKET_ERROR)
{
printf("Client :%d closed!\n", sconnections[i]);
closesocket(sconnections[i]);
if (i < iclient_count - 1)
{
sconnections[i] = sconnections[iclient_count - 1];
}
i--;
iclient_count--;
}
else
{
message[iret] = 0;
send(sconnections[i], message, iret, 0);
}
}
}
}
}
bool blocked_server_manager::asynchronous_accept_by_crt()
{
brunning = TRUE;
callback.start_accept_by_crt();
return true;
}
bool blocked_server_manager::asynchronous_accept_by_winapi()
{
brunning = TRUE;
callback.start_accept_by_winapi();
return true;
}
void blocked_server_manager::asynchronous_receive()
{
callback.start_receive();
}
void blocked_server_manager::shutdown()
{
brunning = FALSE;
callback.shutdown();
WSACleanup();
}
int CALLBACK ConditionFunc(LPWSABUF lpCallerId, LPWSABUF lpCallerData, LPQOS lpSQOS, LPQOS lpGQOS, LPWSABUF lpCalleeId, LPWSABUF lpCalleeData, GROUP FAR* g, DWORD_PTR dwCallbackData)
{
if (dwCallbackData > 2)
{
printf_s("连接数已达到最大值。\n");
return CF_REJECT;
}
return CF_ACCEPT;
}
blocked_server.cpp代码如下:
#include "stdafx.h"
#include "blocked_server_manager.h"
blocked_server_manager app;
int _tmain(int argc, _TCHAR* argv[])
{
app.asynchronous_accept_by_winapi();
app.asynchronous_receive();
printf_s("按任意键关闭服务器并退出程序。\n");
system("pause");
app.shutdown();
return 0;
}
首选上一张截图吧。
优点是简单,直接上手,缺点是效率不高,在没有客户端的连接的时候CPU空转导致使用率可以达到100%。所有这个方式是不推荐使用的。