Socket重叠IO
1.为什么到现在才弄懂这个
不知道这个Socket重叠IO这种模型是不是socket IO完成端口的基础,不过我感觉,学习一下这个再去学习socket IO完成端口是比较有好处的。
这个Scoket重叠IO我以前记得看过好几次,都没看懂。一部分原因是我没能静态心来写代码,还有更重要的原因就是,Socket重叠他们的结构体参数,还有传参数让人很难理解。下面我将对这些数据结构和参数进行一下讲解
2.初识WSARecv 函数
int WSARecv(
SOCKET s,//要接收消息的socket
LPWSABUF lpBuffers, //一个结构体数组。当接收IO操作完毕后接收内容就在这个里面了
DWORD dwBufferCount, //要接多少个WSABUF
LPDWORD lpNumberOfBytesRecvd,//接收了多少个字节
LPDWORD lpFlags,
LPWSAOVERLAPPED lpOverlapped,//Overlapped结构体指针
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine//在本节用不用
);
lpBuffers参数:这是一WSABUF数组,意思是这个函数可以接收不止一个字符缓冲,但是我们一般用一个就够了。 接收多个我还没能测试
dwBufferCount参数:是指上一个参数的数组个数
lpOverlapped参数:这个参数是Overlappad结构体指针,这个指针当IO操作完毕的时候,这里会被系统填充。当IO操作完成时这个结构也可以通过WSAGetOverlappedResult得到
返回值:
0:没有错误发生,IO操作当即完成
SOCKET_ERROR:发生错误
如果是SOCKET_ERROR并且WSAGetLastError() == WSA_IO_PENDING 这时表示操作已经提交。异步操作大部分都是这样的。
3.何时得到收取的消息,然后取出消息
当异常操作完成时,Overlapped的hEvent这个事件会触发。这时Overlapped的InternalHigh表示接受的字节数。Internal表示错误代码。消息的内容即是你当初调用WSARecv时传入的lpBuffers参数。
4.代码组织
以服务端为例
首先传入WSARecv的几个参数必定与一个socket关联。而且这些参数在异步调用完成之后,但是以后还要用(在WaitForMutiObjects时要用到),而且每一个socket得拥有一个不同的Event来标识是哪个客户端来消息了。所以为每一个客户端socket构造一个Overlapped结构。比如我测试的代码中每一个客户端都有这样一个结构体,而且当accept来的时候表示有新的socket连接,就得生成这样一个结构体,当客户端掉线的时候,就得删除这样一个结构体
下面就是这个结构体:
struct CClientInfo
{
public:
CClientInfo()
{
ZeroMemory(&m_ol,sizeof(m_ol));
ZeroMemory(m_szBuf,);
m_ol.hEvent = WSACreateEvent();
}
~CClientInfo()
{
WSACloseEvent(m_ol.hEvent);
}
WSAOVERLAPPED m_ol;
SOCKET sSocket;
CString strIp;
u_short nPort;
CString GetShowText();
char m_szBuf[];
};
下面是两个函数,一个是当客户端连接的时候,一个是当客户端断开的时候
CClientInfo * CServerDlg::OnSocketConnected(SOCKET sClientSocket,sockaddr_in * saClient)
{
u_short uPort = ntohs(((sockaddr_in *)saClient)->sin_port);
CString strIp = CA2T(inet_ntoa(((sockaddr_in *)saClient)->sin_addr));
CClientInfo * pClientInfo = new CClientInfo;
pClientInfo->nPort = uPort;
pClientInfo->strIp = strIp;
pClientInfo->sSocket = sClientSocket;
LockClientArray();
m_ClientArray.Add(pClientInfo);
int nIndexInserted = m_ClientListBox.AddString(pClientInfo->GetShowText());
m_ClientListBox.SetItemData(nIndexInserted,pClientInfo->sSocket);
UnLockClientArray();
return pClientInfo;
} void CServerDlg::OnSocketDisconnect(SOCKET aClientSocket)
{
LockClientArray();
for(int i = ;i<m_ClientArray.GetCount();i++)
{
CClientInfo * pClientInfo = m_ClientArray.GetAt(i);
if(pClientInfo->sSocket == aClientSocket)
{
m_ClientListBox.DeleteString(m_ClientListBox.FindString(,pClientInfo->GetShowText()));
delete pClientInfo;
m_ClientArray.RemoveAt(i);
break;
}
}
UnLockClientArray();
}
5.没有测试的内容和疑问
发送的时候没有用WSASend,以后再学习
6.注意
当使用AcceptEx之后,一般用GetAcceptExSockaddrs这个函数来得到本地或者远程的地址信息。但也可以调用getsockname这个函数,前提是,必须先给接受到socket设置一个SO_UPDATE_ACCEPT_CONTEXT。
7.AcceptEx用法
BOOL AcceptEx(
__in SOCKET sListenSocket,
__in SOCKET sAcceptSocket,
__in PVOID lpOutputBuffer,
__in DWORD dwReceiveDataLength,
__in DWORD dwLocalAddressLength,
__in DWORD dwRemoteAddressLength,
__out LPDWORD lpdwBytesReceived,
__in LPOVERLAPPED lpOverlapped
);
● sListenSocket 参数指定的是一个监听套接字。
● sAcceptSocket 参数指定的是另一个套接字,负责对进入连接请求的“接受”。
AcceptEx 函数和 accept 函数的区别在于,我们必须提供接受的套接字,而不是让函数自动为我们创建。
正是由于要提供套接字,所以要求我们事先调用 socket 或 WSASocket 函数,创建一个套接字,以便通过 sAcceptSocket 参数,将其传递给 AcceptEx。
● lpOutputBuffer 参数指定的是一个特殊的缓冲区,因为它要负责三种数据的接收:服务器的本地地址,客户机的远程地址,以及在新建连接上发送的第一个数据块。
● dwReceiveDataLength参数以字节为单位,指定了在 lpOutputBuffer 缓冲区中,保留多大的空间,用于数据的接收。
如这个参数设为0,那么在连接的接受过程中,不会再一道接收任何数据。
● dwLocalAddressLength 和 dwRemoteAddressLength 参数也是以字节为单位,指定在 lpOutputBuffer 缓冲区中,保留多大的空间,
在一个套接字被接受的时候,用于本地和远程地址信息的保存。
要注意的是,和当前采用的传送协议允许的最大地址长度比较起来,这里指定的缓冲区大小至少应多出16字节。
举个例子来说:假定正在使用的是 TCP/IP 协议,那么这里的大小应设为“SOCKADDRIN 结构的长度+16字节”。
● lpdwBytesReceived 参数用于返回接收到的实际数据量,以字节为单位。
只有在操作以同步方式完成的前提下,才会设置这个参数。假如 AcceptEx 函数返回 ERROR_IO_PENDING,
那么这个参数永远都不会设置,我们必须利用完成事件通知机制,获知实际读取的字节量。
● lpOverlapped 参数对应的是一个 OVERLAPPED 结构,允许 AcceptEx 以一种异步方式工作。
如我们早先所述,只有在一个重叠 I/O 应用中,该函数才需要使用事件对象通知机制,这是由于此时没有一个完成例程参数可供使用。
也就是说 AcceptEx 函数只能由本节课给大家讲的“事件通知”方式获取异步 I/O 请求的结果,而“完成例程”方法无法被使用。
8.源代码
.h文件
// ServerDlg.h : 头文件
// #pragma once #include "afxwin.h"
#include <Winsock2.h>
#pragma comment(lib,"Ws2_32.lib") #define WM_SOCKET WM_USER+220 enum IO_TYPE
{
IO_RECV,
IO_ACCEPT,
IO_UNKNOW
}; // CServerDlg 对话框
struct CClientInfo
{
public:
CClientInfo()
{
ZeroMemory(&m_ol,sizeof(m_ol));
ZeroMemory(m_szBuf,);
sAcceptSocket = INVALID_SOCKET;
m_ol.hEvent = WSACreateEvent();
m_IO_type = IO_UNKNOW;
}
~CClientInfo()
{
WSACloseEvent(m_ol.hEvent);
}
WSAOVERLAPPED m_ol;
SOCKET sSocket;
SOCKET sAcceptSocket;
CString strIp;
u_short nPort;
CString GetShowText();
char m_szBuf[];
IO_TYPE m_IO_type;
}; class CServerDlg : public CDialogEx
{
// 构造
public:
CServerDlg(CWnd* pParent = NULL); // 标准构造函数
~CServerDlg();
// 对话框数据
enum { IDD = IDD_SERVER_DIALOG }; protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持 // 实现
protected:
HICON m_hIcon; // 生成的消息映射函数
virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
DECLARE_MESSAGE_MAP() public:
afx_msg void OnBnClickedButton1();
void InitSocket();
SOCKET m_ServerSocket;
CClientInfo * OnSocketConnected(SOCKET sClientSocket,sockaddr_in * saClient);
void OnSocketDisconnect(SOCKET aClientSocket);
CListBox m_ClientListBox;
CArray<CClientInfo *> m_ClientArray;
CListBox m_RecvMsgListBox;
CRITICAL_SECTION m_CS_ClientArray;
void LockClientArray(){EnterCriticalSection(&m_CS_ClientArray);}
void UnLockClientArray(){LeaveCriticalSection(&m_CS_ClientArray);}
afx_msg void OnBnClickedButton3();
void PostRecv(CClientInfo * p);
BOOL PostAccept(SOCKET sListenSocket);
BOOL PostAccept(CClientInfo * p);
void OnError();
};
.cpp文件
// ServerDlg.cpp : 实现文件
// #include "stdafx.h"
#include "Server.h"
#include "ServerDlg.h"
#include "afxdialogex.h"
#include <Mswsock.h> #ifdef _DEBUG
#define new DEBUG_NEW
#endif // 用于应用程序“关于”菜单项的 CAboutDlg 对话框 class CAboutDlg : public CDialogEx
{
public:
CAboutDlg(); // 对话框数据
enum { IDD = IDD_ABOUTBOX }; protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持 // 实现
protected:
DECLARE_MESSAGE_MAP()
}; CAboutDlg::CAboutDlg() : CDialogEx(CAboutDlg::IDD)
{
} void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
} BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP() // CServerDlg 对话框 CServerDlg::CServerDlg(CWnd* pParent /*=NULL*/)
: CDialogEx(CServerDlg::IDD, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
m_ServerSocket = INVALID_SOCKET;
InitializeCriticalSection(&m_CS_ClientArray);
}
CServerDlg::~CServerDlg()
{
DeleteCriticalSection(&m_CS_ClientArray);
}
void CServerDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Control(pDX, IDC_LIST_CLIENT_LIST, m_ClientListBox);
DDX_Control(pDX, IDC_LIST_RECV, m_RecvMsgListBox);
} BEGIN_MESSAGE_MAP(CServerDlg, CDialogEx)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_BUTTON1, &CServerDlg::OnBnClickedButton1)
ON_BN_CLICKED(IDC_BUTTON3, &CServerDlg::OnBnClickedButton3)
END_MESSAGE_MAP() // CServerDlg 消息处理程序 BOOL CServerDlg::OnInitDialog()
{
CDialogEx::OnInitDialog(); // 将“关于...”菜单项添加到系统菜单中。 // IDM_ABOUTBOX 必须在系统命令范围内。
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000); CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
BOOL bNameValid;
CString strAboutMenu;
bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
ASSERT(bNameValid);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
} // 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动
// 执行此操作
SetIcon(m_hIcon, TRUE); // 设置大图标
SetIcon(m_hIcon, FALSE); // 设置小图标 // TODO: 在此添加额外的初始化代码 InitSocket(); SetDlgItemInt(IDC_EDIT_PORT,); return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
} void CServerDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialogEx::OnSysCommand(nID, lParam);
}
} // 如果向对话框添加最小化按钮,则需要下面的代码
// 来绘制该图标。对于使用文档/视图模型的 MFC 应用程序,
// 这将由框架自动完成。 void CServerDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // 用于绘制的设备上下文 SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), ); // 使图标在工作区矩形中居中
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + ) / ;
int y = (rect.Height() - cyIcon + ) / ; // 绘制图标
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialogEx::OnPaint();
}
} //当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR CServerDlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
} CString CClientInfo::GetShowText()
{
CString strItemText;
strItemText.Format(_T("%s:%d"),strIp,nPort);
return strItemText;
} CClientInfo * CServerDlg::OnSocketConnected(SOCKET sClientSocket,sockaddr_in * saClient)
{
BOOL bDelete = FALSE;
if(saClient == NULL)
{
sockaddr_in * pTempClientAddr = new sockaddr_in;
int nLen = sizeof(sockaddr_in);
if(SOCKET_ERROR == getpeername(sClientSocket,(sockaddr*)pTempClientAddr,&nLen))
{
int nErrorCode = ::WSAGetLastError();
if(SOCKET_ERROR == getpeername(sClientSocket,(sockaddr*)pTempClientAddr,&nLen))
OutputDebugStringA("Failed!\n");
}
saClient = (sockaddr_in*)pTempClientAddr;
bDelete = TRUE;
}
u_short uPort = ntohs(((sockaddr_in *)saClient)->sin_port);
CString strIp = CA2T(inet_ntoa(((sockaddr_in *)saClient)->sin_addr));
CClientInfo * pClientInfo = new CClientInfo;
pClientInfo->nPort = uPort;
pClientInfo->strIp = strIp;
pClientInfo->sSocket = sClientSocket;
pClientInfo->m_IO_type = IO_RECV;
LockClientArray();
m_ClientArray.Add(pClientInfo);
int nIndexInserted = m_ClientListBox.AddString(pClientInfo->GetShowText());
m_ClientListBox.SetItemData(nIndexInserted,pClientInfo->sSocket);
UnLockClientArray();
if(bDelete)
delete saClient;
return pClientInfo;
} void CServerDlg::OnSocketDisconnect(SOCKET aClientSocket)
{
LockClientArray();
for(int i = ;i<m_ClientArray.GetCount();i++)
{
CClientInfo * pClientInfo = m_ClientArray.GetAt(i);
if(pClientInfo->sSocket == aClientSocket)
{
m_ClientListBox.DeleteString(m_ClientListBox.FindString(,pClientInfo->GetShowText()));
delete pClientInfo;
m_ClientArray.RemoveAt(i);
break;
}
}
UnLockClientArray();
} BOOL CServerDlg::PostAccept(SOCKET sListenSocket)
{
// SOCKET sClientSocket = WSASocket(AF_INET,
// SOCK_STREAM,
// IPPROTO_TCP,
// NULL,0,WSA_FLAG_OVERLAPPED);
// char szBuf[256] = {0};
// DWORD dwByteReceived = 0;
// OVERLAPPED ov;
// memset(&ov,0,sizeof(ov));
// AcceptEx(sListenSocket,sClientSocket,szBuf,0,sizeof(sockaddr_in) + 16,sizeof(sockaddr_in) + 16,&dwByteReceived,&ov);
OutputDebugStringA("PostAccept!\n");
CClientInfo * pClientInfo = new CClientInfo;
SOCKET sClientSocket = WSASocket(AF_INET,
SOCK_STREAM,
IPPROTO_TCP,
NULL,,WSA_FLAG_OVERLAPPED);
pClientInfo->sSocket = sListenSocket;
pClientInfo->m_IO_type = IO_ACCEPT;
pClientInfo->sAcceptSocket = sClientSocket;
DWORD dwByteRecieved = ;
int nRet = AcceptEx(pClientInfo->sSocket,pClientInfo->sAcceptSocket,
pClientInfo->m_szBuf,,
sizeof(sockaddr_in) + ,sizeof(sockaddr_in) + ,&dwByteRecieved,
&pClientInfo->m_ol);
if(nRet || ::WSAGetLastError() == ERROR_IO_PENDING)
{
LockClientArray();
m_ClientArray.Add(pClientInfo);
UnLockClientArray();
return TRUE;
}
else
{
AfxMessageBox(_T("出错非常严重!PostAccept"));
return FALSE;
} } BOOL CServerDlg::PostAccept(CClientInfo * pClientInfo)
{
OutputDebugStringA("PostAccept!\n");
SOCKET sClientSocket = WSASocket(AF_INET,
SOCK_STREAM,
IPPROTO_TCP,
NULL,,WSA_FLAG_OVERLAPPED);
pClientInfo->m_IO_type = IO_ACCEPT;
pClientInfo->sAcceptSocket = sClientSocket;
DWORD dwByteRecieved = ;
int nRet = AcceptEx(pClientInfo->sSocket,pClientInfo->sAcceptSocket,
pClientInfo->m_szBuf,,
sizeof(sockaddr_in) + ,sizeof(sockaddr_in) + ,&dwByteRecieved,
&pClientInfo->m_ol);
if(nRet || ::WSAGetLastError() == ERROR_IO_PENDING)
{
return TRUE;
}
else
{
AfxMessageBox(_T("出错非常严重!PostAccept")); delete pClientInfo;
return FALSE;
}
} void CServerDlg::PostRecv(CClientInfo * pClientInfo)
{
OutputDebugStringA("PostRecv!\n");
WSABUF pBuf;
pBuf.buf = pClientInfo->m_szBuf;
pBuf.len = ;
DWORD cbRecv = ;
DWORD dwFlag = ;
int nRet = WSARecv(pClientInfo->sSocket,&pBuf,,&cbRecv,&dwFlag,&pClientInfo->m_ol,NULL);
if(nRet != )
{
int nError = WSAGetLastError();
}
else
{
m_RecvMsgListBox.AddString((LPCTSTR)pBuf.buf);
}
}
UINT AFX_CDECL AcceptThreadProc(LPVOID p)
{
CServerDlg * pThis = (CServerDlg*)p;
while(true)
{
sockaddr_in saClient = {};
int nClientSocketLen = sizeof(saClient);
SOCKET sClientSocket = accept(pThis->m_ServerSocket,(sockaddr *)&saClient,&nClientSocketLen);
CClientInfo * pClientInfo = pThis->OnSocketConnected(sClientSocket,&saClient);
pThis->PostRecv(pClientInfo);
Sleep();
}
return ;
} UINT AFX_CDECL WorkThreadProc(LPVOID p)
{
CServerDlg * pThis = (CServerDlg*)p;
while (true)
{
pThis->LockClientArray();
int nClientCount = pThis->m_ClientArray.GetCount();
WSAEVENT * pEvent = NULL;
if(nClientCount > )
{
pEvent = new WSAEVENT[nClientCount];
for(int i = ;i < nClientCount;i++)
pEvent[i] = pThis->m_ClientArray.GetAt(i)->m_ol.hEvent;
}
pThis->UnLockClientArray();
if(pEvent == NULL)
{
Sleep();
continue;
}
DWORD dwWaitRet = ::WaitForMultipleObjects(nClientCount,pEvent,FALSE,);
if(dwWaitRet == WAIT_FAILED)
{ }
else if(dwWaitRet == WAIT_TIMEOUT)
{ }
else
{
pThis->LockClientArray();
CClientInfo * pClientInfo = pThis->m_ClientArray.GetAt(dwWaitRet - WAIT_OBJECT_0);
WSAResetEvent(pClientInfo->m_ol.hEvent);
char szDbg[] = {};
sprintf_s(szDbg,"OverLapped:Internal:0x%x,InternalHigh:%d,Offset:%d,OffsetHigh:%d\n",
pClientInfo->m_ol.Internal,
pClientInfo->m_ol.InternalHigh,//发了多少字节
pClientInfo->m_ol.Offset,
pClientInfo->m_ol.OffsetHigh);
OutputDebugStringA(szDbg);
switch(pClientInfo->m_IO_type)
{
case IO_RECV:
if(pClientInfo->m_ol.InternalHigh == )
{
//断开连接
pThis->OnSocketDisconnect(pClientInfo->sSocket);
}
else
{
pClientInfo->m_szBuf[pClientInfo->m_ol.InternalHigh] = ;
pThis->m_RecvMsgListBox.AddString((LPCTSTR)pClientInfo->m_szBuf);
pThis->PostRecv(pClientInfo);
}
break;
case IO_ACCEPT:
setsockopt(pClientInfo->sAcceptSocket,
SOL_SOCKET,
SO_UPDATE_ACCEPT_CONTEXT,
(char*)&pClientInfo->sAcceptSocket,
sizeof(pClientInfo->sAcceptSocket));
CClientInfo * pNewClientInfo = pThis->OnSocketConnected(pClientInfo->sAcceptSocket,NULL);
pThis->PostRecv(pNewClientInfo);
pThis->PostAccept(pClientInfo);
break;
} pThis->UnLockClientArray(); } delete [] pEvent;
}
} void CServerDlg::OnBnClickedButton1()
{
//启动服务端
int nPort = GetDlgItemInt(IDC_EDIT_PORT);
// TODO: 在此添加控件通知处理程序代码
m_ServerSocket = WSASocket(AF_INET,
SOCK_STREAM,
IPPROTO_TCP,
NULL,,WSA_FLAG_OVERLAPPED);
if(m_ServerSocket == INVALID_SOCKET)
{
AfxMessageBox(_T("创建套接字失败"));
return ;
}
sockaddr_in saServer;
saServer.sin_family = AF_INET; //地址家族
saServer.sin_port = htons(nPort); //注意转化为网络节序
saServer.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
if(SOCKET_ERROR == bind(m_ServerSocket,(SOCKADDR *)&saServer,sizeof(saServer)))
{
AfxMessageBox(_T("绑定失败"));
return ;
}
if(SOCKET_ERROR == listen(m_ServerSocket,SOMAXCONN))
{
AfxMessageBox(_T("监听失败啊"));
return ;
} //AfxBeginThread(AcceptThreadProc,this);
PostAccept(m_ServerSocket);
AfxBeginThread(WorkThreadProc,this);
GetDlgItem(IDC_BUTTON1)->EnableWindow(FALSE);
} void CServerDlg::InitSocket()
{
WSADATA wsaData = {};
if( != WSAStartup(MAKEWORD(,),&wsaData))
{
AfxMessageBox(_T("socket 初始化失败"));
return ;
}
} void CServerDlg::OnBnClickedButton3()
{
// TODO: 在此添加控件通知处理程序代码
if(m_ClientListBox.GetSelCount() <= )
{
AfxMessageBox(_T("请选中右边的客户端进行发送"));
return ;
}
CString strSend;
GetDlgItemText(IDC_EDIT2,strSend);
for(int i = ;i < m_ClientListBox.GetCount();i++)
{
if(m_ClientListBox.GetSel(i) > )
{
SOCKET aClientSocket = m_ClientListBox.GetItemData(i);
if( SOCKET_ERROR == send(aClientSocket,(const char * )strSend.GetBuffer(),strSend.GetLength() * sizeof(TCHAR),))
{
int nError = ::WSAGetLastError();
CString strError;
strError.Format(_T("send 失败%d"),nError);
AfxMessageBox(strError);
}
}
}
}
Socket重叠IO的更多相关文章
- winSocket编程(九)重叠IO
重叠模型的优点 重叠模型的基本原理 关于重叠模型的基础知识 重叠模型的实现步骤 多客户端情况的注意事项 一.重叠模型的优点 1.可以运行在支持Winsock2的所有Windows平台 ,而不像完成端口 ...
- 重叠IO
一. 异步IO 说到重叠模型首先还是提一下异步IO比较好,因为从本质上讲,重叠模型也是一种异步IO模型. 我们知道,相对于计算机执行的其他操作而言,设备IO(文件.管道.套接 ...
- 重叠io操作
第一章 一. 重叠模型的优点 1. 可以运行在支持Winsock2的所有Windows平台 ,而不像完成端口只是支持NT系统. 2. 比起阻塞.select.WSAAsyncSelect以及WSAEv ...
- WinSock 重叠IO模型
title: WinSock 重叠IO模型 tags: [WinSock 模型, 网络编程, 重叠IO模型] date: 2018-06-29 20:26:13 categories: Windows ...
- windows网络模型之重叠IO的使用
大部分内容转载自https://blog.csdn.net/piggyxp/article/details/114883 目录: 1. 重叠模型的优点 2. 重叠模型的基本原理 3. 关于重叠模型的基 ...
- 四.Windows I/O模型之重叠IO(overlapped)模型
1.适用于除Windows CE之外的各种Windows平台.在使用这个模型之前应该确保该系统安装了Winsock2.重叠模型的基本设计原理是使用一个重叠的数据结构,一次投递一个或多个Winsock ...
- 重叠IO 模型
1. 重叠模型的优点 2. 重叠模型的基本原理 3. 关于重叠模型的基础知识 4. 重叠模型的实现步骤 5. 多客户端情况的注意事项 一.重叠模型的优点 1.可以运行在支持Winsock2的所有W ...
- socket的IO多路复用
IO 多路复用 I/O多路复用指:通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作. Linux Linux中的 select,poll, ...
- PHP实现网络Socket及IO多路复用
一直以来,PHP很少用于socket编程,毕竟是一门脚本语言,效率会成为很大的瓶颈,但是不能说PHP就无法用于socket编程,也不能说PHP的socket编程性能就有多么的低,例如知名的一款PHP ...
随机推荐
- 夺命雷公狗---DEDECMS----16dedecms取出首页今日更新
我们这次就要来取出我们的电影和电视剧以及综艺节目: 我们首先在我们受页面的模版文件中获取电影和电视剧的标签: 我们发现这里有一大堆,我只留一个即可: 然后我们到后台更新下首页的模版,看下是否只有一个模 ...
- MYSQL日期类型的加减更新使用INTERVAL 1 DAY
例如:UPDATE teachingplan SET teachPlanBeginTime = teachPlanBeginTime +INTERVAL 1 DAY
- 开源软件free download manager在windows defender中报毒
从官网上下载的fdm lite 3.9.6,从图片中可以看出安装包有数字签名,windows defender报毒,在线杀毒也检出木马,官网的程序更新到了3.9.6版本,在sourceforge上的源 ...
- 《zw版·Halcon-delphi系列原创教程》航母舰载机·视觉定位标志的识别代码
<zw版·Halcon-delphi系列原创教程>航母舰载机·视觉定位标志的识别代码 航母舰载机机身上的黄黑圆圈的标志是什么意思,辐射?核动力?战术核弹? <百度百科>介绍如下 ...
- Objective-C代码的文件扩展名与数据类型
Objective-C数据类型可以分为:基本数据类型.对象类型和id类型. 基本数据类型有:int.float.double和char类型. 对象类型就是类或协议所声明的指针类型,例如:SAutore ...
- 使用Application Loader打包上传AppStore流程
配置完你的证书,Bundle Identifier 和描述文件的配置 然后配置工程打开你项目工程 第一步,这里不能选择模拟器,选择iOS Device 如果不支持横屏,把这2个勾去掉 然后查看版本号和 ...
- 分页实体类:PageBean
package com.eaju.soms.entity.custom; import java.util.List; @SuppressWarnings("rawtypes")p ...
- jQuery中attr() 和 prop()【转】
Version 1.9.0 开始不建议使用 attr() 来对具有 true 和 false 两个属性的属性进行操作. 具有 true 和 false 两个属性的属性,如 checked, selec ...
- struts2的两个核心配置文件
struts2的两个核心配置文件,即:struts.default.xml和struts.properties A,位置:都在struts2-core-version.jar文件中 B,作用,stru ...
- setw和setfill控制输出间隔
在C++中,setw(int n)用来控制输出间隔.例如:cout<<'s'<<setw(8)<<'a'<<endl;则在屏幕显示s a //s与a之间 ...