• 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,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+220
    
    
    enum 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!
    ");
            }
            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!
    ");
        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!
    ");
        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!
    ");
        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
    ",
                    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);
                }
            }
        }
    }
  • 相关阅读:
    Linux中touch和mkdir、vi的区别
    宿主机和虚拟机的IP地址和端口号的区别
    测试环境
    Fiddler
    Linux 常用指令
    测试环境的网址与账号密码
    书签
    快速寻找满足条件的两个数
    android 资讯阅读器(二)
    android 资讯阅读器
  • 原文地址:https://www.cnblogs.com/zhangdongsheng/p/4850533.html
Copyright © 2020-2023  润新知