博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[C++]-Windows下Socket连接之客户端
阅读量:2055 次
发布时间:2019-04-28

本文共 4595 字,大约阅读时间需要 15 分钟。

连接

接收

发送

 

在《》中介绍了Windows下Socket编程的一些基本知识与服务端实现,现在介绍一下客户端的实现。相比于服务端,客户端流程相对简单些,主要就是:

  • 连接服务端;

  • 收发消息

此客户端实现,除发送接口外,其他的都使用IOCP(I/O Completion Port,I/O完成端口)接口WSAXXX。IOCP是性能良好的I/O模型,可以支持大并发(通过完成端口,避免大量线程的创建),更适合在服务端使用。

等待服务端应答及退出事件,都是通过C++条件变量实现的(参见《》)

XuEvent g_evtQuit;XuEvent g_evtAck;

连接

与服务端相似,在使用socket前,需要先通过initSocket初始化,并在退出前做清理工作。

连接时,先设定要连接的地址与端口,然后发起连接,成功后进行收发处理即可。

SOCKET openConnection(const string &strHost, unsigned short nPort,    function
 funError,    function
 funMsg) {    string strError;    SOCKADDR_IN sockAddr;    SOCKET sockClient = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);    if (INVALID_SOCKET == sockClient) {        goto FUN_CLEANUP;    }    // to connect    sockAddr.sin_family = AF_INET;    sockAddr.sin_port = htons(nPort);    inet_pton(AF_INET, strHost.c_str(), &(sockAddr.sin_addr));    if (SOCKET_ERROR == WSAConnect(sockClient, (SOCKADDR*)&sockAddr, sizeof(sockAddr), nullptr, nullptr, nullptr, nullptr)) {        goto FUN_CLEANUP;    }    g_bStop = false;    {        thread thrRecv(receiveMsg, sockClient, funError, funMsg);        thrRecv.detach();        thread thrAlive(heartBeat, sockClient, funError);        thrAlive.detach();    }    return sockClient;FUN_CLEANUP:    int nError = WSAGetLastError();    if (nullptr != funError) {        funError(strError, nError);    }    return INVALID_SOCKET;}

接收

接收消息使用的完成端口WSARecv实现的,接受者通过通过事件等待,在有消息到达时系统会主动通知。

接收返回WSA_IO_PENDING错误时,不是真正的错误,只是告诉上层当前未收到消息,需要等待,在有消息到达时会触发通知。

void receiveMsg(SOCKET sock,    function
 funError,    function
 funMsg) {    int nPort = 0;    string strAddr = XuNet::peerAddress(sock, &nPort);    cout << "Connected " << strAddr << ":" << nPort << endl;    string strError;    const int MaxBufSize = 255;    char szBuff[MaxBufSize + 1] = { 0 };    int nRet = 0, nRecvError = 0;    WSABUF wsaBuf;    WSAOVERLAPPED overLapped;    ZeroMemory(&overLapped, sizeof(overLapped));    overLapped.hEvent = WSACreateEvent();    if(NULL == overLapped.hEvent){        nRecvError = WSAGetLastError();        strError = "WSACreateEvent";        goto FUN_CLEANUP;    }    wsaBuf.buf = szBuff;    wsaBuf.len = MaxBufSize;    while (!g_bStop) {        DWORD dwFlag = 0;        DWORD dwRecved = 0;        nRet = WSARecv(sock, &wsaBuf, 1, &dwRecved, &dwFlag, &overLapped, NULL);        if (SOCKET_ERROR == nRet) {            nRecvError = WSAGetLastError();            if (WSA_IO_PENDING != nRecvError) {                strError = "WSARecv";                goto FUN_CLEANUP;            }            nRecvError = 0;        }        nRet = WSAWaitForMultipleEvents(1, &overLapped.hEvent, TRUE, INFINITE, FALSE);        if (WSA_WAIT_FAILED == nRet) {            nRecvError = WSAGetLastError();            strError = "WSAWaitForMultipleEvents";            goto FUN_CLEANUP;        }        nRet = WSAGetOverlappedResult(sock, &overLapped, &dwRecved, FALSE, &dwFlag);        if(FALSE == nRet){            nRecvError = WSAGetLastError();            strError = "WSAGetOverlappedResult";            goto FUN_CLEANUP;        }        if (dwRecved > 0) {            string strMsg(szBuff, dwRecved);            if (strMsg.substr(0, 3) == MSG_Ack) {                g_evtAck.notifyAll();            }                       else if (nullptr != funMsg) {                funMsg(strMsg);            }        }        WSAResetEvent(overLapped.hEvent);    }FUN_CLEANUP:    WSACloseEvent(overLapped.hEvent);    if (nullptr != funError) {        funError(strError, nRecvError);    }}

发送

通过定时向服务端发送心跳包,及时发现连接问题(服务端会根据心跳,做超时处理)。发送完消息后,会等待服务端做一个应答,只有接收到服务端应答后才会真正确认发送成功。

bool sendMessage(SOCKET sock, string strMsg, int nWaitSecs) {    strMsg += MSG_Delim;    g_evtAck.reset();    int nLen = send(sock, strMsg.c_str(), strMsg.length(), 0);    if (SOCKET_ERROR == nLen) {        int nError = WSAGetLastError();        cout << "!!!Send " << strMsg << " fail: " << nError << endl;        return false;    }    return g_evtAck.wait(nWaitSecs);}

心跳包:

void heartBeat(SOCKET sock, function
 funError) {    int nLostAck = 0;    int nCount = 0;    while (!g_bStop) {        if (sendMessage(sock, MSG_Alive + std::to_string(++nCount), 1)) {            nLostAck = 0;        }        else {            if (++nLostAck > HeartBeat_MaxLost) {                if (nullptr != funError) {                    funError("too many Ack of heartBeat lost", -1);                }                break;            }        }        if (g_evtQuit.wait(HeartBeat_Seconds)) {            cout << "evtQuit got signal" << endl;            break;        }    }    closesocket(sock);}

 

转载地址:http://jgnlf.baihongyu.com/

你可能感兴趣的文章
TCP为什么是三次握手,为什么不是两次或者四次 && TCP四次挥手
查看>>
C结构体、C++结构体、C++类的区别
查看>>
进程和线程的概念、区别和联系
查看>>
CMake 入门实战
查看>>
绑定CPU逻辑核心的利器——taskset
查看>>
Linux下perf性能测试火焰图只显示函数地址不显示函数名的问题
查看>>
c结构体、c++结构体和c++类的区别以及错误纠正
查看>>
Linux下查看根目录各文件内存占用情况
查看>>
A星算法详解(个人认为最详细,最通俗易懂的一个版本)
查看>>
利用栈实现DFS
查看>>
逆序对的数量(递归+归并思想)
查看>>
数的范围(二分查找上下界)
查看>>
算法导论阅读顺序
查看>>
Windows程序设计:直线绘制
查看>>
linux之CentOS下文件解压方式
查看>>
Django字段的创建并连接MYSQL
查看>>
div标签布局的使用
查看>>
HTML中表格的使用
查看>>
(模板 重要)Tarjan算法解决LCA问题(PAT 1151 LCA in a Binary Tree)
查看>>
(PAT 1154) Vertex Coloring (图的广度优先遍历)
查看>>