ラズパイとPCでソケット通信します!
過去に、WindowsPC同士でクライアントとサーバに分かれてソケット通信をするブログを書きましたが、今回はRaspberryPiでサーバを建てて、WindowsPCから文字列を送るプログラムを作成します。
▼前回の記事
⇒【Socket通信】WindowsPC同士でサーバとクライアントで通信!
ソース(ラズパイ側)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 |
#include <stdio.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/ioctl.h> #include <netinet/in.h> #include <net/if.h> #include <unistd.h> /* for close */ #include <arpa/inet.h> #define PORT_TEMP 12345 //転送用構造体 typedef struct { float prm_pc_to_soc[20]; //PC→SoCへの転送パラメータ float prm_soc_to_pc[20]; //SoC→PChへの転送パラメータ } Transfer_data; char buf[1024]; int main() { int sock0; struct sockaddr_in addr; struct sockaddr_in client; int len; int sock; int yes = 1; int fd; struct ifreq ifr; fd = socket(AF_INET, SOCK_DGRAM, 0); ioctl(fd, SIOCGIFADDR, &ifr); close(fd); //パラメータ転送構造体 Transfer_data td_recv; Transfer_data td_send; //転送テスト用データ float send0 = 30; float send1 = 444; //ソケット作成 sock0 = socket(AF_INET, SOCK_STREAM, 0); //ソケット作成エラーチェック if(sock0 < 0) { perror("socket"); return -1; } //接続先情報設定 addr.sin_family = AF_INET; addr.sin_port = htons(PORT_TEMP); addr.sin_addr.s_addr = INADDR_ANY; //ソケットのオプション設定 setsockopt(sock0, SOL_SOCKET, SO_REUSEADDR, (const char *)&yes, sizeof(yes)); //アドレスとソケットディスクリプタを関連付け if (bind(sock0, (struct sockaddr *)&addr, sizeof(addr)) != 0) { perror("bind"); return -1; } //接続待ちに設定(acceptにて接続要求を受ける) if(listen(sock0, 5) != 0) { perror("listen"); return -1; } printf("IPアドレス:%s\n", inet_ntoa(((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr)); puts("waiting connect client..."); //接続要求待ちループ while (1) { len = sizeof(client); sock = accept(sock0, (struct sockaddr *)&client, &len); //接続エラー判定 if (sock < 0) { //エラー時はループを抜けて処理を終了 perror("accept"); break; } //データ受信 // 送られてきたメッセージ(COMMAND)を受け取ります memset(buf, 0, 1024); recv(sock, buf, 1024, 0); if (buf[0] == '\0') strcpy(buf, "NULL"); puts(buf); } //ソケットクローズ close(sock0); printf( "Close socket...\n" ); return 0; } |
ソース(WindowsPC側)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 |
#include <winsock2.h> #include <windows.h> #include <stdio.h> #include <conio.h> WSADATA wsaData; struct sockaddr_in addr; SOCKET sockw; #define PORT_TEMP 12345 //PORT番号設定(クライアント/サーバで必ず合わせる) // IPアドレス/ホスト名の取得 char* getIpHost(void) { HOSTENT *lpHost; // ホスト情報を格納する構造体 IN_ADDR inaddr; // IPアドレスを格納する構造体 char szBuf[256], szIP[16]; // ホスト名/IPアドレスを格納する配列 // ローカルマシンのホスト名を取得する gethostname(szBuf, (int)sizeof(szBuf)); //printf("HOST Name : %s\n", szBuf); // ホスト情報を取得 lpHost = gethostbyname(szBuf); // IPアドレスを取得 for (int i = 0; lpHost->h_addr_list[i]; i++) { memcpy(&inaddr, lpHost->h_addr_list[i], 4); strcpy(szIP, inet_ntoa(inaddr)); } return szIP; } int sendMessage(char* sendIP, char* message) { char buf[40]; int n, i; // winsock2の初期化 if (WSAStartup(MAKEWORD(2, 0), &wsaData)) { puts("reset winsock failed"); return -1; } // Server に connect して COMMAND を送信する // ソケットの作成 sockw = socket(AF_INET, SOCK_STREAM, 0); if (sockw == INVALID_SOCKET) { puts("make socket failed"); return -1; } //char ip_add[30]; //for (int i = 0; i < 30; i++) // ip_add[i] = NULL; //strcpy(ip_add, argv[1]); // 接続先指定用構造体の準備 addr.sin_family = AF_INET; addr.sin_port = htons(PORT_TEMP); addr.sin_addr.S_un.S_addr = inet_addr(sendIP); // サーバーに接続 if (connect(sockw, (struct sockaddr *)&addr, sizeof(addr))) { puts("connect failed"); return -1; } // Server にリクエスト(COMMAND)を送信 n = send(sockw, message, strlen(message), 0); if (n < 0) { puts("send failed"); return -1; } // ソケットのクローズ closesocket(sockw); // winsock2 の終了処理 WSACleanup(); return 0; } int main(int argc,char* argv[]) { // WinSockの初期化 WSADATA wsaData; char myIP[16]; char sendIP[16]; char message[256]; for (int i = 0; i < 16; i++) { myIP[i] = NULL; sendIP[i] = NULL; for (int j = 0; j < 16; j++) { message[i*j] = NULL; } } // エラー処理 if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0) { printf("WSAStartup Error\n"); return -1; } // IPアドレス/ホスト名の取得 strcpy(myIP, getIpHost()); WSACleanup(); printf("myIP : %s\n", myIP); if (argc == 3 && *argv[1]=='/') {//argv[1]:IPアドレス argv[2]:送信内容 int strNum = strlen(argv[1]) - 1; strncpy(sendIP, argv[1]+1 ,strNum); strcpy(message, argv[2]); printf("IP:%sに%sを送信します\n", sendIP, message); sendMessage(sendIP, message); } else if (argc == 1) { } else { printf("clTest.exe </ip mes>\nip = ip address\nmes = send message\n"); } return 0; } |
解説
過去に書いたWindows同士の記事と大きくは変わりません。
Windows系のライブラリを使う代わりにLinuxで使えるライブラリを使うため、一部の関数が変わるだけって感じです。
クライアント(WindowsPC)側のプログラムは、実行時にコマンドライン引数として、
- IPアドレス
- 送信メッセージ
の2つです。
尚、IPアドレスは必ず「/」(スラッシュ)を頭に付けてください。(例:/0.0.0.0)
引数が多いor少ない、またはIPアドレスの頭にスラッシュを付け忘れているときは仕様説明が出力されます。
サーバ側は起動時に自信のIPアドレスが出るようになっているため、そのIPアドレスをクライアント側のプログラムのコマンドライン引数に使用してください。
サーバは無限ループしているため、クライアント側からの信号を常に受け付ける一方で、クライアント側は一度送信すると終了するようになっています。
参考にした記事
⇒【Geekなページ】インターフェースのIPアドレスを取得する
今回のブログ曲
今回投稿中に聴いていた曲はこちら