刚刚把重叠I/O套接字理解了一点,于是在此做个笔记,给出一个重叠I/O处理单个套接字的程序。
这个程序是TCP的服务器端程序。该程序只能接收一个客户端的连接,循环发送信息,以及该客户端退出时服务器端得到响应。
套接字库初始化等代码省略了,只包含一个监听部分和一个处理I/O部分的代码。如下:
WSABUF wsabuf;
wsabuf.buf=new char[1000];
wsabuf.len=1000;
memset(wsabuf.buf,0,1000);
m_listensock=socket(AF_INET,SOCK_STREAM,0);
SOCKADDR_IN m_addr;
m_addr.sin_addr.S_un.S_addr=INADDR_ANY;
m_addr.sin_family=AF_INET;
m_addr.sin_port=htons(6000);
bind(m_listensock,(SOCKADDR*)&m_addr,sizeof(SOCKADDR));
listen(m_listensock,5);
RECVPARAM *p=new RECVPARAM;
p->sock[0]=m_acceptsock;
p->sock[1]=m_listensock;
p->wsabuf.buf=wsabuf.buf;
p->wsabuf.len=wsabuf.len;
HANDLE h_thread=CreateThread(NULL,0,IOProc,(LPVOID)p,0,NULL);
CloseHandle(h_thread);
}
DWORD WINAPI COVERLAPPEDDlg::IOProc(LPVOID lparam)
{
WSABUF wsabuf;
DWORD dwRead;
DWORD dwFlag=0;
SOCKET s=((RECVPARAM*)lparam)->sock[0];
SOCKET sL=((RECVPARAM*)lparam)->sock[1];
wsabuf.buf=((RECVPARAM*)lparam)->wsabuf.buf;
wsabuf.len=((RECVPARAM*)lparam)->wsabuf.len;
s=accept(sL,NULL,NULL);
if(s!=INVALID_SOCKET)
AfxMessageBox("有一个用户连接上了!");
EventArray[EventTotal]=WSACreateEvent();
ZeroMemory(&lp,sizeof(WSAOVERLAPPED));
lp.hEvent=EventArray[EventTotal];
wsabuf.buf=new char[1000];
wsabuf.len=1000;
EventTotal++;//EventTotal=1了
memset(wsabuf.buf, 0, 1000);
//把s变成阻塞的
u_long ul=0;
ioctlsocket(s,FIONBIO,(u_long*)&ul);
//发送一个接收数据请求,通常是返回ERROR的,这里判断在ERROR时
//如果是WSA_IO_PENDING则说明I/O操作正在进行,就接着执行后面的操作
//否则返回
if(WSARecv(s,&wsabuf,1,&dwRead,&dwFlag,&lp,NULL)
==NO_ERROR)//这时候重叠结构lp对应的事件对象受信了
{
if(WSAGetLastError()!=WSA_IO_PENDING)
{
// 错误返回不是WSA_IO_PENDING,这时候可能是对方根本就
//没发送数据,I/O操作没有执行
AfxMessageBox("出错!");
return 1;
}
}
//开始进行重叠I/O的操作了
while(1)
{
//查询哪个事件对象是受信的
int index=WSAWaitForMultipleEvents(EventTotal,EventArray,FALSE,WSA_INFINITE,FALSE);
//把那个事件对象重置为不受信状态
WSAResetEvent(EventArray[index-index-WSA_WAIT_EVENT_0]);
DWORD flag;
DWORD byte;
//判断重叠调用的返回状态
WSAGetOverlappedResult(s,&lp,&byte,FALSE,&flag);
//对方已关闭则关闭套接字
if(byte==0)
{
AfxMessageBox("对方关闭连接2!");
closesocket(s);
//释放事件对象,注意索引
WSACloseEvent(EventArray[index-index-WSA_WAIT_EVENT_0]);
return 1;
}
//如果上面检查对方没关闭则显示接收到的数据
AfxMessageBox(wsabuf.buf);
SOCKADDR_IN M_ADDR;
int len=sizeof(SOCKADDR);
//s=accept(sL,(SOCKADDR*)&M_ADDR,&len);
flag=0;
//重新把重叠结构清理
ZeroMemory(&lp,sizeof(WSAOVERLAPPED));
//再与刚刚被重置的事件对象关联
lp.hEvent=EventArray[index-WSA_WAIT_EVENT_0];
//再发送另一个接收请求,解释跟第一个一样
if(WSARecv(s,&wsabuf,1,&dwRead,&flag,&lp,NULL)==NO_ERROR)
{
if(WSAGetLastError()!=WSA_IO_PENDING)
{
AfxMessageBox("出错!");
return 1;
}
}
}
return 0;
}
上面是MFC下实现的单套接字的情况,下面给出在控制台下实现的多套接字情况,其实也不能说是“多”,因为他每处理一个套接字都要创建一个线程,这显然是不行的。
typedef struct userInfo
{
SOCKET hSocket; // 主键:通信套接字
char szClientIP[16];// 自定义字段:客户端地址
}USERINFO, * PUSERINFO;
DWORD WINAPI IOProc(LPVOID lparam);
int main( )
{
PUSERINFO pUserInfo;
SOCKET m_listensock;
SOCKET m_acceptsock;
int len;
HANDLE hThread;
// Start WinSock and create a listen socket.
WSADATA wsadata;
WSAStartup(MAKEWORD(2,2),&wsadata);
m_listensock=socket(AF_INET,SOCK_STREAM,0);
SOCKADDR_IN add;
add.sin_addr.S_un.S_addr=INADDR_ANY;
add.sin_family=AF_INET;
add.sin_port=htons(6000);
bind(m_listensock,(SOCKADDR*)&add,sizeof(SOCKADDR));
listen(m_listensock, 5);
while(1)
{
len = sizeof(SOCKADDR);
m_acceptsock = accept(m_listensock, (LPSOCKADDR)&add, &len);
pUserInfo = (PUSERINFO)malloc(sizeof(userInfo));
pUserInfo->hSocket = m_acceptsock;
// 注意这里将连接的客户端的IP地址,保存到了pUserInfo字段中了
strcpy(pUserInfo->szClientIP, inet_ntoa(add.sin_addr));
// 为本次客户请求产生子线程
hThread = CreateThread(
NULL,
0,
IOProc,
(LPVOID)pUserInfo, // 将pUserInfo传给子线程
0,
NULL
);
CloseHandle(hThread);
}
return 0;
}
DWORD WINAPI IOProc(LPVOID lpParam)
{
PUSERINFO pUserInfo = (PUSERINFO)lpParam;
WSAOVERLAPPED Overlapped;
WSABUF wsaBuf;
char Buffer[1024];
BOOL bResult;
int nResult;
DWORD dwNumOfBytesRecved;
DWORD dwBytesTransferred;
DWORD dwFlags;
ZeroMemory(&Overlapped, sizeof(WSAOVERLAPPED));
wsaBuf.buf = Buffer;
wsaBuf.len = sizeof(Buffer);
memset(wsaBuf.buf,0,wsaBuf.len);
dwFlags = 0;
nResult = WSARecv(
pUserInfo->hSocket, // Receive socket
&wsaBuf, // 指向WSABUF结构的指针
1, // WSABUF数组的个数
&dwNumOfBytesRecved, // 存放当WSARecv完成后所接收到的字节数
&dwFlags, // A pointer to flags
&Overlapped, // A pointer to a WSAOVERLAPPED structure
NULL // A pointer to the completion routine,this is NULL
);
if ( nResult == SOCKET_ERROR && GetLastError() != WSA_IO_PENDING)
{
printf("WSARecv(..) failed./n");
free(pUserInfo);
return -1;
}
while (TRUE)
{
bResult = WSAGetOverlappedResult(
pUserInfo->hSocket,
&Overlapped,
&dwBytesTransferred, // 当一个同步I/O完成后,接收到的字节数
TRUE, // 等待I/O操作的完成
&dwFlags
);
if (dwBytesTransferred == 0)
{
printf("客户端已退出,将断开与之的连接!/n");
closesocket(pUserInfo->hSocket);
free(pUserInfo);
break;
}
// 在这里将接收到的数据进行处理
printf("Received from IP: %s./ndata: %s/n", pUserInfo->szClientIP, wsaBuf.buf);
// 发送另外一个请求操作
ZeroMemory(&Overlapped, sizeof(WSAOVERLAPPED));
dwFlags = 0;
nResult = WSARecv(
pUserInfo->hSocket, // Receive socket
&wsaBuf, // 指向WSABUF结构的指针
1, // WSABUF数组的个数
&dwNumOfBytesRecved, // 存放当WSARecv完成后所接收到的字节数
&dwFlags, // A pointer to flags
&Overlapped, // A pointer to a WSAOVERLAPPED structure
NULL // A pointer to the completion routine,this is NULL
);
if ( nResult == SOCKET_ERROR && GetLastError() != WSA_IO_PENDING)
{
printf("WSARecv(..) failed./n");
free(pUserInfo);
return -1;
}
}
return 1;
}