文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

C++ TCP/IP 关于tcp断线重连的问题

2023-09-18 06:16

关注

在工控上经常用到tcp连接,比如串口服务器或某些支持modbustcp协议的仪表等,以前尽量使用串口服务器的虚拟串口功能,现在逐步使用上了tcpserver或tcpclient模式。

搜索了个C++ 的tcp断线重连的案例(http://www.cnblogs.com/kingdom_0/articles/2571727.html),使用这个的原因还因其使用的是收发多线程。server和client都很全,也许是作者的疏忽,client出现了明显的bug。如果掉线了,client的send和recv将重新建两个socket。

所以send和recv两个线程中的socket必须以指针形式传入,其次关闭socket不能用shutdown。经改进,目前已实现比较完美的断线(断开服务器程序和拔掉网线方式测试)自动连接功能。

完整client代码cpp文件如下:

include #include #include #include #include #include #include #pragma comment(lib,"Ws2_32.lib")using namespace std;#define PORT 6100#define IP_ADDRESS "127.0.0.1"#include "ClientTcp.h"#include "ThreadLock.h"//线程自动锁 ThreadLock.h WSADATA  Ws;SOCKET ClientSocket;struct sockaddr_in ServerAddr;int Ret = 0;HANDLE hSendThread = NULL;HANDLE hRevcThread = NULL;//发送消息结构体struct SendMsgStruct{    SOCKET* clientSocket;    string msg;    struct sockaddr_in ServerAddr;};//接收消息结构体struct RecvMsgStruct{    SOCKET* clientSocket;    struct sockaddr_in ServerAddr;};DWORD WINAPI SendThread(LPVOID lpParameter);//发送消息子线程DWORD WINAPI RecvThread(LPVOID lpParameter);//接收消息子线程ClientTcp::ClientTcp(std::string strIp, unsigned int uPort) :    m_strIp(strIp),    m_uPort(uPort){}ClientTcp::~ClientTcp(){       if (ClientSocket)       {           closesocket(ClientSocket);           ClientSocket = NULL;       }}bool ClientTcp::InitClient(){    //初始化 Windows Socket    if (WSAStartup(MAKEWORD(2, 2), &Ws) != 0)    {       std::cout << "初始化 Socket 失败:" << GetLastError() << endl;        return -1;    }    //创建 Socket    ClientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);    if (ClientSocket == INVALID_SOCKET)    {        cout << "创建 Socket 失败:" << GetLastError() << endl;        return -1;    }    ServerAddr.sin_family = AF_INET;    ServerAddr.sin_addr.s_addr = inet_addr(IP_ADDRESS);    ServerAddr.sin_port = htons(PORT);    //设置ServerAddr中前8个字符为0x00    memset(ServerAddr.sin_zero, 0x00, 8);    Ret = connect(ClientSocket, (struct sockaddr*)&ServerAddr, sizeof(ServerAddr));    if (Ret == SOCKET_ERROR)    {        cout << "建立连接过程发生错误:" << GetLastError() << endl;    }    else    {        cout << "连接建立成功" << endl;    }    //创建一个子线程,用于向服务器端发送消息    struct SendMsgStruct* msgSend = new struct SendMsgStruct();    msgSend->clientSocket = &ClientSocket;    msgSend->msg = "你好,Msg From Client";    msgSend->ServerAddr = ServerAddr;    //传递一个struct    hSendThread = CreateThread(NULL, 0, SendThread, (LPVOID)msgSend, 0, NULL);    //WaitForSingleObject(hSendThread, INFINITE);    if (hSendThread == NULL)    {        cout << "创建发送消息子线程失败" << endl;        system("pause");        return -1;    }    //创建一个子线程,用于接收从服务器端发送过来的消息    struct RecvMsgStruct* msgRecv = new struct RecvMsgStruct();    msgRecv->clientSocket = &ClientSocket;    msgRecv->ServerAddr = ServerAddr;    //传递一个struct指针参数    hRevcThread = CreateThread(NULL, 0, RecvThread, (LPVOID)msgRecv, 0, NULL);    //WaitForSingleObject(hRevcThread, INFINITE);    if (hRevcThread == NULL)    {        cout << "创建接收消息子线程失败" << endl;        system("pause");        return -1;    }    //客户端输入exit,退出       closesocket(ClientSocket);    WSACleanup();}//发送消息子线程DWORD WINAPI SendThread(LPVOID lpParameter){    SendMsgStruct* myStruct = (SendMsgStruct*)lpParameter;    SOCKET* ClientSocket = myStruct->clientSocket;    string SendMsg = myStruct->msg;    struct sockaddr_in ServerAddr = myStruct->ServerAddr;    while (true)    {        int flag = 0;        int bufSize = SendMsg.length();        char* buf = const_cast(SendMsg.c_str());        {            CAutoLock ALock(&ctLock);            flag = send(*ClientSocket, buf, bufSize, 0);            //判断当前时候存在可用连接,如果没有,再次连接            while (flag == SOCKET_ERROR || flag == 0)            {                cout << "准备重连" << endl;                closesocket(*ClientSocket);                *ClientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);                if (connect(*ClientSocket, (struct sockaddr*)&ServerAddr, sizeof(ServerAddr)) == SOCKET_ERROR)                {                    cout << "重连失败 :" << GetLastError() << endl;                    Sleep(5000);                }                else                {                    break;                }            }            if (flag < bufSize)            {                flag = send(*ClientSocket, buf, bufSize - flag, 0);            }            else    //传输成功            {                cout << "\n消息传输成功" << endl;            }        }        Sleep(2000);       //每2秒发送一次    }    return 0;}//接收消息子线程DWORD WINAPI RecvThread(LPVOID lpParameter){    RecvMsgStruct* recvStruct = (RecvMsgStruct*)lpParameter;    SOCKET* ClientSocket = recvStruct->clientSocket;    struct sockaddr_in ServerAddr = recvStruct->ServerAddr;    while (true)    {        char recvBuf[500] = { "0" };        int byteRecv = recv(*ClientSocket, recvBuf, 500, 0);        CAutoLock ALock(&ctLock);        int connectState;        while (byteRecv == 0 || byteRecv == SOCKET_ERROR)        {            //连接断开,重连            cout << "byteRecv <= 0" << endl;             closesocket(*ClientSocket);            *ClientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);            connectState = connect(*ClientSocket, (struct sockaddr*)&ServerAddr, sizeof(ServerAddr));            if (connectState == SOCKET_ERROR)            {                cout << "建立连接发生错误,错误代码:" << GetLastError() << endl;            }            else            {                cout << "重连成功!!!!!!!" << endl;                break;            }            Sleep(5000);        }        cout << recvBuf << endl;    }    return 0;}

H头文件中代码如下:

#pragma once#include #include #include #pragma comment(lib,"ws2_32")//Standard socket API.#include "sendByte.h"//发送属性实体(根据需求自定义变量即可)#include "TcpDatas.h"//接收属性实体(根据需求自定义变量即可)#include "Totype.h"//类型转化函数类class ClientTcp{public:    ClientTcp(std::string strIp, unsigned int uPort);    virtual ~ClientTcp();    //初始化网络服务端    bool InitClient(); private:    unsigned int m_uPort;//监听端口    std::string m_strIp;//用于监听本机指定IP地址  }; 

入口文件中调用:

void TcpClientRun(){    ClientTcp clienttcp("192.168.124.3", 6100);    if (!clienttcp.InitClient())    {        getchar();    }}int main(int argc, char* argv[]){    std::thread CTcpTh(TcpClientRun);    this_thread::sleep_for(std::chrono::milliseconds(1000));    CTcpTh.join();    return 0;  }

这样就可以了 

线程自动锁 ThreadLock.h 选用https://www.cnblogs.com/pilipalajun/p/5415673.html

本文主要参考原文链接:https://blog.csdn.net/gongzhu110/article/details/83147994

来源地址:https://blog.csdn.net/qiuyuanxiang1230/article/details/130487710

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯