完成した完成した詐欺(マテ;
次の3点に対応した。
- 行単位送受信処理の行の解釈・レスポンス送出処理からの分離(コルーチン化)
- 受信タイムアウト設定
- そこそこ強固なエラー処理
Receive()やSend()の戻り値がSOCKET_ERRORかどうか見るようにしたり色々。
今や上のEchoサーバーもどきは次のように書ける。
// SocketTest.cpp : コンソール アプリケーションのエントリ ポイントを定義します。 // #include "stdafx.h" #include "Telex.h" const int CSAPort = 4081; const int LineSizeMax = 128; const UINT TCTimeout = 10 * 1000; #ifdef _UNICODE # define ostringstream wostringstream # define strlen wstrlen #endif /// エラーメッセージを表示します。 void error(LPCSTR func, LPCTSTR msg) { ostringstream err; err << func << _T("(): ") << msg << _T(" (") << hex << showbase << GetLastError() << _T(")"); AfxMessageBox(err.str().c_str()); } #define ERR(msg) (error(__FUNCTION__, _T(msg))) /// Echoサーバーのサンプルです。 void SampleServer(LPVOID param) { CSocket mstSock; CTimeoutSocket slvSock; if (!mstSock.Create(CSAPort)) { ERR("Create失敗"); return; } if (!mstSock.Listen(1)) { ERR("Listen失敗"); return; } for (;;) { if (!mstSock.Accept(slvSock)) { ERR("Accept失敗"); return; } Telex tty(slvSock, LineSizeMax); // 行単位送受信オブジェクト tty.SetTimeout(TCTimeout); // タイムアウト時定数設定 int line = 0; for (;;) { // 1行受信 LPCSTR str; int len; if (!tty.Receive(str, len)) { if (tty.PopTimeoutState()) { cout << "******* Timeout (" << TCTimeout << " ms Expired) *******" << endl; break; } else { error(__FUNCTION__, tty.LastErr()); return; } } // エコーバック if (!tty.Send(str, len)) { error(__FUNCTION__, tty.LastErr()); return; } // サーバー側にも表示 cout << ++line << ">" << str << " (" << len << ")" << endl; } } } /// メインルーチンです。 int _tmain(int argc, _TCHAR* argv[]) { if (!AfxSocketInit()) { ERR("AfxSocketInit()失敗"); return -1; } SampleServer(0); return 0; }
Telexクラス(笑)が行単位の送受信、それのみを担当する。
↓↓↓↓そのヘッダファイル↓↓↓↓
#pragma once /// LFを行末とする電文1行づつの送受機能を提供します。 class Telex { private: CTimeoutSocket& socket; const int LineSizeMax; // バッファ const int bufSz; BYTE* const buf; // 行切り出し制御 int wp, rp; bool lineFeed; bool bTimeout; UINT uTimeout; // エラーメッセージ TCHAR lastErr[128]; /// エラーを記録します。 void Error(LPCSTR func, LPCTSTR msg); public: /// 新しいインスタンスを初期化します。 Telex(CTimeoutSocket& sock, int lszmax = 128) : socket(sock), LineSizeMax(lszmax), bufSz(2 * lszmax), buf(new BYTE[2 * LineSizeMax]) { wp = rp = 0; lineFeed = false; bTimeout = false; memset(lastErr, 0, sizeof(lastErr)); } /// インスタンスを破棄します。 virtual ~Telex() { delete[] buf; } /// 1行受信し、内容へのポインタと長さ(バイト数)を返します。 /// 受信内容はTelexオブジェクトが確保したバッファで保持されます。 bool Receive(LPCSTR& p, int& len); /// 1行受信し、内容へのポインタを返します。 /// 受信内容はTelexオブジェクトが確保したバッファで保持されます。 bool Receive(LPCSTR& p) { int len; Receive(p, len); } /*= (両Receive()に関する補足) * - 返されたポインタpはヌル終端文字列としても扱えます。 * - 文字列pにLFは含まれません。 * - LFの直前にも制御文字が並んでいた場合、それらも文字列pに含まれません。 * - 文字数が制限を超える行については超過部分を無視します。 * - 受信内容自体にヌル文字が含まれるか否かは関知しません。 * (含まれていた場合、strlen(p) < lenになります。) */ /// pが指すlenバイトのデータにLFを付加して送信します。 /// データにヌル文字が含まれるか否かは関知しません。 bool Send(LPCSTR str, int len); /// ヌル終端文字列pにLFを付加して送信します。 bool Send(LPCSTR str); /// 最後に起きたエラーを取得します。 LPCTSTR LastErr() { return lastErr; } /// タイムアウト時定数を設定します。0でタイムアウト無しです。 void SetTimeout(UINT tmo) { uTimeout = tmo; } /// 受信タイムアウトが発生したか否かを取得します(リードクリア)。 bool PopTimeoutState() { bool val = bTimeout; bTimeout = false; return val; } };
実行結果。
(前略) 8426>To_Move:+ (9) 8427>BEGIN Time (10) 8428>Time_Unit:1sec (14) 8429>Total_Time:1500 (15) 8430>Least_Time_Per_Move:1 (21) 8431>END Time (8) 8432>BEGIN Position (14) 8433>P1-KY-KE-GI-KI-OU-KI-GI-KE-KY (29) 8434>P2 * -HI * * * * * -KA * (28) 8435>P3-FU-FU-FU-FU-FU-FU-FU-FU-FU (29) 8436>P4 * * * * * * * * * (28) 8437>P5 * * * * * * * * * (28) 8438>P6 * * * * * * * * * (28) 8439>P7+FU+FU+FU+FU+FU+FU+FU+FU+FU (29) 8440>P8 * +KA * * * * * +HI * (28) 8441>P9+KY+KE+GI+KI+OU+KI+GI+KE+KY (29) 8442>P+ (2) 8443>P- (2) 8444>+ (1) 8445>+2726FU,T12 (11) 8446>-3334FU,T6 (10) 8447>END Position (12) 8448>END Game_Summary (16) ####### Timeout (10000 ms Expired) #######
タイムアウトやエラーが起きたらコネクションをクローズして接続待ちに戻る、、、、
、、、、
、、、、
何を必死になってEchoサーバー作ってるんだ漏れorz