| @@ -1,66 +1,6 @@ | |||||
| #include "common.h" | |||||
| #include "Interactive.h" | |||||
| /********************************************************************************************* | |||||
| * 功能 : IP地址有效性检测 | |||||
| * 描述 : 检测输入的IP地址是否合法 | |||||
| * 输入 : IP 输入的IP地址 | |||||
| * 输出 : true IP地址合法 | |||||
| * false IP地址非法 | |||||
| *********************************************************************************************/ | |||||
| bool Check_IP(string ip) | |||||
| { | |||||
| int s[4]; | |||||
| if (ip.length() < 7 || ip.length() > 15) //长度判定 | |||||
| return false; | |||||
| if (sscanf_s(ip.c_str(), "%d.%d.%d.%d", &s[0], &s[1], &s[2], &s[3]) != 4) //IPV4格式正确 | |||||
| { | |||||
| return false; | |||||
| } | |||||
| string newip = to_string(s[0]) + "." + to_string(s[1]) + "." + to_string(s[2]) + "." + to_string(s[3]); | |||||
| if (ip != newip) //前导0 | |||||
| return false; | |||||
| if ((s[0] & 0xffffff00) || (s[1] & 0xffffff00) || (s[2] & 0xffffff00) || (s[3] & 0xffffff00)) //判断每一段大小是否符合要求 | |||||
| { | |||||
| return false; | |||||
| } | |||||
| return true; | |||||
| } | |||||
| /********************************************************************************************* | |||||
| * 功能 : 获取从站IP地址和端口号 | |||||
| * 描述 : 终端输入从站IP地址和端口号 | |||||
| * 输入 : IP 地址 (IPV4) | |||||
| * *Port_number 端口号(1-65535) | |||||
| * 输出 : 无 | |||||
| *********************************************************************************************/ | |||||
| void Input_IP(string& ip, unsigned int *port_number) | |||||
| { | |||||
| int i = 1; | |||||
| do | |||||
| { | |||||
| if (i == 1) | |||||
| { | |||||
| cout << "请输入从站IP:"; | |||||
| i = 0; | |||||
| } | |||||
| else | |||||
| cout << "IP地址格式不正确,请重新输入从站IP:"; | |||||
| cin >> ip; | |||||
| } while (!Check_IP(ip)); | |||||
| i = 1; | |||||
| cout << "IP地址输格式入正确,请输入从站端口号:"; | |||||
| do | |||||
| { | |||||
| if (i != 1) | |||||
| cout << "请重新输入从站端口号:"; | |||||
| cin >> *port_number; | |||||
| i = 0; | |||||
| } while (*port_number == 0 || *port_number > 65535); //端口不能为0 ,端口号范围1---65535 | |||||
| } | |||||
| /********************************************************************************************* | /********************************************************************************************* | ||||
| * 功能 : 选择功能码 | * 功能 : 选择功能码 | ||||
| * 描述 : 在功能码 0x01 0x03 0x0F 0x10中选择一个功能码 | * 描述 : 在功能码 0x01 0x03 0x0F 0x10中选择一个功能码 | ||||
| @@ -218,33 +158,12 @@ unsigned int Count_Write_date_number(int function_code, unsigned int operations_ | |||||
| return write_date_number; | return write_date_number; | ||||
| } | } | ||||
| /********************************************************************************************* | |||||
| * 功能 : 计算请求报文计算预期响应报文字节数 | |||||
| * 描述 : 通过对应的功能码和操作数量计算对应的数据字节数 | |||||
| * 输入 : Function_code 选择的功能码类型 Operations_Number对应功能码类型的操作数量 | |||||
| * 输出 : Respone_len 预期响应的数据字节数 | |||||
| *********************************************************************************************/ | |||||
| unsigned int Count_Respone_Len(int function_code, unsigned int operations_number) | |||||
| { | |||||
| unsigned int respone_len = 0; | |||||
| if (function_code == 0x0F || function_code == 0x10) | |||||
| return respone_len; | |||||
| if (function_code == 0x01) //读线圈 | |||||
| { | |||||
| respone_len = operations_number / 8; | |||||
| if (operations_number % 8) | |||||
| respone_len++; | |||||
| } | |||||
| if (function_code == 0x03) | |||||
| respone_len = operations_number * 2; | |||||
| return respone_len; | |||||
| } | |||||
| /********************************************************************************************* | /********************************************************************************************* | ||||
| * 功能 : 输入写入数据 | * 功能 : 输入写入数据 | ||||
| * 描述 : 根据写入数量和功能码类型输入相应的数据 | * 描述 : 根据写入数量和功能码类型输入相应的数据 | ||||
| * 输入 : Function_code 选择的功能码类型 Operations_Number对应功能码类型的操作数量 | * 输入 : Function_code 选择的功能码类型 Operations_Number对应功能码类型的操作数量 | ||||
| * 输出 : Write_date 写入的数据 | |||||
| * 输出 : Write_date 写入的数据 | |||||
| *********************************************************************************************/ | *********************************************************************************************/ | ||||
| string Input_Write_date(int function_code, unsigned int operations_Number) | string Input_Write_date(int function_code, unsigned int operations_Number) | ||||
| { | { | ||||
| @@ -253,7 +172,7 @@ string Input_Write_date(int function_code, unsigned int operations_Number) | |||||
| unsigned int write_date_number = Count_Write_date_number(function_code, operations_Number); | unsigned int write_date_number = Count_Write_date_number(function_code, operations_Number); | ||||
| if (function_code == 0x01 || function_code == 0x03) | if (function_code == 0x01 || function_code == 0x03) | ||||
| return write_date;//读线圈/寄存器不需要输入写入数据 | return write_date;//读线圈/寄存器不需要输入写入数据 | ||||
| printf("请输入N组写入数据(2位16进制为一组,空格间隔)例如:00 00 01 00 20 05\n"); | printf("请输入N组写入数据(2位16进制为一组,空格间隔)例如:00 00 01 00 20 05\n"); | ||||
| do | do | ||||
| { | { | ||||
| @@ -272,6 +191,67 @@ string Input_Write_date(int function_code, unsigned int operations_Number) | |||||
| return write_date; | return write_date; | ||||
| } | } | ||||
| /********************************************************************************************* | |||||
| * 功能 : 打印读取从站的线圈状态 | |||||
| * 描述 : 对响应报文中的数据进行解析和显示 | |||||
| * 输入 : Response_Message 响应报文 Request_Message 请求报文 | |||||
| * 输出 : 无 | |||||
| *********************************************************************************************/ | |||||
| void Printf_Coil_date(UINT8 *Response_Message, UINT8 *Request_Message) | |||||
| { | |||||
| printf("从站设备ID %02X 功能码为 %02X\n", Response_Message[6], Response_Message[7]); | |||||
| unsigned int temp1 = ((Request_Message[8] << 8) | Request_Message[9]); | |||||
| unsigned int number = ((Request_Message[10] << 8) | Request_Message[11]); | |||||
| printf("线圈起始地址为%d \n", temp1); | |||||
| unsigned int temp = temp1; | |||||
| for (int i = 0; i < Response_Message[8]; i++) | |||||
| { | |||||
| unsigned int temp2 = temp + 7; | |||||
| if (temp2 > temp1 + number - 1) | |||||
| temp2 = temp1 + number - 1; | |||||
| printf("线圈%d --- %d的状态为:%02X \n", temp2, temp, Response_Message[9 + i]); | |||||
| temp = temp + 8; | |||||
| } | |||||
| } | |||||
| /********************************************************************************************* | |||||
| * 功能 : 打印读取从站的寄存器状态 | |||||
| * 描述 : 对响应报文中的数据进行解析和显示 | |||||
| * 输入 : Response_Message 响应报文 Request_Message 请求报文 | |||||
| * 输出 : 无 | |||||
| *********************************************************************************************/ | |||||
| void Printf_Register_date(UINT8 *Response_Message, UINT8 *Request_Message) | |||||
| { | |||||
| printf("从站设备ID %02X 功能码为 %02X\n", Response_Message[6], Response_Message[7]); | |||||
| unsigned int temp1 = ((Request_Message[8] << 8) | Request_Message[9]); | |||||
| printf("寄存器起始地址为%d \n", temp1); | |||||
| for (int i = 0; i < Response_Message[8]; i = i + 2) | |||||
| { | |||||
| printf("寄存器%d的值为:%02X %02X \n", temp1++, Response_Message[9 + i], Response_Message[10 + i]); | |||||
| } | |||||
| } | |||||
| /********************************************************************************************* | |||||
| * 功能 : 打印读取异常响应报文信息 | |||||
| * 描述 : 对响应报文中的数据进行解析和显示 | |||||
| * 输入 : Response_Message 响应报文 | |||||
| * 输出 : 无 | |||||
| *********************************************************************************************/ | |||||
| void Printf_Anomaly_date(UINT8 *Response_Message) | |||||
| { | |||||
| UINT8 anomaly_code = Response_Message[8]; | |||||
| printf("从站设备ID %02X 功能码为 %02X\n", Response_Message[6], Response_Message[7]); | |||||
| switch (anomaly_code) | |||||
| { | |||||
| case 0x01: printf("%02X : 从站设备不支持此功能码\n", anomaly_code); break; | |||||
| case 0x02: printf("%02X : 指定的数据地址在从站设备中不存在\n", anomaly_code); break; | |||||
| case 0x03: printf("%02X : 指定的数据超过范围或者不允许使用\n", anomaly_code); break; | |||||
| case 0x04: printf("%02X : 从站设备处理响应的过程中,出现未知错误等\n", anomaly_code); break; | |||||
| default: printf("Unkown Other Error!!!!!\n"); | |||||
| } | |||||
| } | |||||
| /********************************************************************************************* | /********************************************************************************************* | ||||
| * 功能 : 日志记录 | * 功能 : 日志记录 | ||||
| * 描述 : 记录每次通信的请求和响应报文 | * 描述 : 记录每次通信的请求和响应报文 | ||||
| @@ -288,14 +268,14 @@ void Log_Note(UINT8 *Message, int flage, int message_len) | |||||
| p = gmtime(&timep); | p = gmtime(&timep); | ||||
| string recv_str = to_string(1900 + p->tm_year) + "-" + to_string(1 + p->tm_mon) + "-" + to_string(p->tm_mday) | string recv_str = to_string(1900 + p->tm_year) + "-" + to_string(1 + p->tm_mon) + "-" + to_string(p->tm_mday) | ||||
| + " " + to_string(8 + p->tm_hour) + ":" + to_string(p->tm_min) + ":" + to_string(p->tm_sec) + " Recv:"; | + " " + to_string(8 + p->tm_hour) + ":" + to_string(p->tm_min) + ":" + to_string(p->tm_sec) + " Recv:"; | ||||
| string send_str= to_string(1900 + p->tm_year) + "-" + to_string(1 + p->tm_mon) + "-" + to_string(p->tm_mday) | |||||
| string send_str = to_string(1900 + p->tm_year) + "-" + to_string(1 + p->tm_mon) + "-" + to_string(p->tm_mday) | |||||
| + " " + to_string(8 + p->tm_hour) + ":" + to_string(p->tm_min) + ":" + to_string(p->tm_sec) + " Send:"; | + " " + to_string(8 + p->tm_hour) + ":" + to_string(p->tm_min) + ":" + to_string(p->tm_sec) + " Send:"; | ||||
| /* 打开文件用于读写 */ | /* 打开文件用于读写 */ | ||||
| if ((fp = fopen("Modbus_TCP_log.txt", "a+")) == NULL) | if ((fp = fopen("Modbus_TCP_log.txt", "a+")) == NULL) | ||||
| { | { | ||||
| printf("打开文件失败"); | printf("打开文件失败"); | ||||
| return ; | |||||
| return; | |||||
| } | } | ||||
| if (flage == 1) | if (flage == 1) | ||||
| { | { | ||||
| @@ -304,7 +284,7 @@ void Log_Note(UINT8 *Message, int flage, int message_len) | |||||
| { | { | ||||
| fprintf(fp, "%02X ", Message[i]); | fprintf(fp, "%02X ", Message[i]); | ||||
| } | } | ||||
| fprintf(fp,"\n"); | |||||
| fprintf(fp, "\n"); | |||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| @@ -319,136 +299,46 @@ void Log_Note(UINT8 *Message, int flage, int message_len) | |||||
| fp = NULL; | fp = NULL; | ||||
| } | } | ||||
| /********************************************************************************************* | |||||
| * 功能 : socket版本 | |||||
| * 描述 : 启动socket服务 | |||||
| * 输入 : 无 | |||||
| * 输出 : true 启动成功 | |||||
| * false 启动失败 | |||||
| *********************************************************************************************/ | |||||
| bool InitSocket_Version(void) | |||||
| { | |||||
| WORD sockVersion = MAKEWORD(2, 2);//使用winsocket2.2版本 | |||||
| WSADATA wsaData; | |||||
| if (WSAStartup(sockVersion, &wsaData) != 0) | |||||
| { | |||||
| return false; | |||||
| } | |||||
| return true; | |||||
| } | |||||
| /********************************************************************************************* | |||||
| * 功能 : 初始化客户端 | |||||
| * 描述 : 根据终端输入从站IP地址和端口号连接对应的服务器,设置超时时间 | |||||
| * 输入 : 无 | |||||
| * 输出 : ClientSocket 连接成功的套接字 | |||||
| *********************************************************************************************/ | |||||
| SOCKET Init_Client(void) | |||||
| void Printf_Message(UINT8 *Message, int flage, int message_len) | |||||
| { | { | ||||
| if (InitSocket_Version() == 0) | |||||
| { | |||||
| printf("启动Socket服务失败,请检查Socket版本"); | |||||
| return INVALID_SOCKET; | |||||
| } | |||||
| string ip_address; | |||||
| unsigned int port_number; | |||||
| Input_IP(ip_address, &port_number); | |||||
| SOCKET clientSocket = socket(AF_INET, SOCK_STREAM, 0); | |||||
| sockaddr_in serversock_in; | |||||
| serversock_in.sin_addr.S_un.S_addr = inet_addr(ip_address.c_str()); | |||||
| serversock_in.sin_family = AF_INET; | |||||
| serversock_in.sin_port = htons(port_number); | |||||
| if (SOCKET_ERROR == connect(clientSocket, (SOCKADDR*)&serversock_in, sizeof(SOCKADDR))) | |||||
| if (flage == 1) | |||||
| printf("主站请求 :"); | |||||
| else | |||||
| printf("从站响应 :"); | |||||
| for (int i = 0; i < message_len; i++) | |||||
| { | { | ||||
| cout << "尝试连接TCP从站失败" << endl; | |||||
| return INVALID_SOCKET; | |||||
| printf("%02x ", Message[i]); | |||||
| } | } | ||||
| cout << "连接TCP从站成功" << endl; | |||||
| TIMEVAL timeout; | |||||
| timeout.tv_sec = 200; //ms | |||||
| timeout.tv_usec = 0; //us | |||||
| setsockopt(clientSocket, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(struct timeval));//设置接收超时时间 | |||||
| return clientSocket; | |||||
| } | |||||
| void Clear_buf(SOCKET clientSocket) | |||||
| { | |||||
| unsigned long bytesToRecv; | |||||
| char temp[500]; | |||||
| do | |||||
| { | |||||
| ioctlsocket(clientSocket, FIONREAD, &bytesToRecv); | |||||
| if (bytesToRecv != 0)//不等于0时进行清理操作 | |||||
| { | |||||
| if (bytesToRecv > 500) | |||||
| { | |||||
| recv(clientSocket, temp, 500, 0); | |||||
| } | |||||
| else | |||||
| recv(clientSocket, temp, bytesToRecv, 0); | |||||
| } | |||||
| } while (bytesToRecv != 0); | |||||
| printf("\n"); | |||||
| if (LOG_NOTE_SWITCH) | |||||
| Log_Note(Message, flage, message_len); | |||||
| } | } | ||||
| bool Send_date(SOCKET clientSocket, UINT8 *Request_Message, int request_message_len) | |||||
| void Printf_Analysis_Outcome(UINT8 *Response_Message, UINT8 *Request_Message) | |||||
| { | { | ||||
| Clear_buf(clientSocket); //清理缓冲区 | |||||
| int status = send(clientSocket, (char*)Request_Message, request_message_len, 0); | |||||
| if (status == (-1)) | |||||
| switch (Response_Message[7]) | |||||
| { | { | ||||
| return false; | |||||
| case 0x01:Printf_Coil_date(Response_Message, Request_Message); break; | |||||
| case 0x03:Printf_Register_date(Response_Message, Request_Message); break; | |||||
| case 0x0F:printf("成功写入从站线圈%d个\n", ((Request_Message[10] << 8) | Request_Message[11])); break; | |||||
| case 0x10:printf("成功写入从站寄存器%d个\n", ((Request_Message[10] << 8) | Request_Message[11])); break; | |||||
| default: printf("Other Error"); | |||||
| } | } | ||||
| return true; | |||||
| } | |||||
| bool Abnormal_Connection(SOCKET *clientSocket) | |||||
| { | |||||
| printf("连接异常,请检查连接状态。\n"); | |||||
| printf("**************************** Press Enter To Contioun ****************************\n"); | |||||
| getchar(); | |||||
| system("cls"); | |||||
| closesocket(*clientSocket); | |||||
| WSACleanup(); | |||||
| printf("是否重新连接服务器: 1 重新连接 0 关闭本软件\n"); | |||||
| int flage = 0; | |||||
| do | |||||
| { | |||||
| cin >> flage; | |||||
| cin.clear(); | |||||
| cin.sync(); | |||||
| } while (!(flage == 0 || flage == 1)); | |||||
| if (flage == 0) | |||||
| return false; | |||||
| system("cls"); | |||||
| do | |||||
| { | |||||
| *clientSocket = Init_Client(); | |||||
| } while (INVALID_SOCKET == *clientSocket); | |||||
| system("cls"); | |||||
| return true; | |||||
| } | } | ||||
| int Recv_date(SOCKET clientSocket, UINT8 *Response_Message) | |||||
| void Printf_Interactive_Data(UINT8 *Response_Message, UINT8 *Request_Message, int flage, int request_message_len, int response_message_len) | |||||
| { | { | ||||
| int Response_Message_len = recv(clientSocket, (char*)Response_Message, 600, 0); | |||||
| if (Response_Message_len > 0) | |||||
| Printf_Message(Request_Message, 1, request_message_len); | |||||
| Printf_Message(Response_Message, 0, response_message_len); | |||||
| switch (flage) | |||||
| { | { | ||||
| return Response_Message_len; | |||||
| case MSG_LEN_ERROR: printf("Message Length Error"); break; | |||||
| case MABP_ERROR:printf("MBAP Date Error"); break; | |||||
| case FUNCTION_CODE_ERROR:printf("Function Code Error"); break; | |||||
| case START_ADDRESS_ERROR:printf("Start Address Error"); break; | |||||
| case OPERATION_NUMBER_ERROR:printf("Operation Number Error"); break; | |||||
| case NORMAL_RESPONSE: Printf_Analysis_Outcome(Response_Message, Request_Message); break; | |||||
| case ABNORMAL_RESPONSE:Printf_Anomaly_date(Response_Message); break; | |||||
| default:printf("Other Error"); | |||||
| } | } | ||||
| return 0; | |||||
| } | |||||
| bool Test_Connection_status(SOCKET clientSocket) | |||||
| { | |||||
| TIMEVAL timeout; | |||||
| timeout.tv_sec = 0; | |||||
| timeout.tv_usec = 0; | |||||
| fd_set reads; | |||||
| FD_ZERO(&reads); | |||||
| FD_SET(clientSocket, &reads); | |||||
| if (select(0, &reads, 0, 0, &timeout)) | |||||
| return false; | |||||
| else | |||||
| return true; | |||||
| } | } | ||||
| @@ -1,20 +1,27 @@ | |||||
| #ifndef __COMMON_H | |||||
| #define __COMMON_H | |||||
| #ifndef __INTERACTIVE_H | |||||
| #define __INTERACTIVE_H | |||||
| #include <stdio.h> | #include <stdio.h> | ||||
| #include <winsock2.h> | |||||
| #include <WS2tcpip.h> | |||||
| #include <Windows.h> | |||||
| #include <vector> | #include <vector> | ||||
| #include <string> | #include <string> | ||||
| #include <iostream> | |||||
| #include <time.h> | #include <time.h> | ||||
| #pragma comment(lib,"ws2_32.lib") | |||||
| #include <iostream> | |||||
| using namespace std; | using namespace std; | ||||
| #define DEVICE_ID 0x01 //设备ID | |||||
| #define LOG_NOTE_SWITCH 1 //日志记录控制位 | |||||
| #define LOG_NOTE_SWITCH 1 //日志记录控制位 | |||||
| enum Response_Type{ | |||||
| MSG_LEN_ERROR, | |||||
| MABP_ERROR, | |||||
| FUNCTION_CODE_ERROR, | |||||
| START_ADDRESS_ERROR, | |||||
| OPERATION_NUMBER_ERROR, | |||||
| NORMAL_RESPONSE, | |||||
| ABNORMAL_RESPONSE | |||||
| }; | |||||
| int Input_Function_code(void); | int Input_Function_code(void); | ||||
| unsigned int Input_Starting_address(void); | unsigned int Input_Starting_address(void); | ||||
| @@ -22,13 +29,7 @@ unsigned int Input_Operations_number(int function_code); | |||||
| unsigned int Count_Write_date_number(int function_code, unsigned int operations_Number); | unsigned int Count_Write_date_number(int function_code, unsigned int operations_Number); | ||||
| string Input_Write_date(int function_code, unsigned int operations_Number); | string Input_Write_date(int function_code, unsigned int operations_Number); | ||||
| void Log_Note(UINT8 *Message, int flage, int message_len); | void Log_Note(UINT8 *Message, int flage, int message_len); | ||||
| unsigned int Count_Respone_Len(int function_code, unsigned int operations_number); | |||||
| SOCKET Init_Client(void); | |||||
| bool Send_date(SOCKET clientSocket, UINT8 *Request_Message, int request_message_len); | |||||
| bool Abnormal_Connection(SOCKET *clientSocket); | |||||
| int Recv_date(SOCKET clientSocket, UINT8 *Response_Message); | |||||
| bool Test_Connection_status(SOCKET clientSocket); | |||||
| void Printf_Interactive_Data(UINT8 *Response_Message, UINT8 *Request_Message, int flage, int request_message_len, int response_message_len); | |||||
| #endif | #endif | ||||
| @@ -0,0 +1,235 @@ | |||||
| #include "Modbus_Master.h" | |||||
| UINT16 Transmission_Indicator = 0x00; //事务号 | |||||
| /********************************************************************************************* | |||||
| * 功能 : 计算请求报文计算预期响应报文字节数 | |||||
| * 描述 : 通过对应的功能码和操作数量计算对应的数据字节数 | |||||
| * 输入 : Function_code 选择的功能码类型 Operations_Number对应功能码类型的操作数量 | |||||
| * 输出 : Respone_len 预期响应的数据字节数 | |||||
| *********************************************************************************************/ | |||||
| unsigned int Count_Respone_Len(int function_code, unsigned int operations_number) | |||||
| { | |||||
| unsigned int respone_len = 0; | |||||
| if (function_code == 0x0F || function_code == 0x10) | |||||
| return respone_len; | |||||
| if (function_code == 0x01) //读线圈 | |||||
| { | |||||
| respone_len = operations_number / 8; | |||||
| if (operations_number % 8) | |||||
| respone_len++; | |||||
| } | |||||
| if (function_code == 0x03) | |||||
| respone_len = operations_number * 2; | |||||
| return respone_len; | |||||
| } | |||||
| /********************************************************************************************* | |||||
| * 功能 : 检测响应报文长度 | |||||
| * 描述 : 对响应报文中存放长度字节进行判断和请求报文对比 | |||||
| * 输入 : Response_Message 响应报文 Request_Message 请求报文 Response_Message_len 接收到的数据长度 | |||||
| * 输出 : true 长度正常 false 长度异常 | |||||
| *********************************************************************************************/ | |||||
| bool Check_Response_Message_len(UINT8 *Response_Message, UINT8 *Request_Message, int response_message_len) | |||||
| { | |||||
| if ((response_message_len - 6) != Response_Message[5]) | |||||
| return false; | |||||
| unsigned int operations_Number = Request_Message[10] << 8 | Request_Message[11]; | |||||
| unsigned int respone_Len = Count_Respone_Len(Request_Message[7], operations_Number); | |||||
| if (Request_Message[7] == 0x0F || Request_Message[7] == 0x10) | |||||
| if (response_message_len != 12) | |||||
| return false; | |||||
| if (Request_Message[7] == 0x01 || Request_Message[7] == 0x03) | |||||
| { | |||||
| if (respone_Len + 9 != response_message_len) | |||||
| return false; | |||||
| if (respone_Len != Response_Message[8]) | |||||
| return false; | |||||
| } | |||||
| return true; | |||||
| } | |||||
| /********************************************************************************************* | |||||
| * 功能 : 判断响应报文是否可以正常解析 | |||||
| * 描述 : 通过长度和异常码等判定该响应报文是否可以解析 | |||||
| * 输入 : Response_Message 响应报文 Request_Message请求报文 Response_Message_len 响应报文长度 | |||||
| * 输出 : 响应数据类型 | |||||
| *********************************************************************************************/ | |||||
| int Analysis_Response_Message(UINT8 *Response_Message, UINT8 *Request_Message, int response_message_len) | |||||
| { | |||||
| if (response_message_len < 9 || response_message_len > 260) | |||||
| return MSG_LEN_ERROR; | |||||
| for (int i = 0; i < 7; i++) | |||||
| { | |||||
| if (i == 4 || i == 5) //后续字节长度 | |||||
| continue; | |||||
| if (Response_Message[i] != Request_Message[i]) //0 1 传输标识,2 3 协议标识,6设备ID | |||||
| return MABP_ERROR; //一旦不一致,判定为异常报文数据 | |||||
| } | |||||
| if (Response_Message[4] != 0x00) //4 固定0x00 | |||||
| return MABP_ERROR; | |||||
| if (Response_Message[7] == Request_Message[7] + 0x80)//先处理异常响应 | |||||
| { | |||||
| if (response_message_len == 9) | |||||
| { | |||||
| return ABNORMAL_RESPONSE; | |||||
| } | |||||
| else | |||||
| return MSG_LEN_ERROR; | |||||
| } | |||||
| if (Response_Message[7] != Request_Message[7]) //功能码判断 | |||||
| return FUNCTION_CODE_ERROR; | |||||
| if (Request_Message[7] == 0x0F || Request_Message[7] == 0x10) //0F 10 地址和操作数量判定 | |||||
| { | |||||
| for (int j = 8; j < 10; j++) | |||||
| { | |||||
| if (Request_Message[j] != Response_Message[j]) | |||||
| return START_ADDRESS_ERROR; | |||||
| } | |||||
| for (int j = 10; j < 12; j++) | |||||
| { | |||||
| if (Request_Message[j] != Response_Message[j]) | |||||
| return OPERATION_NUMBER_ERROR; | |||||
| } | |||||
| } | |||||
| if (!Check_Response_Message_len(Response_Message, Request_Message, response_message_len)) | |||||
| return MSG_LEN_ERROR; | |||||
| return NORMAL_RESPONSE; | |||||
| } | |||||
| /********************************************************************************************* | |||||
| * 功能 : 生成MBAP报头 | |||||
| * 描述 : MBAP报文头的包括的内容: | |||||
| * +-------------+---------+--------+--------+------------------------------+ | |||||
| * | 域 | 长度 | 客户机 | 服务器 | 描述 | | |||||
| * +-------------+---------+--------+--------+------------------------------+ | |||||
| * |事务元标识符 | 2个字节 | 启动 | 复制 |请求/响应事务处理的识别码 | | |||||
| * +-------------+---------+--------+--------+------------------------------+ | |||||
| * |协议标识符 | 2个字节 | 启动 | 复制 |0=MODBUS 协议 | | |||||
| * +-------------+---------+--------+--------+------------------------------+ | |||||
| * | 长度 | 2个字节 | 启动 | 启动 |以下字节的数量 | | |||||
| * +-------------+---------+--------+--------+------------------------------+ | |||||
| * |单元标识符 | 1个字节 | 启动 | 复制 |连接的远程从站的识别码 | | |||||
| * 输入 : 无 | |||||
| * 输出 : MBAP报文头内容 | |||||
| *********************************************************************************************/ | |||||
| void Create_MBAP(UINT8 *Message, int function_code, unsigned int operations_number) | |||||
| { | |||||
| Message[0] = Transmission_Indicator >> 8;//事务号 | |||||
| Message[1] = (UINT8)Transmission_Indicator; | |||||
| Transmission_Indicator++; | |||||
| Message[2] = 0x00; //Modbus协议标识 | |||||
| Message[3] = 0x00; | |||||
| Message[4] = 0x00;//后续字节长度 | |||||
| Message[5] = Count_Write_date_number(function_code, operations_number) + 0x06; | |||||
| if (function_code == 0x0F || function_code == 0x10)//0f/10功能码后续字节数多1 | |||||
| { | |||||
| Message[5] = Message[5] + 1; | |||||
| } | |||||
| Message[6] = DEVICE_ID; | |||||
| } | |||||
| /********************************************************************************************* | |||||
| * 功能 : 字符串转UINT8类型 | |||||
| * 描述 : 根据写入数量和功能码类型输入相应的数据 | |||||
| * 输入 : *Message 消息帧存放的数组 Write_date 要转换的字符串 | |||||
| * Message_len消息帧数组的起始位置 | |||||
| * 输出 : Message_len 消息帧的长度 | |||||
| *********************************************************************************************/ | |||||
| int HexStringtoByte(UINT8 *Message, string write_date, int message_len, int function_code, unsigned int operations_Number) | |||||
| { | |||||
| if (write_date.length() == 0) | |||||
| return message_len; | |||||
| Message[message_len] = Count_Write_date_number(function_code, operations_Number); | |||||
| message_len++; | |||||
| const char *b = write_date.c_str(); | |||||
| int temp = 0; | |||||
| for (unsigned int i = 0; i + 3 < write_date.length(); i = i + 3) | |||||
| { | |||||
| sscanf_s(b + i, "%02X", &temp); | |||||
| Message[message_len] = (UINT8)temp; | |||||
| message_len++; | |||||
| } | |||||
| sscanf_s(b + write_date.length() - 2, "%02X", &temp); | |||||
| Message[message_len] = (UINT8)temp; | |||||
| return ++message_len; | |||||
| } | |||||
| /********************************************************************************************* | |||||
| * 功能 : 生成TCP模式下的消息帧 | |||||
| * 描述 : 根据要写入的数据生成消息帧 | |||||
| * 输入 : *Message 消息帧存放的数组 Write_date 写入数据的字符串 | |||||
| * Function_code 功能码 Operations_Number 操作数量 Starting_address起始地址 | |||||
| * 输出 : 消息帧的总长度 | |||||
| *********************************************************************************************/ | |||||
| int Create_TCP_Message(UINT8 *Message, int function_code, unsigned int operations_Number, unsigned int starting_address, string write_date) | |||||
| { | |||||
| Create_MBAP(Message, function_code, operations_Number); | |||||
| Message[7] = function_code;//功能码 | |||||
| Message[8] = starting_address >> 8;//起始地址H | |||||
| Message[9] = starting_address;//起始地址L | |||||
| Message[10] = operations_Number >> 8;//操作数量H | |||||
| Message[11] = operations_Number;//操作数量L | |||||
| return HexStringtoByte(Message, write_date, 12, function_code, operations_Number); | |||||
| } | |||||
| /********************************************************************************************* | |||||
| * 功能 : 运行客户端 | |||||
| * 描述 : 初始化并且生成发送请求等待响应 | |||||
| * 输入 : 无 | |||||
| * 输出 : 无 | |||||
| *********************************************************************************************/ | |||||
| int Tcp_Client() | |||||
| { | |||||
| SOCKET clientSocket; | |||||
| UINT8 Request_Message[600]; | |||||
| memset(Request_Message, 0, 600); | |||||
| UINT8 Response_Message[600]; | |||||
| memset(Response_Message, 0, 600); | |||||
| do | |||||
| { | |||||
| clientSocket = Init_Client(); | |||||
| } while (INVALID_SOCKET == clientSocket); | |||||
| while (true) | |||||
| { | |||||
| int function_code = Input_Function_code();//交互 | |||||
| unsigned int operations_Number = Input_Operations_number(function_code); | |||||
| unsigned int starting_address = Input_Starting_address(); | |||||
| string write_date = Input_Write_date(function_code, operations_Number); | |||||
| int request_message_len = Create_TCP_Message(Request_Message, function_code, operations_Number, starting_address, write_date);//生成 | |||||
| if (!Send_date(clientSocket, Request_Message, request_message_len)) //发送 | |||||
| { | |||||
| if (Abnormal_Connection(&clientSocket)) | |||||
| continue; | |||||
| else | |||||
| break; | |||||
| } | |||||
| int response_message_len = Recv_date(clientSocket, Response_Message);//接收 | |||||
| if (response_message_len) | |||||
| { | |||||
| int flage = Analysis_Response_Message(Response_Message, Request_Message, response_message_len); | |||||
| Printf_Interactive_Data(Response_Message, Request_Message, flage, request_message_len, response_message_len); | |||||
| } | |||||
| else if(!Test_Connection_status(clientSocket)) | |||||
| { | |||||
| if (Abnormal_Connection(&clientSocket)) | |||||
| continue; | |||||
| else | |||||
| break; | |||||
| } | |||||
| else | |||||
| printf("响应超时\n"); | |||||
| printf("**************************** Press Enter To Contioun ****************************\n"); | |||||
| getchar(); | |||||
| system("cls"); | |||||
| } | |||||
| //关闭套接字 | |||||
| closesocket(clientSocket); | |||||
| //关闭服务 | |||||
| WSACleanup(); | |||||
| return 0; | |||||
| } | |||||
| @@ -0,0 +1,12 @@ | |||||
| #ifndef __MODBUS_MASTER_H | |||||
| #define __MODBUS_MASTER_H | |||||
| #include "TCP_client.h" | |||||
| #include "Interactive.h" | |||||
| #define DEVICE_ID 0x01 //É豸ID | |||||
| int Tcp_Client(); | |||||
| int Create_TCP_Message(UINT8 *Message, int function_code, unsigned int operations_Number, unsigned int starting_address, string write_date); | |||||
| int Analysis_Response_Message(UINT8 *Response_Message, UINT8 *Request_Message, int response_message_len); | |||||
| #endif | |||||
| @@ -66,14 +66,16 @@ | |||||
| </Link> | </Link> | ||||
| </ItemDefinitionGroup> | </ItemDefinitionGroup> | ||||
| <ItemGroup> | <ItemGroup> | ||||
| <ClCompile Include="common.cpp" /> | |||||
| <ClCompile Include="main.cpp" /> | |||||
| <ClCompile Include="TCP_client.cpp" /> | <ClCompile Include="TCP_client.cpp" /> | ||||
| <ClCompile Include="Interactive.cpp" /> | |||||
| <ClCompile Include="main.cpp" /> | |||||
| <ClCompile Include="Modbus_Master.cpp" /> | |||||
| </ItemGroup> | </ItemGroup> | ||||
| <ItemGroup> | <ItemGroup> | ||||
| <ClInclude Include="common.h" /> | |||||
| <ClInclude Include="main.h" /> | |||||
| <ClInclude Include="TCP_client.h" /> | <ClInclude Include="TCP_client.h" /> | ||||
| <ClInclude Include="Interactive.h" /> | |||||
| <ClInclude Include="main.h" /> | |||||
| <ClInclude Include="Modbus_Master.h" /> | |||||
| </ItemGroup> | </ItemGroup> | ||||
| <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> | <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> | ||||
| <ImportGroup Label="ExtensionTargets"> | <ImportGroup Label="ExtensionTargets"> | ||||
| @@ -18,7 +18,10 @@ | |||||
| <ClCompile Include="main.cpp"> | <ClCompile Include="main.cpp"> | ||||
| <Filter>源文件</Filter> | <Filter>源文件</Filter> | ||||
| </ClCompile> | </ClCompile> | ||||
| <ClCompile Include="common.cpp"> | |||||
| <ClCompile Include="Interactive.cpp"> | |||||
| <Filter>源文件</Filter> | |||||
| </ClCompile> | |||||
| <ClCompile Include="Modbus_Master.cpp"> | |||||
| <Filter>源文件</Filter> | <Filter>源文件</Filter> | ||||
| </ClCompile> | </ClCompile> | ||||
| <ClCompile Include="TCP_client.cpp"> | <ClCompile Include="TCP_client.cpp"> | ||||
| @@ -26,13 +29,16 @@ | |||||
| </ClCompile> | </ClCompile> | ||||
| </ItemGroup> | </ItemGroup> | ||||
| <ItemGroup> | <ItemGroup> | ||||
| <ClInclude Include="common.h"> | |||||
| <ClInclude Include="main.h"> | |||||
| <Filter>头文件</Filter> | <Filter>头文件</Filter> | ||||
| </ClInclude> | </ClInclude> | ||||
| <ClInclude Include="TCP_client.h"> | |||||
| <ClInclude Include="Interactive.h"> | |||||
| <Filter>头文件</Filter> | <Filter>头文件</Filter> | ||||
| </ClInclude> | </ClInclude> | ||||
| <ClInclude Include="main.h"> | |||||
| <ClInclude Include="Modbus_Master.h"> | |||||
| <Filter>头文件</Filter> | |||||
| </ClInclude> | |||||
| <ClInclude Include="TCP_client.h"> | |||||
| <Filter>头文件</Filter> | <Filter>头文件</Filter> | ||||
| </ClInclude> | </ClInclude> | ||||
| </ItemGroup> | </ItemGroup> | ||||
| @@ -1,303 +1,196 @@ | |||||
| #include "TCP_client.h" | #include "TCP_client.h" | ||||
| UINT16 Transmission_Indicator = 0x00; //事务号 | |||||
| /********************************************************************************************* | |||||
| * 功能 : 打印读取从站的线圈状态 | |||||
| * 描述 : 对响应报文中的数据进行解析和显示 | |||||
| * 输入 : Response_Message 响应报文 Request_Message 请求报文 | |||||
| * 输出 : 无 | |||||
| *********************************************************************************************/ | |||||
| void Printf_Coil_date(UINT8 *Response_Message, UINT8 *Request_Message) | |||||
| { | |||||
| printf("从站设备ID %02X 功能码为 %02X\n", Response_Message[6], Response_Message[7]); | |||||
| unsigned int temp1 = ((Request_Message[8] << 8) | Request_Message[9]); | |||||
| unsigned int number = ((Request_Message[10] << 8) | Request_Message[11]); | |||||
| printf("线圈起始地址为%d \n",temp1); | |||||
| unsigned int temp = temp1; | |||||
| for (int i = 0; i < Response_Message[8]; i++) | |||||
| { | |||||
| unsigned int temp2 = temp + 7; | |||||
| if (temp2 > temp1 + number - 1) | |||||
| temp2 = temp1 + number - 1; | |||||
| printf("线圈%d --- %d的状态为:%02X \n", temp2, temp, Response_Message[9+i]); | |||||
| temp = temp + 8; | |||||
| } | |||||
| } | |||||
| /********************************************************************************************* | |||||
| * 功能 : 打印读取从站的寄存器状态 | |||||
| * 描述 : 对响应报文中的数据进行解析和显示 | |||||
| * 输入 : Response_Message 响应报文 Request_Message 请求报文 | |||||
| * 输出 : 无 | |||||
| *********************************************************************************************/ | |||||
| void Printf_Register_date(UINT8 *Response_Message, UINT8 *Request_Message) | |||||
| { | |||||
| printf("从站设备ID %02X 功能码为 %02X\n", Response_Message[6], Response_Message[7]); | |||||
| unsigned int temp1 = ((Request_Message[8] << 8) | Request_Message[9]); | |||||
| printf("寄存器起始地址为%d \n", temp1); | |||||
| for (int i = 0; i < Response_Message[8]; i = i + 2) | |||||
| { | |||||
| printf("寄存器%d的值为:%02X %02X \n", temp1++, Response_Message[9 + i], Response_Message[10+i]); | |||||
| } | |||||
| } | |||||
| /********************************************************************************************* | /********************************************************************************************* | ||||
| * 功能 : 打印读取异常响应报文信息 | |||||
| * 描述 : 对响应报文中的数据进行解析和显示 | |||||
| * 输入 : Response_Message 响应报文 | |||||
| * 输出 : 无 | |||||
| * 功能 : IP地址有效性检测 | |||||
| * 描述 : 检测输入的IP地址是否合法 | |||||
| * 输入 : IP 输入的IP地址 | |||||
| * 输出 : true IP地址合法 | |||||
| * false IP地址非法 | |||||
| *********************************************************************************************/ | *********************************************************************************************/ | ||||
| void Printf_Anomaly_date(UINT8 *Response_Message) | |||||
| bool Check_IP(string ip) | |||||
| { | { | ||||
| UINT8 anomaly_code = Response_Message[8]; | |||||
| printf("从站设备ID %02X 功能码为 %02X\n", Response_Message[6], Response_Message[7]); | |||||
| switch (anomaly_code) | |||||
| int s[4]; | |||||
| if (ip.length() < 7 || ip.length() > 15) //长度判定 | |||||
| return false; | |||||
| if (sscanf_s(ip.c_str(), "%d.%d.%d.%d", &s[0], &s[1], &s[2], &s[3]) != 4) //IPV4格式正确 | |||||
| { | { | ||||
| case 0x01: printf("%02X : 从站设备不支持此功能码\n", anomaly_code); break; | |||||
| case 0x02: printf("%02X : 指定的数据地址在从站设备中不存在\n", anomaly_code); break; | |||||
| case 0x03: printf("%02X : 指定的数据超过范围或者不允许使用\n", anomaly_code); break; | |||||
| case 0x04: printf("%02X : 从站设备处理响应的过程中,出现未知错误等\n", anomaly_code); break; | |||||
| default: printf("Unkown Other Error!!!!!\n"); | |||||
| return false; | |||||
| } | } | ||||
| } | |||||
| /********************************************************************************************* | |||||
| * 功能 : 检测响应报文长度 | |||||
| * 描述 : 对响应报文中存放长度字节进行判断和请求报文对比 | |||||
| * 输入 : Response_Message 响应报文 Request_Message 请求报文 Response_Message_len 接收到的数据长度 | |||||
| * 输出 : true 长度正常 false 长度异常 | |||||
| *********************************************************************************************/ | |||||
| bool Check_Response_Message_len(UINT8 *Response_Message, UINT8 *Request_Message, int response_message_len) | |||||
| { | |||||
| if ((response_message_len - 6) != Response_Message[5]) | |||||
| string newip = to_string(s[0]) + "." + to_string(s[1]) + "." + to_string(s[2]) + "." + to_string(s[3]); | |||||
| if (ip != newip) //前导0 | |||||
| return false; | return false; | ||||
| unsigned int operations_Number = Request_Message[10] << 8 | Request_Message[11]; | |||||
| unsigned int respone_Len = Count_Respone_Len(Request_Message[7], operations_Number); | |||||
| if (Request_Message[7] == 0x0F || Request_Message[7] == 0x10) | |||||
| if (response_message_len != 12) | |||||
| return false; | |||||
| if (Request_Message[7] == 0x01 || Request_Message[7] == 0x03) | |||||
| if ((s[0] & 0xffffff00) || (s[1] & 0xffffff00) || (s[2] & 0xffffff00) || (s[3] & 0xffffff00)) //判断每一段大小是否符合要求 | |||||
| { | { | ||||
| if (respone_Len + 9 != response_message_len) | |||||
| return false; | |||||
| if (respone_Len != Response_Message[8]) | |||||
| return false; | |||||
| return false; | |||||
| } | } | ||||
| return true; | return true; | ||||
| } | } | ||||
| bool Printf_Analysis_Outcome(UINT8 *Response_Message, UINT8 *Request_Message) | |||||
| { | |||||
| switch (Response_Message[7]) | |||||
| { | |||||
| case 0x01:Printf_Coil_date(Response_Message, Request_Message); break; | |||||
| case 0x03:Printf_Register_date(Response_Message, Request_Message); break; | |||||
| case 0x0F:printf("成功写入从站线圈%d个\n", ((Request_Message[10] << 8) | Request_Message[11])); break; | |||||
| case 0x10:printf("成功写入从站寄存器%d个\n", ((Request_Message[10] << 8) | Request_Message[11])); break; | |||||
| default: return false; | |||||
| } | |||||
| return true; | |||||
| } | |||||
| /********************************************************************************************* | /********************************************************************************************* | ||||
| * 功能 : 判断响应报文是否可以正常解析 | |||||
| * 描述 : 通过长度和异常码等判定该响应报文是否可以解析 | |||||
| * 输入 : Response_Message 响应报文 Request_Message请求报文 Response_Message_len 响应报文长度 | |||||
| * 输出 : true可以正常解析 false 无法解析 | |||||
| * 功能 : 获取从站IP地址和端口号 | |||||
| * 描述 : 终端输入从站IP地址和端口号 | |||||
| * 输入 : IP 地址 (IPV4) | |||||
| * *Port_number 端口号(1-65535) | |||||
| * 输出 : 无 | |||||
| *********************************************************************************************/ | *********************************************************************************************/ | ||||
| bool Analysis_Response_Message(UINT8 *Response_Message, UINT8 *Request_Message, int response_message_len) | |||||
| void Input_IP(string& ip, unsigned int *port_number) | |||||
| { | { | ||||
| if (response_message_len < 9 || response_message_len > 260) | |||||
| return false; | |||||
| for (int i = 0; i < 7; i++) | |||||
| { | |||||
| if (i == 4 || i == 5) //后续字节长度 | |||||
| continue; | |||||
| if (Response_Message[i] != Request_Message[i]) //0 1 传输标识,2 3 协议标识,6设备ID | |||||
| return false; //一旦不一致,判定为异常报文数据 | |||||
| } | |||||
| if (Response_Message[4] != 0x00) //4 固定0x00 | |||||
| return false; | |||||
| if (Response_Message[7] == Request_Message[7] + 0x80)//先处理异常响应 | |||||
| int i = 1; | |||||
| do | |||||
| { | { | ||||
| if (response_message_len == 9) | |||||
| if (i == 1) | |||||
| { | { | ||||
| Printf_Anomaly_date(Response_Message); | |||||
| return true; | |||||
| cout << "请输入从站IP:"; | |||||
| i = 0; | |||||
| } | } | ||||
| else | else | ||||
| return false; | |||||
| } | |||||
| if (Response_Message[7] != Request_Message[7]) //功能码判断 | |||||
| return false; | |||||
| if (Request_Message[7] == 0x0F || Request_Message[7] == 0x10) //0F 10 地址和操作数量判定 | |||||
| { | |||||
| for (int j = 8; j < 12; j++) | |||||
| { | |||||
| if (Request_Message[j] != Response_Message[j]) | |||||
| return false; | |||||
| } | |||||
| } | |||||
| if (!Check_Response_Message_len(Response_Message, Request_Message, response_message_len)) | |||||
| return false; | |||||
| //打印解析结果 | |||||
| return Printf_Analysis_Outcome(Response_Message, Request_Message); | |||||
| } | |||||
| cout << "IP地址格式不正确,请重新输入从站IP:"; | |||||
| cin >> ip; | |||||
| } while (!Check_IP(ip)); | |||||
| void Printf_Message(UINT8 *Message,int flage, int message_len) | |||||
| { | |||||
| if (flage == 1) | |||||
| printf("主站请求 :"); | |||||
| else | |||||
| printf("从站响应 :"); | |||||
| for (int i = 0; i < message_len; i++) | |||||
| i = 1; | |||||
| cout << "IP地址输格式入正确,请输入从站端口号:"; | |||||
| do | |||||
| { | { | ||||
| printf("%02x ", Message[i]); | |||||
| } | |||||
| printf("\n"); | |||||
| if (LOG_NOTE_SWITCH) | |||||
| Log_Note(Message, flage, message_len); | |||||
| } | |||||
| if (i != 1) | |||||
| cout << "请重新输入从站端口号:"; | |||||
| cin >> *port_number; | |||||
| i = 0; | |||||
| } while (*port_number == 0 || *port_number > 65535); //端口不能为0 ,端口号范围1---65535 | |||||
| } | |||||
| /********************************************************************************************* | /********************************************************************************************* | ||||
| * 功能 : 生成MBAP报头 | |||||
| * 描述 : MBAP报文头的包括的内容: | |||||
| * +-------------+---------+--------+--------+------------------------------+ | |||||
| * | 域 | 长度 | 客户机 | 服务器 | 描述 | | |||||
| * +-------------+---------+--------+--------+------------------------------+ | |||||
| * |事务元标识符 | 2个字节 | 启动 | 复制 |请求/响应事务处理的识别码 | | |||||
| * +-------------+---------+--------+--------+------------------------------+ | |||||
| * |协议标识符 | 2个字节 | 启动 | 复制 |0=MODBUS 协议 | | |||||
| * +-------------+---------+--------+--------+------------------------------+ | |||||
| * | 长度 | 2个字节 | 启动 | 启动 |以下字节的数量 | | |||||
| * +-------------+---------+--------+--------+------------------------------+ | |||||
| * |单元标识符 | 1个字节 | 启动 | 复制 |连接的远程从站的识别码 | | |||||
| * 功能 : socket版本 | |||||
| * 描述 : 启动socket服务 | |||||
| * 输入 : 无 | * 输入 : 无 | ||||
| * 输出 : MBAP报文头内容 | |||||
| * 输出 : true 启动成功 | |||||
| * false 启动失败 | |||||
| *********************************************************************************************/ | *********************************************************************************************/ | ||||
| void Create_MBAP(UINT8 *Message, int function_code, unsigned int operations_number) | |||||
| bool InitSocket_Version(void) | |||||
| { | { | ||||
| Message[0] = Transmission_Indicator >> 8;//事务号 | |||||
| Message[1] = (UINT8)Transmission_Indicator; | |||||
| Transmission_Indicator++; | |||||
| Message[2] = 0x00; //Modbus协议标识 | |||||
| Message[3] = 0x00; | |||||
| Message[4] = 0x00;//后续字节长度 | |||||
| Message[5] = Count_Write_date_number(function_code, operations_number) + 0x06; | |||||
| if (function_code == 0x0F || function_code == 0x10)//0f/10功能码后续字节数多1 | |||||
| WORD sockVersion = MAKEWORD(2, 2);//使用winsocket2.2版本 | |||||
| WSADATA wsaData; | |||||
| if (WSAStartup(sockVersion, &wsaData) != 0) | |||||
| { | { | ||||
| Message[5] = Message[5] + 1; | |||||
| return false; | |||||
| } | } | ||||
| Message[6] = DEVICE_ID; | |||||
| return true; | |||||
| } | } | ||||
| /********************************************************************************************* | /********************************************************************************************* | ||||
| * 功能 : 字符串转UINT8类型 | |||||
| * 描述 : 根据写入数量和功能码类型输入相应的数据 | |||||
| * 输入 : *Message 消息帧存放的数组 Write_date 要转换的字符串 | |||||
| * Message_len消息帧数组的起始位置 | |||||
| * 输出 : Message_len 消息帧的长度 | |||||
| * 功能 : 初始化客户端 | |||||
| * 描述 : 根据终端输入从站IP地址和端口号连接对应的服务器,设置超时时间 | |||||
| * 输入 : 无 | |||||
| * 输出 : ClientSocket 连接成功的套接字 | |||||
| *********************************************************************************************/ | *********************************************************************************************/ | ||||
| int HexStringtoByte(UINT8 *Message, string write_date, int message_len, int function_code, unsigned int operations_Number) | |||||
| SOCKET Init_Client(void) | |||||
| { | { | ||||
| if (write_date.length() == 0) | |||||
| return message_len; | |||||
| Message[message_len] = Count_Write_date_number(function_code, operations_Number); | |||||
| message_len++; | |||||
| const char *b = write_date.c_str(); | |||||
| int temp = 0; | |||||
| for (unsigned int i = 0; i + 3 < write_date.length(); i = i + 3) | |||||
| if (InitSocket_Version() == 0) | |||||
| { | |||||
| printf("启动Socket服务失败,请检查Socket版本"); | |||||
| return INVALID_SOCKET; | |||||
| } | |||||
| string ip_address; | |||||
| unsigned int port_number; | |||||
| Input_IP(ip_address, &port_number); | |||||
| SOCKET clientSocket = socket(AF_INET, SOCK_STREAM, 0); | |||||
| sockaddr_in serversock_in; | |||||
| serversock_in.sin_addr.S_un.S_addr = inet_addr(ip_address.c_str()); | |||||
| serversock_in.sin_family = AF_INET; | |||||
| serversock_in.sin_port = htons(port_number); | |||||
| if (SOCKET_ERROR == connect(clientSocket, (SOCKADDR*)&serversock_in, sizeof(SOCKADDR))) | |||||
| { | { | ||||
| sscanf_s(b + i, "%02X", &temp); | |||||
| Message[message_len] = (UINT8)temp; | |||||
| message_len++; | |||||
| cout << "尝试连接TCP从站失败" << endl; | |||||
| return INVALID_SOCKET; | |||||
| } | } | ||||
| sscanf_s(b + write_date.length() - 2, "%02X", &temp); | |||||
| Message[message_len] = (UINT8)temp; | |||||
| return ++message_len; | |||||
| cout << "连接TCP从站成功" << endl; | |||||
| TIMEVAL timeout; | |||||
| timeout.tv_sec = 200; //ms | |||||
| timeout.tv_usec = 0; //us | |||||
| setsockopt(clientSocket, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(struct timeval));//设置接收超时时间 | |||||
| return clientSocket; | |||||
| } | } | ||||
| /********************************************************************************************* | |||||
| * 功能 : 生成TCP模式下的消息帧 | |||||
| * 描述 : 根据要写入的数据生成消息帧 | |||||
| * 输入 : *Message 消息帧存放的数组 Write_date 写入数据的字符串 | |||||
| * Function_code 功能码 Operations_Number 操作数量 Starting_address起始地址 | |||||
| * 输出 : 消息帧的总长度 | |||||
| *********************************************************************************************/ | |||||
| int Create_TCP_Message(UINT8 *Message, int function_code, unsigned int operations_Number, unsigned int starting_address, string write_date) | |||||
| void Clear_buf(SOCKET clientSocket) | |||||
| { | { | ||||
| Create_MBAP(Message, function_code, operations_Number); | |||||
| Message[7] = function_code;//功能码 | |||||
| Message[8] = starting_address >> 8;//起始地址H | |||||
| Message[9] = starting_address;//起始地址L | |||||
| Message[10] = operations_Number >> 8;//操作数量H | |||||
| Message[11] = operations_Number;//操作数量L | |||||
| return HexStringtoByte(Message, write_date, 12, function_code, operations_Number); | |||||
| unsigned long bytesToRecv; | |||||
| char temp[500]; | |||||
| do | |||||
| { | |||||
| ioctlsocket(clientSocket, FIONREAD, &bytesToRecv); | |||||
| if (bytesToRecv != 0)//不等于0时进行清理操作 | |||||
| { | |||||
| if (bytesToRecv > 500) | |||||
| { | |||||
| recv(clientSocket, temp, 500, 0); | |||||
| } | |||||
| else | |||||
| recv(clientSocket, temp, bytesToRecv, 0); | |||||
| } | |||||
| } while (bytesToRecv != 0); | |||||
| } | } | ||||
| /********************************************************************************************* | |||||
| * 功能 : 运行客户端 | |||||
| * 描述 : 初始化并且生成发送请求等待响应 | |||||
| * 输入 : 无 | |||||
| * 输出 : 无 | |||||
| *********************************************************************************************/ | |||||
| int Tcp_Client() | |||||
| bool Send_date(SOCKET clientSocket, UINT8 *Request_Message, int request_message_len) | |||||
| { | { | ||||
| SOCKET clientSocket; | |||||
| UINT8 Request_Message[600]; | |||||
| memset(Request_Message, 0, 600); | |||||
| UINT8 Response_Message[600]; | |||||
| memset(Response_Message, 0, 600); | |||||
| Clear_buf(clientSocket); //清理缓冲区 | |||||
| int status = send(clientSocket, (char*)Request_Message, request_message_len, 0); | |||||
| if (status == (-1)) | |||||
| { | |||||
| return false; | |||||
| } | |||||
| return true; | |||||
| } | |||||
| bool Abnormal_Connection(SOCKET *clientSocket) | |||||
| { | |||||
| printf("连接异常,请检查连接状态。\n"); | |||||
| printf("**************************** Press Enter To Contioun ****************************\n"); | |||||
| getchar(); | |||||
| system("cls"); | |||||
| closesocket(*clientSocket); | |||||
| WSACleanup(); | |||||
| printf("是否重新连接服务器: 1 重新连接 0 关闭本软件\n"); | |||||
| int flage = 0; | |||||
| do | do | ||||
| { | { | ||||
| clientSocket = Init_Client(); | |||||
| } while (INVALID_SOCKET == clientSocket); | |||||
| while (true) | |||||
| cin >> flage; | |||||
| cin.clear(); | |||||
| cin.sync(); | |||||
| } while (!(flage == 0 || flage == 1)); | |||||
| if (flage == 0) | |||||
| return false; | |||||
| system("cls"); | |||||
| do | |||||
| { | { | ||||
| int function_code = Input_Function_code();//交互 | |||||
| unsigned int operations_Number = Input_Operations_number(function_code); | |||||
| unsigned int starting_address = Input_Starting_address(); | |||||
| string write_date = Input_Write_date(function_code, operations_Number); | |||||
| int request_message_len = Create_TCP_Message(Request_Message, function_code, operations_Number, starting_address, write_date);//生成 | |||||
| if (!Send_date(clientSocket, Request_Message, request_message_len)) //发送 | |||||
| { | |||||
| if (Abnormal_Connection(&clientSocket)) | |||||
| continue; | |||||
| else | |||||
| break; | |||||
| } | |||||
| int Response_Message_len = Recv_date(clientSocket, Response_Message);//接收 | |||||
| Printf_Message(Request_Message, 1, request_message_len); | |||||
| Printf_Message(Response_Message, 0, Response_Message_len); | |||||
| if (Response_Message_len) | |||||
| { | |||||
| if (!Analysis_Response_Message(Response_Message, Request_Message, Response_Message_len)) | |||||
| printf("响应报文数据异常\n"); | |||||
| } | |||||
| else if(!Test_Connection_status(clientSocket)) | |||||
| { | |||||
| if (Abnormal_Connection(&clientSocket)) | |||||
| continue; | |||||
| else | |||||
| break; | |||||
| } | |||||
| else | |||||
| printf("响应超时\n"); | |||||
| *clientSocket = Init_Client(); | |||||
| } while (INVALID_SOCKET == *clientSocket); | |||||
| system("cls"); | |||||
| return true; | |||||
| } | |||||
| printf("**************************** Press Enter To Contioun ****************************\n"); | |||||
| getchar(); | |||||
| system("cls"); | |||||
| int Recv_date(SOCKET clientSocket, UINT8 *Response_Message) | |||||
| { | |||||
| int Response_Message_len = recv(clientSocket, (char*)Response_Message, 600, 0); | |||||
| if (Response_Message_len > 0) | |||||
| { | |||||
| return Response_Message_len; | |||||
| } | } | ||||
| //关闭套接字 | |||||
| closesocket(clientSocket); | |||||
| //关闭服务 | |||||
| WSACleanup(); | |||||
| return 0; | return 0; | ||||
| } | } | ||||
| bool Test_Connection_status(SOCKET clientSocket) | |||||
| { | |||||
| TIMEVAL timeout; | |||||
| timeout.tv_sec = 0; | |||||
| timeout.tv_usec = 0; | |||||
| fd_set reads; | |||||
| FD_ZERO(&reads); | |||||
| FD_SET(clientSocket, &reads); | |||||
| if (select(0, &reads, 0, 0, &timeout)) | |||||
| return false; | |||||
| else | |||||
| return true; | |||||
| } | |||||
| @@ -1,11 +1,21 @@ | |||||
| #ifndef __TCP_CLIENT_H | #ifndef __TCP_CLIENT_H | ||||
| #define __TCP_CLIENT_H | #define __TCP_CLIENT_H | ||||
| #include "common.h" | |||||
| #include <stdio.h> | |||||
| #include <winsock2.h> | |||||
| #include <WS2tcpip.h> | |||||
| #include <vector> | |||||
| #include <string> | |||||
| #include <iostream> | |||||
| #pragma comment(lib,"ws2_32.lib") | |||||
| using namespace std; | |||||
| unsigned int Count_Respone_Len(int function_code, unsigned int operations_number); | |||||
| SOCKET Init_Client(void); | |||||
| bool Send_date(SOCKET clientSocket, UINT8 *Request_Message, int request_message_len); | |||||
| bool Abnormal_Connection(SOCKET *clientSocket); | |||||
| int Recv_date(SOCKET clientSocket, UINT8 *Response_Message); | |||||
| bool Test_Connection_status(SOCKET clientSocket); | |||||
| int Tcp_Client(); | |||||
| int Create_TCP_Message(UINT8 *Message, int function_code, unsigned int operations_Number, unsigned int starting_address, string write_date); | |||||
| bool Analysis_Response_Message(UINT8 *Response_Message, UINT8 *Request_Message, int response_message_len); | |||||
| #endif | #endif | ||||
| @@ -1,6 +1,6 @@ | |||||
| #ifndef __MAIN_H | #ifndef __MAIN_H | ||||
| #define __MAIN_H | #define __MAIN_H | ||||
| #include "TCP_client.h" | |||||
| #include "Modbus_Master.h" | |||||
| #endif | #endif | ||||