版权声明: https://blog.csdn.net/dashoumeixi/article/details/86495818
基础:套接字重叠io
客户端: telnet 或者自己随意写一个;
纯重叠实现的服务器模型,基本上就是使用OVERLAPPED和APC函数完成.
APC函数即需要使用alterable状态的函数 , 例 SleepEx.
另外重叠io与非阻塞是2个概念.
下面的代码中使用到了非阻塞io, 仅仅作用在了accept上. 让其不阻塞;
这种纯重叠io模型的服务器缺点是既要accept,又要让线程进入alterab;e状态
这个例子同时解释了非阻塞io. 和重叠io : 可以把设置非阻塞的函数注释.看看效果
#include "stdafx.h"
#include "../utils.h"
#define BUFFSIZE 8192
//自定义一块数据. 用于放入OVERLAPPED的Event中,以便传入APC
typedef struct{
SOCKET hSocket;
WSABUF wsabuf;
char buf[BUFFSIZE];
} PER_DATA , *PPER_DATA;
void CALLBACK WriteRouine(IN DWORD dwError,IN DWORD cbTransferred,IN LPWSAOVERLAPPED lpOverlapped,IN DWORD dwFlags);
//读取 - APC
void CALLBACK ReadRoutine(
IN DWORD dwError,
IN DWORD cbTransferred,
IN LPWSAOVERLAPPED lpOverlapped,
IN DWORD dwFlags
){
printf("readRoutine , error:%ld \t bytesRead : %ld \tflags:%ld", dwError, cbTransferred, dwFlags);
PPER_DATA pData = (PPER_DATA)lpOverlapped->hEvent;
//如果对端EOF
if (cbTransferred == 0) {
closesocket(pData->hSocket);
free(lpOverlapped->hEvent);
free(lpOverlapped);
puts("peer closed!!");
}
else{
//否则回传
pData->wsabuf.len = cbTransferred;
//最后一个参数传递, write routine。 即接受完发送数据,发送数据完,等待再次接受
WSASend(pData->hSocket, &pData->wsabuf, 1, NULL, 0, lpOverlapped, WriteRouine);
puts("send data;");
}
}
//写入 - APC
void CALLBACK WriteRouine(
IN DWORD dwError,
IN DWORD cbTransferred,
IN LPWSAOVERLAPPED lpOverlapped,
IN DWORD dwFlags
){
DWORD flag = 0;
printf("WriteRouine , error:%ld \t bytesRead : %ld \tflags:%ld", dwError, cbTransferred, dwFlags);
PPER_DATA pData = (PPER_DATA)lpOverlapped->hEvent;
//写完后等待再次接受数据
WSARecv(pData->hSocket, &pData->wsabuf, 1, NULL, &flag, lpOverlapped, ReadRoutine);
puts("recv data");
}
//设置非阻塞
int setNonBlockMode(SOCKET sock,u_long bEnable)
{
return ioctlsocket(sock, FIONBIO, &bEnable);
}
int _tmain(int argc, _TCHAR* argv[])
{
WSAData wsadata;
if (WSAStartup(MAKEWORD(2, 2), &wsadata) == SOCKET_ERROR){
print_error(WSAGetLastError());
return 0;
}
SOCKET hListenSocket =WSASocket(AF_INET, SOCK_STREAM, 0, NULL, NULL, WSA_FLAG_OVERLAPPED);
//设置监听socket为非阻塞.可以把这一行注释掉,这样就将阻塞在accept上了
setNonBlockMode(hListenSocket, TRUE);
SOCKADDR_IN serv_addr, client_addr;
memset(&serv_addr, 0, sizeof(serv_addr));
memset(&client_addr, 0, sizeof(client_addr));
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(PORT);
if (SOCKET_ERROR == bind(hListenSocket, (SOCKADDR*)&serv_addr, sizeof(serv_addr))){
print_error(WSAGetLastError());
return 0;
}
if (SOCKET_ERROR == listen(hListenSocket, BACKLOG)){
print_error(WSAGetLastError());
return 0;
}
int client_addr_size = sizeof(client_addr) , error = 0 , ret = 0;
SOCKET hClientSock = NULL;
PPER_DATA client_info = NULL;
WSAOVERLAPPED * pOver = NULL;
DWORD flags = 0, recvBytes = 0;;
while (1)
{
flags = 0;
ret = SleepEx(1000, TRUE); // alterable状态
if (ret == WAIT_IO_COMPLETION){ //一旦routine完成将返回这个信息
puts("\t io completed");
}
client_addr_size = sizeof(client_addr);
hClientSock = accept(hListenSocket, (SOCKADDR*)&client_addr, &client_addr_size);
//由于是非阻塞套接字,因此需要判断是否真的有连接进入
if (INVALID_SOCKET == hClientSock){
error = WSAGetLastError();
//这个错误信息是非阻塞的典型错误
if (error == WSAEWOULDBLOCK)
puts("no connection");
continue;
}
//创建一个OVERLAPPED结构
pOver =(LPWSAOVERLAPPED) malloc(sizeof(WSAOVERLAPPED));
memset(pOver, 0, sizeof(WSAOVERLAPPED));
//创建一块自定义数据
client_info = (PPER_DATA)malloc(sizeof(PER_DATA));
client_info->hSocket = hClientSock;
client_info->wsabuf.buf = client_info->buf;
client_info->wsabuf.len = BUFFSIZE;
//由于使用了APC,hEvent可以自由分配
pOver->hEvent = (HANDLE)client_info;
//接受数据,无论是否是非阻塞 与 OVERLAPPED 无关
ret = WSARecv(hClientSock, &client_info->wsabuf, 1, &recvBytes, &flags, pOver, ReadRoutine);
if (0 == ret){
printf("recv quickly , recvBytes:%ld\n", recvBytes);
}
else {
error = WSAGetLastError();
if (WSA_IO_PENDING == error)
puts("**** pending *** ");
else
print_error(error);
}
}
WSACleanup();
return 0;
}