Sock-08.てきとう掲示板2
今回のお題
てきとう掲示板のサーバ側をノンブロッキングにする
クライアントが送信すると即出力される…といいな
クライアントは入力待ち( std::cinとかscanfとかのブロッキング )を回避できない( マルチスレッドにすればできるけどしない )ので、ブロッキング風
サーバ
1. ヘッダ・ライブラリ~WinSockの初期化と終了処理
必要なヘッダインクルードしたり、ライブラリを追加したりする
WinSockの初期化処理と終了処理を書く
code:Server05.cpp
#include ...
#include ...
#include ...
#pragma comment( ... )
int main()
{
// WinSockの初期化処理 WinSock2.2
// WinSockの終了処理
return 0;
}
2. TCP リスンソケットの作成とリスンソケットの破棄
いったんここのリスンソケットはブロッキング状態でいく
3クライアント分accept()しないと次にいけない形
code:Server05.cpp
#include ...
#include ...
#include ...
#pragma comment( ... )
int main()
{
// WinSockの初期化処理 WinSock2.2
// TCPリスンソケットの作成
// TCPリスンソケットの破棄
// WinSockの終了処理
return 0;
}
3. リスンソケットに固定ポート番号割り当て
code:Server05.cpp
#include ...
#include ...
#include ...
#pragma comment( ... )
int main()
{
// WinSockの初期化処理 WinSock2.2
// TCPリスンソケットの作成
// SOCKADDR_IN構造体に各種設定
// bind
// TCPリスンソケットの破棄
// WinSockの終了処理
return 0;
}
4. リスンソケットをリスン状態に設定
code:Server05.cpp
#include ...
#include ...
#include ...
#pragma comment( ... )
int main()
{
// WinSockの初期化処理 WinSock2.2
// TCPリスンソケットの作成
// SOCKADDR_IN構造体に各種設定
// bind
// listen
// TCPリスンソケットの破棄
// WinSockの終了処理
return 0;
}
5. クライアントからの接続要求を待つ
今回はいったん3クライアントに固定...マジックナンバーを使って嫌な気持になる
SOCKET型の配列要素数3を用意して、ループでaccept()すればok
前述のとおり、ここではブロッキング発生
確立済みのソケットはノンブロッキングにする
ついでに、3クライアントとのコネクション切断とソケット破棄もかいとこう
code:Server05.cpp
#include ...
#include ...
#include ...
#pragma comment( ... )
int main()
{
// WinSockの初期化処理 WinSock2.2
// TCPリスンソケットの作成
// SOCKADDR_IN構造体に各種設定
// bind
// listen
// 3クライアント用のソケットを用意
SOCKET socks3; // 嫌な気持ちになる3
// 3クライアント用のソケットアドレス構造体を用意...クライアントのアドレスも使いたくなるかもしれないから
SOCKADDR_IN clientSocketAddresses3;
// 3クライアントからの接続要求を待つ
int clientCount = 0;
while( clientCount < 3 )
{
// いったんSOCKET型の変数に入れて…
SOCKET tmpSock = accept( listenSock, (SOCKADDR *)&clientSocketAddressesi, ~ );
if( tmpSock == INVALID_SOCKET )
{
// 接続要求受付エラー
}
else
{
// 接続要求受付たら、ノンブロッキングモードにして
ioctlsocket( tmpSock, ~ ); // ここ!
// 3クライアント用のソケット配列にセットする
socksclientCount = tmpSock;
clientCount++;
}
}
// 3クライアント切断・ソケット破棄
for( int i = 0; i < 3; i++ )
{
shutdown( socksi, SD_BOTH );
closesocket( socksi );
}
// TCPリスンソケットの破棄
// WinSockの終了処理
return 0;
}
6. クライアントからの文字列受信→出力
0→1→2の順に受信・出力を繰り返す
クライアントからの文字列を受信するソケットはノンブロッキングモードにしてあるので、クライアントからデータが送られていない場合は↓
エラー ...つまりrecv()の戻り値がSOCKET_ERROR
WSAGetLastError()で取得したエラーコードがWSAEWOULDBLOCK
WSAはWinSockApi
would block ... たぶんブロッキング?
code:Server05.cpp
#include ...
#include ...
#include ...
#pragma comment( ... )
int main()
{
// WinSockの初期化処理 WinSock2.2
// TCPリスンソケットの作成
// SOCKADDR_IN構造体に各種設定
// bind
// listen
// 3クライアント用のソケットを用意
SOCKET socks3; // 嫌な気持ちになる3
// 3クライアント用のソケットアドレス構造体を用意...クライアントのアドレスも使いたくなるかもしれないから
SOCKADDR_IN clientSocketAddresses3;
// 3クライアントからの接続要求を待つ
int clientCount = 0;
while( clientCount < 3 )
{
// いったんSOCKET型の変数に入れて…
SOCKET tmpSock = accept( listenSock, (SOCKADDR *)&clientSocketAddressesi, ~ );
if( tmpSock == INVALID_SOCKET )
{
// 接続要求受付エラー
}
else
{
// 接続要求受付たら、3クライアント用のソケット配列にセットする
socksclientCount = tmpSock;
clientCount++;
}
}
// 受信・出力部
while(1)
{
for( int i = 0; i < 3; i++ )
{
int ret = recv( socksi, ~ );
if( ~ )
{
// 受信データがなかったってだけで、エラーとはいえない
if( WSAGetLastError() == WSAWEOULDBLOCK )
{
// 未受信メッセージ出してもよし
}
// ちゃんとエラー
else
{
// エラー
}
}
else
{
// 受信文字列の出力
}
}
}
// 3クライアント切断・ソケット破棄
for( int i = 0; i < 3; i++ )
{
shutdown( socksi, SD_BOTH );
closesocket( socksi );
}
// TCPリスンソケットの破棄
// WinSockの終了処理
return 0;
}
おまけ
サーバとコネクション確立後のクライアントを終了させるとエラーが出るはず
コネクション確立してないソケットから受信しているから
この時のエラーコードを確認すれば、コネクション切断したクライアント分のソケットを閉じる→新たなクライアントを受け付けるってことが可能になるはず
たぶんWSAECONNRESET
https://learn.microsoft.com/ja-jp/windows/win32/winsock/windows-sockets-error-codes-2
いずれクライアントを可変にした場合、std::vector<SOCKET>とか使うので、コネクション切断したソケットは閉じるとともにvectorから削除ってこともするはず!
おまけ2
リスンソケットをノンブロッキングにしておく
std::vector<SOCKET> socks;でコネクション確立用のソケットをvectorにしておく
code:sample.cpp
while(1)
{
// accept部
SOCKET tmpSock = accept( ~ );
if( tmpSock > 0 )
{
// tmpSockをノンブロッキング
// socksにtmpSockをpush_back
}
else if( WSAGetLastError() != WSAEWOULDBLOCK )
{
// なんかえらー
}
// recv部
socks分ループ
{
recv( ~ );
if( ret == SOCKET_ERROR )
{
int errorCode = WSAGetLastError();
if( errorCode == WSAEWOULDBLOCK )
{
// 未受信だから無視…
}
else if( errorCode == コネクション切断されたエラー )
{
// socksの該当ソケットをclosesocket()
// socksの該当要素を削除
}
else
{
// なんかえらー
}
}
}
}
こんなことしてると、ソケットディスクリプタとクライアントのソケットアドレスをセットにしたクラスとか作りたくなってくるはず
Server05_omake.cpp
リスンソケットもノンブロッキングにして
クライアントをvectorで可変にして
クライアントが切断したら、vectorから削除
…ってなやつをつけたやつ
もりもりCの中にラムダ式があってきもちわるいやつ!
TCPクライアント
ソケット作って、接続要求だす
文字列入力→送信
#ネットワーク関係
#ネットワークプログラミング