在工控上经常用到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断线重连的问题相关推荐

  1. tcp 重连 java,TCP断线重连机制

    断线重连机制 [java] view plain copy /** * 断线重连机制 * Created by fflin on 2016/5/8. */ public class Reconnect ...

  2. lwip的tcp断线重连例程_STM32F107+LWIP---如何检查tcp通讯断开?并重新连接

    void LwIP_Init(void) { struct ip_addr ipaddr;        //IP地址 struct ip_addr netmask;        //子掩码 str ...

  3. Netty是如何实现TCP心跳机制与断线重连的

    本文来说下Netty 是如何实现 TCP 心跳机制与断线重连的 文章目录 什么是心跳机制HeartBeat 如何实现心跳机制 Netty实现自定义的心跳机制 服务端 客户端 测试效果 客户端断线重连 ...

  4. Labview 编写TCP/IP 客户端断线重连机制程序,亲测可用

    程序面板如下图: 此程序支持任意一方断线重连机制,仅供大家参考! 实际工程中,如果出现服务器出现宕机,那么我们的客户端要有重连的机制,不然软件不会自动连接服务器,明显是我们程序编写的一个漏洞,无论是从 ...

  5. Socket网络编程tcp聊天案例(心跳包,多线程,断线重连机制)

    实现一个聊天的案例,使用多线程和心跳包.当服务器断开的时候,客户端会自动尝试重新连接,当服务器开启的时候,客户端会自动连接 Server服务器类 package Demo3_Chat;import c ...

  6. 面试官问:服务的心跳机制与断线重连,Netty底层是怎么实现的?懵了

    点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 心跳机制 何为心跳 所谓心跳, 即在 TCP 长连接中, ...

  7. Netty实现心跳机制与断线重连

    点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 来源:https://www.jianshu.com/p/ ...

  8. 浅析 Netty 实现心跳机制与断线重连

    基础 何为心跳 顾名思义, 所谓 心跳, 即在 TCP 长连接中, 客户端和服务器之间定期发送的一种特殊的数据包, 通知对方自己还在线, 以确保 TCP 连接的有效性. 为什么需要心跳 因为网络的不可 ...

  9. 四、Netty 实现心跳机制与断线重连

    一.概述 何为心跳 顾名思义, 所谓心跳, 即在 TCP 长连接中, 客户端和服务器之间定期发送的一种特殊的数据包, 通知对方自己还在线, 以确保 TCP 连接的有效性. 为什么需要心跳 因为网络的不 ...

最新文章

  1. Magento — B2C与B2B平台
  2. (转)AS3函数动态添加实例属性
  3. LVS+Keepalived-DR模式负载均衡高可用集群
  4. svm的错误理解之一
  5. 互联网1分钟 |0102
  6. 关于windows下的libtorch配置
  7. 小伙用 12 张图讲明白了 Redis 持久化!
  8. node.js安装express(零起点搭建本地测试服务器)- 教程篇
  9. Vue 脚手架结合 SpringBoot 构建前后端分离入门项目(实现增删改查)
  10. 高清电视开播:大多用户仍难跨入
  11. ipsec over gre与gre over ipsec
  12. jenkins设置构建触发器
  13. JMeter下载及安装详细教程
  14. 【非原创】python调用bartender操控打印机打印条码
  15. python给ppt表格加边框_向ppt里插入图片和表格
  16. 【答粉丝问】前公司挽留涨薪,已经接到下一家公司的offer,是走还是留?
  17. 【附源码】Python计算机毕业设计食疗养生服务平台
  18. c语言中的用户标识符是什么,C语言中用户标识符是什么?
  19. android之broadcastreceiver 耳机按键,Android 实时监听耳机按钮事件
  20. 一级建造师课件免费下载_注册建造师管理制度中的矛盾点总结!

热门文章

  1. 计算机毕业设计ssm双笙映画ou5oj系统+程序+源码+lw+远程部署
  2. Spring loC的介绍
  3. 新生代农民工的露营装备清单
  4. 第6篇:SELECT查询基础篇
  5. Dihedral Group
  6. java jmap 分析_Jmap 分析
  7. axure 8 表格合并_Axure 免费建个网站
  8. gorm多表联合查询
  9. 学习笔记 --- KF、EKF、IKF、ESIKF 公式推导 --Part 1
  10. 卫星通信的会议和期刊