記事ディレクトリ
序文
タスク:
1.
Wireshark を練習して、ネットワーク パケットをキャプチャします。「Crazy Chat Room」プログラムを 2 台のコンピューター (ラップトップ win10 ホストと ubuntu 仮想マシン。ネットワーク カードのブリッジ モードを選択して 2 つのサブネット IPv4 アドレスを取得します) で実行し、wireshark を介してパケットをキャプチャします。1) このプログラムのネットワーク接続を分析します。
使用しているプロトコル (TCP、UDP) とポート番号は?
2) 盗まれたチャット情報をキャプチャ パケットから探します (英字と漢字は何らかのエンコード変換が行われている可能性があり、データ パケットは平文ではありません) 3) ネットワーク接続が TCP を使用している場合は、いつ確立されるかを分析し
ます接続 3 ウェイ ハンドシェイク、切断時の 4 ウェイ ハンドシェイク; UDP の場合、プログラムが同時に複数のコンピューター (同じチャット ルーム番号のみ) 間でチャット データを送信できる理由を説明してください。
二、
C プログラミングを使用して modbus プロトコルを完成させ、クラウド サーバーから温度と湿度のデータを読み取ります。
ソフトウェア:
クレイジー チャット ルーム:
リンク:クレイジー チャット ルーム
抽出コード: 1234
wireshark:
リンク: wireshark
抽出コード: 1234
RedPandaDE:
リンク: RedPandaDE
抽出コード: 1234
1.クレイジーチャットルーム
この実験を行う場合、同じローカル エリア ネットワークの下に 2 台のコンピューターが必要です。
1. ネットワークを構成する
コントロール パネル---->ネットワークとインターネット---->ネットワークと共有センター---->アダプターの設定の変更:
2 台のコンピューターが接続されているネットワークのみを残して、他のゲートウェイを無効にします。
2.部屋を作る
両方のコンピューターに Crazy Chat Room をダウンロードする必要があります。
crazychat.exe をクリックしてルームを作成します:
ルーム番号 8888 を作成し、名前を付けます:
3.お互いにメッセージを送る
2. Wireshark がネットワーク パケットをキャプチャする
Wireshark のインストールはずっと次へと同意します。
1.対応するパッケージを見つける
クレイジーなチャット ルームでメッセージを送信すると、wireshark はすぐに IP アドレスがブロードキャスト アドレス 255.255.255.255 であるパッケージを見つけます。
上の図でキャプチャされたパケット分析から、
このチャット プログラムが UDP プロトコルと 255.255.255.255 のポート番号を使用していることがわかります。
2. キャプチャされたパッケージ内の情報を分析する
キャプチャされたパッケージをダブルクリックして、特定の情報を表示します。
数字:
英語:
中国語:
上の図の分析から、英字と数字は通常、プレーン テキストで表示できますが、漢字はエンコードされた後はプレーン テキストで表示できないことがわかります.3 つの同一の中国語の 16 進数コードを送信した後、3 バイトがエンコードされていることがわかりました。漢字を入力してエンコーディングを推測します. 形式は UTF-8 です.
3. Modbus プロトコルの概要
1. Modbus プロトコルは、1979 年に Modicon (現 Schneider Electric) によって発行された、プログラマブル ロジック コントローラ (PLC) 通信を使用するためのシリアル通信プロトコルです。Modbusプロトコルは、産業分野の通信プロトコルの業界標準となったアプリケーション層プロトコルであり、産業用電子機器間の一般的な接続方式です。
2. Modbus はマスター/スレーブ アーキテクチャ プロトコルです. 1 つのノードがマスター ノードであり, Modbus プロトコルを使用して通信に参加する他のノードはスレーブ ノードです. 各スレーブ デバイスには一意のアドレスがあります. マスター ノードとして指定されたノードのみがコマンドを開始できます。すべての Modbus データ フレームには、伝送の正確性を保証するためのチェック コードが含まれています。基本的な ModBus コマンドは、スレーブ デバイスにそのレジスタの 1 つの値を変更し、I/O ポートを制御または読み取り、1 つまたは複数のレジスタのデータを送り返すようにデバイスに指示することができます。
1. Modbus マスター/スレーブプロトコルの原理
Modbus シリアル リンク プロトコルは、マスター/スレーブ プロトコルです。同時に、バスに接続できるマスター ステーションは 1 つだけで、同じシリアル バスに 1 つまたは複数のスレーブ ステーション (最大数 247) を接続できます。Modbus 通信は、常にマスタ ステーションによって開始されます。スレーブ ステーションがマスタ ステーションから要求を受信しない場合、データは送信されません。マスタ ステーションは同時に 1 つの Modbus トランザクションしか開始できず、スレーブ ステーションは相互に通信できません。
2. 一般的な Modbus フレーム構造 - プロトコル データ ユニット (PDU)
Modbus プロトコルは、基礎となる通信層から独立した単純なプロトコル データ ユニット (PDU) を定義します. 特定のバスまたはネットワーク上の Modbus プロトコル マッピングは、アプリケーション データ ユニット (ADU) にいくつかの追加フィールドを導入できます.
3. 2 つの Modbus シリアル伝送モード
RTU モード: 各 8 ビット バイトには 2 つの 4 ビット 16 進文字が含まれます.利点は、同じボー レートで、ASCII よりも多くのデータを送信できることですが、各メッセージは連続したデータ ストリーミングでなければなりません.
ASCII モード: 情報の 8 ビット バイトごとに 2 つの ASCII 文字が必要です。これには、文字の送信間隔をエラーなしで 1 秒にできるという利点があります。
4. ModbusTCP 通信構造
Modbus TCP/IP 用の通信デバイス: TCP/IP ネットワークに接続された Modbus TCP/IP クライアントおよびサーバー デバイス。
TCP/IP ネットワークとシリアル リンク サブネット間を相互接続するブリッジ、ルーター、ゲートウェイなどの相互接続デバイス。
4. C プログラミングは modbus プロトコルを完了し、クラウド サーバーから温度と湿度のデータを読み取ります。
RedPandaDE を開き、新しいプロジェクトを作成します。
1. 構成コード
1. ソケット dll を初期化し、IP 経由でサーバーの対応するポートに接続します。
WORD winsock_version = MAKEWORD(2,2);
WSADATA wsa_data;
if (WSAStartup(winsock_version, &wsa_data) != 0) {
printf("Failed to init socket!\n");
return 1;
}
SOCKET client_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (client_socket == INVALID_SOCKET) {
printf("Failed to create server socket!\n");
return 2;
}
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(PORT);
server_addr.sin_addr.S_un.S_addr = inet_addr(SERVER_IP);
if (connect(client_socket, (LPSOCKADDR)&server_addr, sizeof(server_addr)) == SOCKET_ERROR) {
printf("Failed to connect server: %ld !\n", GetLastError());
return 3;
}
2. 確認コードを生成します。
uint16_t CRC_16(uint8_t *temp)
{
uint8_t i,j;
uint16_t CRC_1 = 0xFFFF; //声明CRC寄存区,也就是步骤1
for(i = 0;i < 6;i++) //这里的for循环说的是步骤6中的重复步骤 2 到步骤 5
{
CRC_1 ^= temp[i]; //这里就是步骤2,进行异或运算
for(j = 0;j < 8;j++) //用来将异或后的低八位全部移出的for循环
{
if(CRC_1 & 0x01) //判断低八位的最后一位是否为1,为1时执行下列语句,也就是步骤3说的移位判断与步骤5说的右移8次
{
/*一定要先移位,再异或*/
CRC_1 >>=1; //移位后再异或,就是步骤4
CRC_1 ^= 0xA001; //0xA001为0x8005的逆序
}
else //若不为1,则直接移位。
{
CRC_1 >>=1;
}
}
}
// CRC_1 = (((CRC_1 & 0xFF)<<8) + (CRC_1>>8));
// printf("%04x\r\n",CRC_1); //用于打印检测CRC校验码
return(CRC_1);
}
3. 温度と湿度のデータを取得します。
int ret = recv(client_socket, recv_data, BUFFER_SIZE, 0);
if (ret < 0) {
printf("Failed to receive data!\n");
break;
}
recv_data[ret]=0; // correctly ends received string
char yb[4],wd[4];
for(int i=0;i<4;i++){
//TODO
yb[i] = recv_data[4+i];
wd[i] = recv_data[8+i];
}
float mic = hexToDec(yb)/100.0;
float strain_temp = hexToDec(wd)/100.0;
printf("应变:%f\r\n",mic);
printf("温度:%f\r\n",strain_temp);
4. 完全なコード
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <winsock2.h>
#include <math.h>
#include "stdint.h"
#define length_8 8 //定义一个宏,为传入8位16进制数的个数
#define PORT 8002
#define SERVER_IP "123.56.90.74"
#define BUFFER_SIZE 4196
const char* kExitFlag = "exit";
/* 返回ch字符在sign数组中的序号 */
int getIndexOfSigns(char ch)
{
if(ch >= '0' && ch <= '9')
{
return ch - '0';
}
if(ch >= 'A' && ch <='F')
{
return ch - 'A' + 10;
}
if(ch >= 'a' && ch <= 'f')
{
return ch - 'a' + 10;
}
return -1;
}
/* 十六进制数转换为十进制数 */
int hexToDec(char *source)
{
int sum = 0;
int t = 1;
int i, len=4;
char low,high;
for(int i=0,j=7;i<4;i++){
//TODO
high = (source[i] & 0xf0)>>4;
low = source[i] & 0x0f;
sum += high*pow(16,j--)+low*pow(16,j--);
}
return sum;
}
const unsigned char *fromhex(const char *str)
{
static unsigned char buf[512];
size_t len = strlen(str) / 2;
if (len > 512) len = 512;
for (size_t i = 0; i < len; i++) {
unsigned char c = 0;
if (str[i * 2] >= '0' && str[i*2] <= '9')
c += (str[i * 2] - '0') << 4;
if ((str[i * 2] & ~0x20) >= 'A' && (str[i*2] & ~0x20) <= 'F')
c += (10 + (str[i * 2] & ~0x20) - 'A') << 4;
if (str[i * 2 + 1] >= '0' && str[i * 2 + 1] <= '9')
c += (str[i * 2 + 1] - '0');
if ((str[i * 2 + 1] & ~0x20) >= 'A' && (str[i * 2 + 1] & ~0x20) <= 'F')
c += (10 + (str[i * 2 + 1] & ~0x20) - 'A');
buf[i] = c;
}
return buf;
}
uint16_t CRC_16(uint8_t *temp)
{
uint8_t i,j;
uint16_t CRC_1 = 0xFFFF; //声明CRC寄存区,也就是步骤1
for(i = 0;i < 6;i++) //这里的for循环说的是步骤6中的重复步骤 2 到步骤 5
{
CRC_1 ^= temp[i]; //这里就是步骤2,进行异或运算
for(j = 0;j < 8;j++) //用来将异或后的低八位全部移出的for循环
{
if(CRC_1 & 0x01) //判断低八位的最后一位是否为1,为1时执行下列语句,也就是步骤3说的移位判断与步骤5说的右移8次
{
/*一定要先移位,再异或*/
CRC_1 >>=1; //移位后再异或,就是步骤4
CRC_1 ^= 0xA001; //0xA001为0x8005的逆序
}
else //若不为1,则直接移位。
{
CRC_1 >>=1;
}
}
}
// CRC_1 = (((CRC_1 & 0xFF)<<8) + (CRC_1>>8));
// printf("%04x\r\n",CRC_1); //用于打印检测CRC校验码
return(CRC_1);
}
int main() {
// 初始化socket dll。
WORD winsock_version = MAKEWORD(2,2);
WSADATA wsa_data;
if (WSAStartup(winsock_version, &wsa_data) != 0) {
printf("Failed to init socket!\n");
return 1;
}
SOCKET client_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (client_socket == INVALID_SOCKET) {
printf("Failed to create server socket!\n");
return 2;
}
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(PORT);
server_addr.sin_addr.S_un.S_addr = inet_addr(SERVER_IP);
if (connect(client_socket, (LPSOCKADDR)&server_addr, sizeof(server_addr)) == SOCKET_ERROR) {
printf("Failed to connect server: %ld !\n", GetLastError());
return 3;
}
char recv_data[BUFFER_SIZE+1];
while (true) {
uint8_t data[length_8];
printf("0+传感器编号(1,2,3,4,5)0300010002\r\n");
scanf("%s",data);
uint16_t crc;
unsigned char * cmd;
char crc1[8];
cmd = fromhex(data);
crc = CRC_16(cmd);
uint8_t a = 0xFF;
for(int i=0;i<6;i++){
//TODO
crc1[i] = cmd[i];
}
crc1[6] = a & crc;
crc1[7] = (crc >> 8) & a;
if (send(client_socket, crc1, 8, 0) < 0) {
printf("Failed to send data!\n");
break;
}
int ret = recv(client_socket, recv_data, BUFFER_SIZE, 0);
if (ret < 0) {
printf("Failed to receive data!\n");
break;
}
recv_data[ret]=0; // correctly ends received string
char yb[4],wd[4];
for(int i=0;i<4;i++){
//TODO
yb[i] = recv_data[4+i];
wd[i] = recv_data[8+i];
}
float mic = hexToDec(yb)/100.0;
float strain_temp = hexToDec(wd)/100.0;
printf("应变:%f\r\n",mic);
printf("温度:%f\r\n",strain_temp);
// printf("Receive data from server: \"%x\"\n",recv_data);
if (strcmp(data,kExitFlag)==0) {
printf("Exit!\n");
break;
}
}
closesocket(client_socket);
WSACleanup();
return 0;
}
2.効果
V. まとめ
メッセージの送信時にブロードキャストアドレス 255.255.255.255 を使用するため、複数の相手がメッセージを受信できます。Modbus 通信プロトコルと ModbusTCP 通信プロトコルおよび構造の基本原則を理解します。
6.参考文献
Wireshark を使用してチャット情報をキャプチャします (ローカル エリア ネットワークでの udp 通信)
C 言語を使用して modbus プロトコルを完成させ、クラウド サーバーから温度と湿度の情報を読み取ります