关于tcp断线重连的问题

2 篇文章 1 订阅
1 篇文章 1 订阅

在工控上经常用到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代码修改如下:

#define  WIN32
#include <iostream>
#include <Winsock2.h>
#include <Windows.h>
#include <fstream>
#include <map>
#include <string>
#include <sstream>
#pragma comment(lib,"Ws2_32.lib")
using namespace std;
#define PORT 12345
#define IP_ADDRESS "192.168.1.5"
#include "ThreadLock.h"

//#define LISTEN_PORT 8081
//#define LISTEN_IP_ADDRESS "172.16.20.181"
//发送消息结构体
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);//接收消息子线程


CThreadLock ctLock;

//---------------------------------------------------------------------------

#pragma argsused
int main(int argc, char *argv[])
{
    WSADATA  Ws;
    SOCKET ClientSocket, ServerSocket;
    struct sockaddr_in ServerAddr;
    int Ret = 0;

    HANDLE hSendThread = NULL;
    HANDLE hRevcThread = NULL;

    //The WSAStartup function initiates use of WS2_32.DLL by a process.
    //初始化 Windows Socket
    if ( WSAStartup(MAKEWORD(2, 2), &Ws) != 0 )
    {
        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,退出
    string  clientString ;
    do
    {
        getline(cin, clientString);

    }
    while( clientString != "exit" && clientString != "EXIT");
    closesocket(ClientSocket);
    WSACleanup();
    system("pause");
    return 0;
}
//---------------------------------------------------------------------------

//发送消息子线程
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<char *>(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;
            //shutdown(clientSocket, 2);
            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;
}

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

  • 2
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值