转载请注明来源:http://blog.csdn.net/caoshiying/article/details/52876090
LPVOID HeapAlloc(HANDLE hHeap, DWORD dwFlags, SIZE_T dwBytes);
hHeap:要分配堆的句柄,可以通过HeapCreate()函数或GetProcessHeap()函数获得。
HANDLE GetProcessHeap(void);
函数没有参数。如果函数成功,返回调用进程的默认内存堆句柄。这个函数允许你从线程的堆动态分配内存,而不必使用HeapCreare函数建立一个堆。如果函数失败,返回 Null。若想,可以调用GetLastError获得更多错误信息。
BOOL HeapFree(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem);
hHeap:堆内存块释放。这个参数是HeapCreate或GetProcessHeap函数返回的句柄。
int WSARecv(SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount, LPDWORD lpNumberOfBytesRecvd, LPDWORD lpFlags, LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine);
s:连接到此服务器的socket对象。
void CALLBACK CompletionROUTINE(DWORD dwError, DWORD cbTransferred, LPWSAOVERLAPPED lpOverlapped, DWORD dwFlags);
CompletionRoutine is a placeholder for an application-defined or library-defined function name. The dwError specifies the completion status for the overlapped operation as indicated by lpOverlapped. The cbTransferred parameter specifies the number of bytes received. The dwFlags parameter contains information that would have appeared in lpFlags if the receive operation had completed immediately. This function does not return a value.
Returning from this function allows invocation of another pending completion routine for this socket. When using WSAWaitForMultipleEvents, all waiting completion routines are called before the alertable thread's wait is satisfied with a return code of WSA_IO_COMPLETION. The completion routines can be called in any order, not necessarily in the same order the overlapped operations are completed. However, the posted buffers are guaranteed to be filled in the same order in which they are specified.If you are using I/O completion ports, be aware that the order of calls made to WSARecv is also the order in which the buffers are populated. WSARecv should not be called on the same socket simultaneously from different threads, because it can result in an unpredictable buffer order.
typedef struct
{
WSAOVERLAPPED overlap;
WSABUF buf;
char message[SOCKET_MESSAGE_SIZE];
DWORD received_byte_count;
DWORD flags;
SOCKET client;
iserver_manager *manager;
}PEERIO_OPERATION_DATA, *LPPEERIO_OPERATION_DATA;
overlap:记录原始的值。
#pragma once
#include
#include
//#include
#define SOCKET_MESSAGE_SIZE 1024
typedef struct
{
WSAOVERLAPPED overlap;
WSABUF buf;
char message[SOCKET_MESSAGE_SIZE];
DWORD received_byte_count;
DWORD flags;
SOCKET client;
iserver_manager *manager;
}PEERIO_OPERATION_DATA, *LPPEERIO_OPERATION_DATA;
class overlapped_server_manager:
protected iserver_manager
{
private:
int iport;
int iclient_count;
int iaddr_size;
SOCKET server;
SOCKET snew_client;
WSADATA data;
LPPEERIO_OPERATION_DATA peers[WSA_MAXIMUM_WAIT_EVENTS];
common_callback callback;
BOOL brunning;
BOOL bnew_client;
private:
void cleanup();
int find_peer(LPPEERIO_OPERATION_DATA peer);
protected:
bool accept_by_crt();
bool accept_by_winapi();
void receive();
public:
void shutdown();
void start_accept();
void start_receive();
void completion_routine(DWORD error, DWORD transfered, LPPEERIO_OPERATION_DATA peer, DWORD flags);
public:
overlapped_server_manager();
virtual ~overlapped_server_manager();
};
void CALLBACK do_completion_routine(DWORD error, DWORD transfered, LPWSAOVERLAPPED overlapped, DWORD flags);
#include "overlapped_server_manager.h"
#include
#include
#include
overlapped_server_manager::overlapped_server_manager()
{
iport = 5150;
iclient_count = 0;
iaddr_size = sizeof(SOCKADDR_IN);
ZeroMemory(peers, sizeof(LPPEERIO_OPERATION_DATA));
WSAStartup(MAKEWORD(2, 2), &data);
brunning = FALSE;
bnew_client = FALSE;
callback.set_manager(this);
}
overlapped_server_manager::~overlapped_server_manager()
{
cleanup();
}
bool overlapped_server_manager::accept_by_crt()
{
SOCKET server;
SOCKADDR_IN server_addr;
SOCKADDR_IN client_addr;
int iret = 0;
server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
server_addr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(iport);
do
{
iret = bind(server, (struct sockaddr*)&server_addr, iaddr_size);
if (iret == SOCKET_ERROR)
{
iport++;
server_addr.sin_port = htons(iport);
}
} while (iret == -1);
listen(server, 3);
printf("服务启动成功,端口是:%d\n", iport);
while (brunning)
{
snew_client = accept(server, (struct sockaddr*)&client_addr, &iaddr_size);
if (snew_client == INVALID_SOCKET)
continue;
printf("新客户端连接:%s:%d\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
bnew_client = TRUE;
}
return true;
}
bool overlapped_server_manager::accept_by_winapi()
{
return true;
}
void overlapped_server_manager::receive()
{
LPPEERIO_OPERATION_DATA peer = NULL;
while (brunning)
{
if (bnew_client)
{
peer = (LPPEERIO_OPERATION_DATA)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(PEERIO_OPERATION_DATA));
peer->buf.len = SOCKET_MESSAGE_SIZE;
peer->buf.buf = peer->message;
peer->client = snew_client;
peer->manager = this;
peers[iclient_count] = peer;
WSARecv(peer->client, &peer->buf, 1, &peer->received_byte_count, &peer->flags, &peer->overlap, &do_completion_routine);
bnew_client = FALSE;
iclient_count++;
}
SleepEx(1000, TRUE);
}
}
void overlapped_server_manager::shutdown()
{
callback.shutdown();
cleanup();
}
void overlapped_server_manager::cleanup()
{
int i = 0;
brunning = FALSE;
for (i = 0; i < iclient_count; i++)
HeapFree(GetProcessHeap(), 0, peers[i]);
iclient_count = 0;
}
void overlapped_server_manager::completion_routine(DWORD error, DWORD transfered, LPPEERIO_OPERATION_DATA peer, DWORD flags)
{
int index = -1;
index = find_peer(peer);
assert(index != -1);
if (error != 0 || transfered == 0)
{
closesocket(peer->client);
HeapFree(GetProcessHeap(), 0, peer);
if (index < iclient_count - 1)
peers[index] = peers[iclient_count - 1];
peers[iclient_count - 1] = nullptr;
iclient_count--;
}
else
{
peer->message[transfered] = 0;
send(peer->client, peer->message, transfered, 0);
ZeroMemory(&peer->overlap, sizeof(WSAOVERLAPPED));
peer->buf.len = SOCKET_MESSAGE_SIZE;
peer->buf.buf = peer->message;
WSARecv(peer->client, &peer->buf, 1, &peer->received_byte_count, &peer->flags, &peer->overlap, do_completion_routine);
}
}
int overlapped_server_manager::find_peer(LPPEERIO_OPERATION_DATA peer)
{
int index = -1;
int i = 0;
for (i = 0; i < iclient_count; i++)
{
if (peers[i] != peer)
continue;
index = i;
break;
}
return index;
}
void overlapped_server_manager::start_accept()
{
brunning = TRUE;
callback.start_accept_by_crt();
}
void overlapped_server_manager::start_receive()
{
brunning = TRUE;
callback.start_receive();
}
void CALLBACK do_completion_routine(DWORD error, DWORD transfered, LPWSAOVERLAPPED overlapped, DWORD flags)
{
auto peer = (LPPEERIO_OPERATION_DATA)overlapped;
auto pmanager = reinterpret_cast(peer->manager);
pmanager->completion_routine(error, transfered, peer, flags);
}
int main()
{
overlapped_server_manager osm;
osm.start_accept();
osm.start_receive();
printf("重叠(Overlapped)I/0模型服务端启动成功。按任意键关闭服务器。\n");
system("pause");
osm.shutdown();
return 0;
}
WSARecv提供recv几个额外的重要功能:可以进行套接字重叠进行重叠recv操作;允许多个接收缓冲区,使其适应于聚集/分散I/O类型;WSARecv函数通过参数 s 指定连接的SOCKETS 或者绑定未连接的SOCKETS 来读取未接收的数据。SOCKETS的本地地址是必须知道的。在服务器应用程序中,SOCKET通常显式通过bind绑定或者通过隐式的通过accept,WSAAccept绑定;在客户点应用程序中,SOCKET通过connect,WSAConnect,Sento,WSASendTo或者WSAJoinLeaf隐式的绑定本地地址。
对于连接和非连接的SOCKET,这个函数通过accept接收的消息限制地址,它仅仅对指定远程地址返回消息,忽略其他地址发送的消息。
对于重叠的SOCKETS,WSARecv通常投递一个或多个可以填充数据的缓冲区,申请完成后等待指定的完成指示(调用的完成例程或事件对象设置)发生,如果这个操作不能马上完成,最终的完成状态可以通过完成例程或WSAGetOverlappedResult获取.
所有的I/O在线程创建时初始化,线程退出时取消,对于重叠的SOCKETS,如果线程关闭,未决的异步操作可能会在完成之前失败。
如果 lpOverlapped ,lpCompletionRoutine 都NULL,SOCKET 在这个函数中被认为是非重叠的SOCKET。对于非重叠的SOCKETS,阻塞的语意与recv相同,且lpOverlapped ,lpCompletionRoutine都会被忽略,任何已经收到和传输缓冲的数据将被复制到指定的用户缓冲区,如果当前没有收到数据或没有任何数据传输,SOCKET将被阻塞,直到接收到数据。Window Socket 2没有为这个函数定义任何的标准阻塞超时机制。
基于字节流的协议栈试图尽可能返回多的可用的缓冲区空间和可接收的数据。然而,单个字节足够疏通这次调用,而且也没有任何可以保证返回多余一个字节。而基于消息的协议,则发送个完整的数据包。(字节流的协议和基于消息的协议的主要区别)。