在工控上經常用到tcp連接,比如串口服務器或某些支持modbustcp協(xié)議的儀表等,以前盡量使用串口服務器的虛擬串口功能,現(xiàn)在逐步使用上了tcpserver或tcpclient模式。
搜索了個C++ 的tcp斷線重連的案例(http://www.cnblogs.com/kingdom_0/articles/2571727.html),使用這個的原因還因其使用的是收發(fā)多線程。server和client都很全,也許是作者的疏忽,client出現(xiàn)了明顯的bug。如果掉線了,client的send和recv將重新建兩個socket。
所以send和recv兩個線程中的socket必須以指針形式傳入,其次關閉socket不能用shutdown。經改進,目前已實現(xiàn)比較完美的斷線(斷開服務器程序和拔掉網(wǎng)線方式測試)自動連接功能。
完整client代碼cpp文件如下:
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 6100
#define IP_ADDRESS "127.0.0.1"
#include "ClientTcp.h"
#include "ThreadLock.h"http://線程自動鎖 ThreadLock.h
WSADATA Ws;
SOCKET ClientSocket;
struct sockaddr_in ServerAddr;
int Ret = 0;
HANDLE hSendThread = NULL;
HANDLE hRevcThread = NULL;
//發(fā)送消息結構體
struct SendMsgStruct
{
SOCKET* clientSocket;
string msg;
struct sockaddr_in ServerAddr;
};
//接收消息結構體
struct RecvMsgStruct
{
SOCKET* clientSocket;
struct sockaddr_in ServerAddr;
};
DWORD WINAPI SendThread(LPVOID lpParameter);//發(fā)送消息子線程
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;
}
//創(chuàng)建 Socket
ClientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (ClientSocket == INVALID_SOCKET)
{
cout << "創(chuàng)建 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 << "建立連接過程發(fā)生錯誤:" << GetLastError() << endl;
}
else
{
cout << "連接建立成功" << endl;
}
//創(chuàng)建一個子線程,用于向服務器端發(fā)送消息
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 << "創(chuàng)建發(fā)送消息子線程失敗" << endl;
system("pause");
return -1;
}
//創(chuàng)建一個子線程,用于接收從服務器端發(fā)送過來的消息
struct RecvMsgStruct* msgRecv = new struct RecvMsgStruct();
msgRecv->clientSocket = &ClientSocket;
msgRecv->ServerAddr = ServerAddr;
//傳遞一個struct指針參數(shù)
hRevcThread = CreateThread(NULL, 0, RecvThread, (LPVOID)msgRecv, 0, NULL);
//WaitForSingleObject(hRevcThread, INFINITE);
if (hRevcThread == NULL)
{
cout << "創(chuàng)建接收消息子線程失敗" << endl;
system("pause");
return -1;
}
//客戶端輸入exit,退出
/* string clientString;
do
{
getline(cin, clientString);
} while (clientString != "exit" && clientString != "EXIT");*/
closesocket(ClientSocket);
WSACleanup();
}
//發(fā)送消息子線程
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秒發(fā)送一次
}
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 << "建立連接發(fā)生錯誤,錯誤代碼:" << GetLastError() << endl;
}
else
{
cout << "重連成功!!!!!!!" << endl;
break;
}
Sleep(5000);
}
cout << recvBuf << endl;
}
return 0;
}
H頭文件中代碼如下:
#pragma once
#include <string>
#include <winsock2.h>
#include <thread>
#pragma comment(lib,"ws2_32")//Standard socket API.
#include "sendByte.h"http://發(fā)送屬性實體(根據(jù)需求自定義變量即可)
#include "TcpDatas.h"http://接收屬性實體(根據(jù)需求自定義變量即可)
#include "Totype.h"http://類型轉化函數(shù)類
class ClientTcp
{
public:
ClientTcp(std::string strIp, unsigned int uPort);
virtual ~ClientTcp();
//初始化網(wǎng)絡服務端
bool InitClient();
private:
unsigned int m_uPort;//監(jiān)聽端口
std::string m_strIp;//用于監(jiān)聽本機指定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;文章來源:http://www.zghlxwxcb.cn/news/detail-693302.html
本文主要參考原文鏈接:https://blog.csdn.net/gongzhu110/article/details/83147994文章來源地址http://www.zghlxwxcb.cn/news/detail-693302.html
到了這里,關于C++ TCP/IP 關于tcp斷線重連的問題的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!