Socket重叠IO

来源:互联网 发布:mac禁用sleepimage 编辑:程序博客网 时间:2024/06/07 05:23

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,256);        m_ol.hEvent = WSACreateEvent();    }    ~CClientInfo()    {        WSACloseEvent(m_ol.hEvent);    }    WSAOVERLAPPED m_ol;    SOCKET sSocket;    CString strIp;    u_short nPort;    CString GetShowText();    char m_szBuf[256];};
复制代码

 

下面是两个函数,一个是当客户端连接的时候,一个是当客户端断开的时候

复制代码
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 = 0;i<m_ClientArray.GetCount();i++)    {        CClientInfo * pClientInfo = m_ClientArray.GetAt(i);        if(pClientInfo->sSocket == aClientSocket)        {            m_ClientListBox.DeleteString(m_ClientListBox.FindString(0,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.源代码

源码下载 vs2012项目

QQ截图20151003012131

.h文件

复制代码
// ServerDlg.h : 头文件//#pragma once#include "afxwin.h"#include <Winsock2.h>#pragma comment(lib,"Ws2_32.lib")#define WM_SOCKET WM_USER+220enum IO_TYPE{    IO_RECV,    IO_ACCEPT,    IO_UNKNOW};// CServerDlg 对话框struct CClientInfo{public:    CClientInfo()     {        ZeroMemory(&m_ol,sizeof(m_ol));        ZeroMemory(m_szBuf,256);        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[256];    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,10103);    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()), 0);        // 使图标在工作区矩形中居中        int cxIcon = GetSystemMetrics(SM_CXICON);        int cyIcon = GetSystemMetrics(SM_CYICON);        CRect rect;        GetClientRect(&rect);        int x = (rect.Width() - cxIcon + 1) / 2;        int y = (rect.Height() - cyIcon + 1) / 2;        // 绘制图标        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 = 0;i<m_ClientArray.GetCount();i++)    {        CClientInfo * pClientInfo = m_ClientArray.GetAt(i);        if(pClientInfo->sSocket == aClientSocket)        {            m_ClientListBox.DeleteString(m_ClientListBox.FindString(0,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,0,WSA_FLAG_OVERLAPPED);    pClientInfo->sSocket = sListenSocket;    pClientInfo->m_IO_type = IO_ACCEPT;    pClientInfo->sAcceptSocket = sClientSocket;    DWORD dwByteRecieved = 0;    int nRet = AcceptEx(pClientInfo->sSocket,pClientInfo->sAcceptSocket,        pClientInfo->m_szBuf,0,        sizeof(sockaddr_in) + 16,sizeof(sockaddr_in) + 16,&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,0,WSA_FLAG_OVERLAPPED);    pClientInfo->m_IO_type = IO_ACCEPT;    pClientInfo->sAcceptSocket = sClientSocket;    DWORD dwByteRecieved = 0;    int nRet = AcceptEx(pClientInfo->sSocket,pClientInfo->sAcceptSocket,        pClientInfo->m_szBuf,0,        sizeof(sockaddr_in) + 16,sizeof(sockaddr_in) + 16,&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 = 256;    DWORD cbRecv = 0;    DWORD dwFlag = 0;    int nRet = WSARecv(pClientInfo->sSocket,&pBuf,1,&cbRecv,&dwFlag,&pClientInfo->m_ol,NULL);    if(nRet != 0)    {        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 = {0};        int  nClientSocketLen = sizeof(saClient);        SOCKET sClientSocket = accept(pThis->m_ServerSocket,(sockaddr *)&saClient,&nClientSocketLen);        CClientInfo * pClientInfo = pThis->OnSocketConnected(sClientSocket,&saClient);        pThis->PostRecv(pClientInfo);        Sleep(500);    }    return 0;}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 > 0)        {            pEvent = new WSAEVENT[nClientCount];            for(int i = 0;i < nClientCount;i++)                pEvent[i] = pThis->m_ClientArray.GetAt(i)->m_ol.hEvent;        }        pThis->UnLockClientArray();        if(pEvent == NULL)        {            Sleep(1000);            continue;        }        DWORD dwWaitRet = ::WaitForMultipleObjects(nClientCount,pEvent,FALSE,10000);        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[256] = {0};            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 == 0)                {                    //断开连接                     pThis->OnSocketDisconnect(pClientInfo->sSocket);                }                else                {                    pClientInfo->m_szBuf[pClientInfo->m_ol.InternalHigh] = 0;                    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,0,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 = {0};    if(0 != WSAStartup(MAKEWORD(2,2),&wsaData))    {        AfxMessageBox(_T("socket 初始化失败"));        return ;    }}void CServerDlg::OnBnClickedButton3(){    // TODO: 在此添加控件通知处理程序代码    if(m_ClientListBox.GetSelCount() <= 0)    {        AfxMessageBox(_T("请选中右边的客户端进行发送"));        return ;    }    CString strSend;    GetDlgItemText(IDC_EDIT2,strSend);    for(int i = 0;i < m_ClientListBox.GetCount();i++)    {        if(m_ClientListBox.GetSel(i) > 0)        {            SOCKET aClientSocket = m_ClientListBox.GetItemData(i);            if( SOCKET_ERROR == send(aClientSocket,(const char * )strSend.GetBuffer(),strSend.GetLength() * sizeof(TCHAR),0))            {                int nError = ::WSAGetLastError();                CString strError;                strError.Format(_T("send  失败%d"),nError);                AfxMessageBox(strError);            }        }    }}
复制代码
出处:http://www.cnblogs.com/zhangdongsheng/
作者:张东升 QQ:290387340
0 0
原创粉丝点击