WinSock2を使用したC++コード
備忘録代わりに。
WinSockにはバージョンが2.2まであり、そちらを使用した TCP通信をコンソール画面でとりあえず実装する手順。
WinSock2を使うには
最低限下記の2つは必要となります。
①WinSock2.hをインクルード
②WinSock2ライブラリへのリンク
①はそのままソース内に記述してください。
#include<WinSock2.h>
②はVisual Studioのプロジェクトのプロパティからリンカー設定しても大丈夫です。プログラムコードに記述して貰っても大丈夫です。
#pragma comment(lib, "ws2_32.lib")
WSAを初期化
Windows Socket APIを使用してソケット通信を行うため、こちらをまず初期化します。
WSAData wsaData;
int error = WSAStartup(MAKEWORD(2,2),&wsaData);
if(error != 0)
{
// エラー処理
}
MAKEWORDで使用するWinsockのバージョンを指定しているので最新である2.2以外を使用する場合は対象のバージョンに書き換える必要がある。
接続先情報の設定
クライアントがサーバと接続するために必要なのがサーバのIPアドレスやポート番号です。
こちらを今回はループバックアドレス決め打ちで指定してみます。
実際のサーバにアクセスする際はgetaddrinfo関数などでDNSからIPを取得する必要がありますので注意。
getaddrinfo関数は別途記事にする予定です。
// 接続先のIPアドレスやポート番号の情報を保持するための構造体sockaddr_in構造体を用意
struct sockaddr_in addr;
// アドレスファミリ指定(AF_INETはIPv4での通信)
addr.sin_family = AF_INET;
addr.sin_port = htons(PORT);
// 接続先IP設定
addr.sin_addr.S_un.S_un_b.s_b1 = 127;
addr.sin_addr.S_un.S_un_b.s_b2 = 0;
addr.sin_addr.S_un.S_un_b.s_b3 = 0;
addr.sin_addr.S_un.S_un_b.s_b4 = 1;
SOCKET作成
TCP通信ソケットを作成するにはsocket関数を使用します。
第1引数にアドレスファミリ指定、第2引数にソケットの種類(TCPはSOCK_STREAM)、第3引数には通信プロトコルを指定します。
今回はそれぞれAF_INET、SOCK_STREAM、IPPROTO_TCPを指定します。
SOCKET sock = socket(addr.sin_family, SOCK_STREAM, IPPROTO_TCP);
if (sock == INVALID_SOCKET) {
// エラー処理
}
※ ループバックアドレスとはコンピュータが自分自身と通信するための特殊なアドレスです。
接続
サーバへの接続を行います。
TCP通信はコネクションを確立します。そのための関数がconnect関数です。
error = connect(sock, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr));
if (error != 0) {
// エラー処理
}
ここで注意するのはconnect関数の第二引数がsockaddrにキャストして渡すことです。
connect関数の第二引数sockaddrは汎用的な設計になっており、アドレス情報を受け入れることが出来ます。
ここではsockadddr_inですが、IPベースでのアドレス情報となっています。
他にもsockaddr_unなどもあり、sockaddr*にキャストすることで色々な型を受け入れられるようになっています。
通信
TCP通信はソケットに宛先が紐づいているのでこちらを利用してデータの送受信を行っていきます。
送信サンプル
char buffer[6]="hello";
int size = send(sock, buffer, sizeof(buffer), 0);
宛先が紐づいているソケット、送信したいデータのポインタ、送信データのサイズを指定することで送信が行えます。
戻り値は送信したサイズが返ってきており、送信エラーの場合は-1が返ってきます。
受信
TCP通信は1対1の通信なので不特定からまとめて1つのソケットで受信することは出来ません。
複数の相手からデータを受信したい場合は人数分ソケットを用意し、それぞれで受信を行う必要があります。
受信サンプル
char buffer[256];
int size = recv(sock, buffer, sizeof(buffer), 0);
基本的にsend関数と同じような使い方をします。異なるのは送信データのポインタが受信したデータの保存先のポインタに変わっているところです。
指定したポインタに指定したデータのサイズ分保存されます。
終わり
次はホスト側を時間を見つけて書きます。