おおまかな流れ
サーバー側
- socket関数で新規接続を待つ為のソケットを作る
- bind関数でポート番号を関連付ける
- listen関数でソケットが外部からの接続を受け付けるようにする。
- accept関数を呼ぶ。この関数は接続要求が来たら接続済みソケットを作る。タイムアウトで帰ってくることもあるので、必要ならループ中などで再度呼ぶ。
- 接続済みソケットを使ってsend/recv関数でデータをやり取りする
- closesocket関数で終了
※新規接続の待ちうけに使うソケットはリスニングソケットと言うらしい
クライアント側
- socket関数でソケットを作る
- connect関数でサーバーに接続する
- send/recv関数でデータをやり取りする
- closesocket関数で終了
WinSockでやる場合
初期化時と終了時にちょっとだけ手間が増えます。
初期化:WSAStartup関数を呼ぶ
第一引数はWINSOCK_VERSIONを渡しとけば大丈夫です。
第二引数はWSADATA構造体のインスタンスへのポインタを渡します。
戻り値が0以外の場合は失敗という意味。
終了:WSACleanup関数を呼ぶ
引数は特にありません。
クライアント側で使う関数の詳細
SOCKET socket (int af, int type, int protocol);
ソケットを作ります。
第一引数afはAddressFamiryの略で、とりあえずAF_INETを渡せばおk
第二引数はソケットの種類を選べる。TCPならSOCK_STREAM、UDPならSOCK_DGRAMを渡す。
第三引数はプロトコルの選択。0を渡しておけば自動的に選択される。
int connect (SOCKET s, const struct sockaddr FAR* name, int namelen);
サーバーに接続します。
第一引数はsocket関数で作ったソケットを指定します。
第二引数は接続先の情報を指定します。やり方は後述。
第三引数は第二引数で渡す構造体のサイズです。sizeof演算子をつかっておけば間違いありません。
戻り値が0なら成功、それ以外ならエラーです。
接続先の情報は一般的なIPv4で指定する場合、sockaddr_in構造体のインスタンスに情報を格納してそのポインタを渡します。なお、C++ではsockaddr型のポインタへキャストする必要があります。
メンバ変数としてsin_family、sin_port、sin_addr、sin_zeroがあります。最初の三つのそれぞれにはAF_INET、接続先ポート番号、接続先アドレスを格納します。最後のsin_zeroはパディング用のようなので気にしないで大丈夫です。
int send(SOCKET s, const char FAR * buf, int len, int flags);
データを送信します。
第一引数にはソケットを指定します。
第二引数には送信するデータの開始アドレスを指定します。
第三引数には送信するデータの長さを指定します。
第四引数は基本的に0で大丈夫です。
戻り値は送信したデータの長さが帰ってきます。
int recv(SOCKET s, char FAR* buf, int len, int flags);
データを受信します。
第一引数にはソケットを指定します。
第二引数にはデータを受け取るバッファのアドレスを指定します。
第三引数にはバッファの長さを指定します。
第四引数はこれまた0でいいです。
戻り値は受け取ったデータの長さです。
int closesocket (SOCKET s);
ソケットを終了します。
留意点
接続の相手がsendを矢継ぎ早に呼んだ場合、recvでデータを受け取る時にデータがくっついたようになります。一つ一つのデータの終端に約束事を設けるなどしないとバグの元になります。
TCP接続はデータをかなり確実に送受信できますが、若干遅いです。UDP接続は送信完了のチェックや送信順番のチェックをしないので、高速ですがデータが壊れやすいです。場合に応じて使い分けるべきでしょう。
以下ちょっとCM
0 件のコメント:
コメントを投稿