瀏覽代碼

调整TCP主站代码结构

master
zcn1123 4 年之前
父節點
當前提交
f101e23842
共有 9 個檔案被更改,包括 533 行新增484 行删除
  1. +96
    -206
      Modbus_communication/Modbus_TCP/Interactive.cpp
  2. +16
    -15
      Modbus_communication/Modbus_TCP/Interactive.h
  3. +235
    -0
      Modbus_communication/Modbus_TCP/Modbus_Master.cpp
  4. +12
    -0
      Modbus_communication/Modbus_TCP/Modbus_Master.h
  5. +6
    -4
      Modbus_communication/Modbus_TCP/Modbus_TCP.vcxproj
  6. +10
    -4
      Modbus_communication/Modbus_TCP/Modbus_TCP.vcxproj.filters
  7. +143
    -250
      Modbus_communication/Modbus_TCP/TCP_client.cpp
  8. +14
    -4
      Modbus_communication/Modbus_TCP/TCP_client.h
  9. +1
    -1
      Modbus_communication/Modbus_TCP/main.h

Modbus_communication/Modbus_TCP/common.cpp → Modbus_communication/Modbus_TCP/Interactive.cpp 查看文件

@@ -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中选择一个功能码
@@ -218,33 +158,12 @@ unsigned int Count_Write_date_number(int function_code, unsigned int operations_
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对应功能码类型的操作数量
* 输出 : Write_date 写入的数据
* 输出 : Write_date 写入的数据
*********************************************************************************************/
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);
if (function_code == 0x01 || function_code == 0x03)
return write_date;//读线圈/寄存器不需要输入写入数据
printf("请输入N组写入数据(2位16进制为一组,空格间隔)例如:00 00 01 00 20 05\n");
do
{
@@ -272,6 +191,67 @@ string Input_Write_date(int function_code, unsigned int operations_Number)
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);
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:";
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:";

/* 打开文件用于读写 */
if ((fp = fopen("Modbus_TCP_log.txt", "a+")) == NULL)
{
printf("打开文件失败");
return ;
return;
}
if (flage == 1)
{
@@ -304,7 +284,7 @@ void Log_Note(UINT8 *Message, int flage, int message_len)
{
fprintf(fp, "%02X ", Message[i]);
}
fprintf(fp,"\n");
fprintf(fp, "\n");
}
else
{
@@ -319,136 +299,46 @@ void Log_Note(UINT8 *Message, int flage, int message_len)
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;
}

Modbus_communication/Modbus_TCP/common.h → Modbus_communication/Modbus_TCP/Interactive.h 查看文件

@@ -1,20 +1,27 @@
#ifndef __COMMON_H
#define __COMMON_H
#ifndef __INTERACTIVE_H
#define __INTERACTIVE_H


#include <stdio.h>
#include <winsock2.h>
#include <WS2tcpip.h>
#include <Windows.h>
#include <vector>
#include <string>
#include <iostream>
#include <time.h>
#pragma comment(lib,"ws2_32.lib")
#include <iostream>
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);
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);
string Input_Write_date(int function_code, unsigned int operations_Number);
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

+ 235
- 0
Modbus_communication/Modbus_TCP/Modbus_Master.cpp 查看文件

@@ -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;
}

+ 12
- 0
Modbus_communication/Modbus_TCP/Modbus_Master.h 查看文件

@@ -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

+ 6
- 4
Modbus_communication/Modbus_TCP/Modbus_TCP.vcxproj 查看文件

@@ -66,14 +66,16 @@
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="common.cpp" />
<ClCompile Include="main.cpp" />
<ClCompile Include="TCP_client.cpp" />
<ClCompile Include="Interactive.cpp" />
<ClCompile Include="main.cpp" />
<ClCompile Include="Modbus_Master.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="common.h" />
<ClInclude Include="main.h" />
<ClInclude Include="TCP_client.h" />
<ClInclude Include="Interactive.h" />
<ClInclude Include="main.h" />
<ClInclude Include="Modbus_Master.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">


+ 10
- 4
Modbus_communication/Modbus_TCP/Modbus_TCP.vcxproj.filters 查看文件

@@ -18,7 +18,10 @@
<ClCompile Include="main.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="common.cpp">
<ClCompile Include="Interactive.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="Modbus_Master.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="TCP_client.cpp">
@@ -26,13 +29,16 @@
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="common.h">
<ClInclude Include="main.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="TCP_client.h">
<ClInclude Include="Interactive.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="main.h">
<ClInclude Include="Modbus_Master.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="TCP_client.h">
<Filter>头文件</Filter>
</ClInclude>
</ItemGroup>

+ 143
- 250
Modbus_communication/Modbus_TCP/TCP_client.cpp 查看文件

@@ -1,303 +1,196 @@
#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;
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;
}

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
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
{
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;
}

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;
}

+ 14
- 4
Modbus_communication/Modbus_TCP/TCP_client.h 查看文件

@@ -1,11 +1,21 @@
#ifndef __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

+ 1
- 1
Modbus_communication/Modbus_TCP/main.h 查看文件

@@ -1,6 +1,6 @@
#ifndef __MAIN_H
#define __MAIN_H

#include "TCP_client.h"
#include "Modbus_Master.h"

#endif

Loading…
取消
儲存