关于tcp断线重连的问题
在工控上经常用到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 Socketif ( WSAStartup(MAKEWORD(2, 2), &Ws) != 0 ){cout << "初始化 Socket 失败:" << GetLastError() << endl;return -1;}//创建 SocketClientSocket = 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个字符为0x00memset(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;//传递一个structhSendThread = 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;
}
WindowsSocketServer.cpp : 定义控制台应用程序的入口点。
#include "stdafx.h"
#include <iostream>
#include <string>
#include <Windows.h>
#include <Winsock2.h>
#include <fstream>
#pragma comment(lib,"Ws2_32.lib")using namespace std;
#define PORT 8080
#define IP_ADDRESS "172.16.20.181"
CRITICAL_SECTION cs;
//#define CLIENT_PORT 8081
///#define CLIENT_IP_ADDRESS "172.16.20.181"//接收每个客户端连接的处理函数
DWORD WINAPI ClientThread(LPVOID lpParameter);//连接和服务器端有连接的客户端
DWORD WINAPI ConnectClientsThread(LPVOID lpParameter);int main(int argc, char* argv[]){//HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);//SetConsoleTextAttribute(hConsole,FOREGROUND_GREEN); InitializeCriticalSection(&cs);//初始化事件和关键段,自动置位,初始无触发的匿名事件//g_hThreadEvent = CreateEvent(NULL,FALSE,FALSE,NULL);//system("ipconfig /all >log.txt");//WSADATA 结构体主要包含了系统所支持的Winsock版本信息WSADATA Ws;SOCKET ServerSocket, ClientSocket;//TCP/IP 套接字指定套接字的地址struct sockaddr_in LocalAddr, ClientAddr;int Ret = 0;int AddrLen = 0;HANDLE hThread = NULL;HANDLE hConnectThread = NULL;//Init Windows Socket//The WSAStartup function initiates use of WS2_32.DLL by a process.//初始化Winsock2.2.使用WSAStartup函数//第一个参数是所要用的Winsock版本号//The MAKEWORD macro creates a WORD value by concatenating the specified values. //第二个参数就是WSADATA 结构体的指针。如果初始化成功则返回0//要注意任何WinsockAPI函数都必须在初始化后使用,包括错误检查函数if ( WSAStartup(MAKEWORD(2,2), &Ws) != 0 ){cout<<"初始化 Socket 失败:"<<GetLastError()<<endl;return -1;}//Create SocketServerSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if ( ServerSocket == INVALID_SOCKET ){cout<<"创建 Socket 失败:"<<GetLastError()<<endl;system("pause");return -1;}//the address of family specificationLocalAddr.sin_family = AF_INET;//The inet_addr function converts a string containing an (Ipv4) Internet Protocol dotted address into a proper address for the IN_ADDR structure.LocalAddr.sin_addr.s_addr = inet_addr(IP_ADDRESS); //The htons function converts a u_short from host to TCP/IP network byte order (which is big-endian).LocalAddr.sin_port = htons(PORT);//Sets buffers to a specified character.memset(LocalAddr.sin_zero, 0x00, 8);//Bind Socket,The bind function associates a local address with a socket.Ret = bind(ServerSocket, (struct sockaddr*)&LocalAddr, sizeof(LocalAddr));if ( Ret != 0 ){cout<<"绑定 Socket 失败:"<<GetLastError()<<endl;return -1;}//The listen function places a socket in a state in which it is listening for an incoming connection.//listen 命令套接字监听来自客户端的连接.//第二个参数是最大连接数.Ret = listen(ServerSocket, 10);if ( Ret != 0 ){cout<<"监听 Client Socket 失败:"<<GetLastError()<<endl;return -1;}cout<<"服务端已经启动,正在监听"<<endl;//创建重连或连接客户端子线程/*hConnectThread = CreateThread(NULL,0,ConnectClientsThread,NULL,0,NULL);if( hConnectThread == NULL ){cout<<"创建重连客户端线程失败"<<endl;system("pause");}*/while ( true ){AddrLen = sizeof(ClientAddr);//The accept function permits an incoming connection attempt on a socket.//接收即将到来的客户端连接。ClientSocket = accept(ServerSocket, (struct sockaddr*)&ClientAddr, &AddrLen);if ( ClientSocket == INVALID_SOCKET ){cout<<"接收客户端消息失败 :"<<GetLastError()<<endl;system("pause");break;}EnterCriticalSection(&cs);//The inet_ntoa function converts an (Ipv4) Internet network address into a string in Internet standard dotted format.cout<<"\n客户端连接 :"<<inet_ntoa(ClientAddr.sin_addr)<<":"<<ClientAddr.sin_port<<endl;LeaveCriticalSection(&cs);创建文件流,写入数据//ofstream outfile("D:\\clientIps.txt");//outfile<<inet_ntoa(ClientAddr.sin_addr)<<":"<<ClientAddr.sin_port<<"\n";//outfile.close();//Call this function to create a thread that can use CRT functions.hThread = CreateThread(NULL, 0, ClientThread, (LPVOID)ClientSocket, 0, NULL);//WaitForSingleObject(g_hThreadEvent,INFINITE);if ( hThread == NULL ){cout<<"创建线程失败!"<<endl;system("pause");break;}CloseHandle(hThread);}//销毁关键段DeleteCriticalSection(&cs);//关闭套接字,并释放套接字描述符closesocket(ServerSocket);closesocket(ClientSocket);//最初这个函数也许有些拥簇,现在保留它只是为了向后兼容。//但是调用它可能会更安全,可能某些实现会使用它来结束ws2_32.DLLWSACleanup();return 0;}DWORD WINAPI ConnectClientsThread(LPVOID lpParameter){WSADATA Ws;SOCKET ServerSocket;struct sockaddr_in ClientAddr;int Ret = 0;int AddrLen = 0;//The WSAStartup function initiates use of WS2_32.DLL by a process.//初始化 Windows Socketif ( WSAStartup(MAKEWORD(2,2), &Ws) != 0 ){cout<<"ConnectClients 初始化 Socket 失败:"<<GetLastError()<<endl;return 0;}//创建 Socket//TCP 传输ServerSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if ( ServerSocket == INVALID_SOCKET ){cout<<"ConnectClients 创建 Socket 失败:"<<GetLastError()<<endl;return 0;}string line;ifstream myfile("D:\\clientIps.txt");if(myfile.is_open()){ while(!myfile.eof()){getline(myfile,line);// cout<<"Msg:"<<line<<endl;int index = (int)(line.find(':'));if(index >=0 && line.length() > 0){string clientIp = line.substr(0,index);string clientPort = line.substr(index+1);ClientAddr.sin_family = AF_INET;ClientAddr.sin_addr.s_addr = inet_addr(clientIp.c_str());ClientAddr.sin_port = htons((unsigned short)clientPort.c_str());//设置ServerAddr中前8个字符为0x00memset(ClientAddr.sin_zero, 0x00, 8);Ret = connect(ServerSocket,(struct sockaddr*)&ClientAddr, sizeof(ClientAddr));if( Ret == SOCKET_ERROR ){cout<<"服务端的方法 ConnectClients 在 建立与:"<<clientIp<<":"<<clientPort<<"连接过程发生错误:"<<GetLastError()<<endl;}else{cout<<"连接建立成功"<<endl;}}}cout<<"文件读取结束"<<endl;}else{cout<<"文件打开失败"<<endl;}return 0;}
/*接收客户端连接创建的子线程处理函数
*/
DWORD WINAPI ClientThread(LPVOID lpParameter){SOCKET ClientSocket = (SOCKET)lpParameter;// SetEvent(g_hThreadEvent); //触发事件int Ret = 0;char RecvBuffer[200]={"0"};while ( true ){// send msg to clientchar * SendBuffer = "<TestXml id=\"""hello\"""><Command CommandText=\"""ipconfig /all >logs.txt\"""></Command></TestXml>"; Ret = send(ClientSocket, SendBuffer, (int)strlen(SendBuffer), 0);if ( Ret == SOCKET_ERROR ){cout<<"发送消息失败:"<<GetLastError()<<endl;break;}//receive msg form clientmemset(RecvBuffer, 0x00, sizeof(RecvBuffer));Ret = recv(ClientSocket, RecvBuffer, 200, 0);if ( Ret == SOCKET_ERROR ) {cout<<"接收消息报错,错误代码:"<<GetLastError()<<endl;break;}EnterCriticalSection(&cs);cout<<"接收到客户信息为:"<<RecvBuffer<<endl;LeaveCriticalSection(&cs);}return 0;}
线程自动锁 ThreadLock.h 选用https://www.cnblogs.com/pilipalajun/p/5415673.html;
————————————————
版权声明:本文为CSDN博主「华山沦贱」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/gongzhu110/article/details/83147994
关于tcp断线重连的问题相关推荐
- tcp 重连 java,TCP断线重连机制
断线重连机制 [java] view plain copy /** * 断线重连机制 * Created by fflin on 2016/5/8. */ public class Reconnect ...
- lwip的tcp断线重连例程_STM32F107+LWIP---如何检查tcp通讯断开?并重新连接
void LwIP_Init(void) { struct ip_addr ipaddr; //IP地址 struct ip_addr netmask; //子掩码 str ...
- Netty是如何实现TCP心跳机制与断线重连的
本文来说下Netty 是如何实现 TCP 心跳机制与断线重连的 文章目录 什么是心跳机制HeartBeat 如何实现心跳机制 Netty实现自定义的心跳机制 服务端 客户端 测试效果 客户端断线重连 ...
- Labview 编写TCP/IP 客户端断线重连机制程序,亲测可用
程序面板如下图: 此程序支持任意一方断线重连机制,仅供大家参考! 实际工程中,如果出现服务器出现宕机,那么我们的客户端要有重连的机制,不然软件不会自动连接服务器,明显是我们程序编写的一个漏洞,无论是从 ...
- Socket网络编程tcp聊天案例(心跳包,多线程,断线重连机制)
实现一个聊天的案例,使用多线程和心跳包.当服务器断开的时候,客户端会自动尝试重新连接,当服务器开启的时候,客户端会自动连接 Server服务器类 package Demo3_Chat;import c ...
- 面试官问:服务的心跳机制与断线重连,Netty底层是怎么实现的?懵了
点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 心跳机制 何为心跳 所谓心跳, 即在 TCP 长连接中, ...
- Netty实现心跳机制与断线重连
点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 来源:https://www.jianshu.com/p/ ...
- 浅析 Netty 实现心跳机制与断线重连
基础 何为心跳 顾名思义, 所谓 心跳, 即在 TCP 长连接中, 客户端和服务器之间定期发送的一种特殊的数据包, 通知对方自己还在线, 以确保 TCP 连接的有效性. 为什么需要心跳 因为网络的不可 ...
- 四、Netty 实现心跳机制与断线重连
一.概述 何为心跳 顾名思义, 所谓心跳, 即在 TCP 长连接中, 客户端和服务器之间定期发送的一种特殊的数据包, 通知对方自己还在线, 以确保 TCP 连接的有效性. 为什么需要心跳 因为网络的不 ...
最新文章
- Magento — B2C与B2B平台
- (转)AS3函数动态添加实例属性
- LVS+Keepalived-DR模式负载均衡高可用集群
- svm的错误理解之一
- 互联网1分钟 |0102
- 关于windows下的libtorch配置
- 小伙用 12 张图讲明白了 Redis 持久化!
- node.js安装express(零起点搭建本地测试服务器)- 教程篇
- Vue 脚手架结合 SpringBoot 构建前后端分离入门项目(实现增删改查)
- 高清电视开播:大多用户仍难跨入
- ipsec over gre与gre over ipsec
- jenkins设置构建触发器
- JMeter下载及安装详细教程
- 【非原创】python调用bartender操控打印机打印条码
- python给ppt表格加边框_向ppt里插入图片和表格
- 【答粉丝问】前公司挽留涨薪,已经接到下一家公司的offer,是走还是留?
- 【附源码】Python计算机毕业设计食疗养生服务平台
- c语言中的用户标识符是什么,C语言中用户标识符是什么?
- android之broadcastreceiver 耳机按键,Android 实时监听耳机按钮事件
- 一级建造师课件免费下载_注册建造师管理制度中的矛盾点总结!