在工控上经常用到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,退出

/* string clientString;

do

{

getline(cin, clientString);

} while (clientString != "exit" && clientString != "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

精彩文章

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: