前回は、クライアントからの送信、サーバでの受信をパケット化しました。
今回は、サーバからの送信、クライアントでの受信をパケット化します。
サーバからの送信では、ちょっと注意が必要になります。
各クライアントに対して、サーバは送受信スレッドを作成し、処理しています。
クライアントAからのデータを受信するのは送受信スレッドAです。
クライアントBへデータ送信ができるのは送受信スレッドBです。
つまり、送受信スレッドAで受信したデータを送受信スレッドB, Cの送信バッファに
渡さなければなりません。
クライアントBから送られてきたデータも同様に送受信スレッドA, Cの送信バッファに
渡されます。(下図を見てください)

ここで送受信スレッドAとBが、何も考えずにCの送信バッファにデータを書き込むと
2種類のデータが混じってしまってどうしようもなくなってしまいます。
正しく送信するには、異なるデータが混ざらないようにしなければなしません。
これを送信データのシリアライズ(直列化)といいます。
方法としては、送信バッファに書きこむ際に同期オブジェクトを使って、排他的に
書き込む方法や、書き込むスレッドは一つだけに限定する方法などがあります。

今回は、書き込むスレッドを一つにしてみましょう。
このスレッドはすべての送受信スレッドの状態を知っている必要があります。
また、送信すべきデータの有無の検査、送信バッファへの書き込みを定期的(高速)に
行う必要があります。
専用のスレッドを作るのが良いのですが、今回はメインスレッドのDoAccept関数を
使うことにします。(約10msecでループし続けています)

それでは、SimpleServerTransFile(Linux)にチャットメッセージパケット送信機能を
実装することにしましょう。次のような感じです。

1.各クライアントに対応した送受信スレッドでメッセージをパケット化し(SendMessagePacket)、
一時バッファに格納する関数SetDataForDistributeChatを呼びます。
(*)これは、実装済みのRecvMessagePacketから、すでにクライアントに実装済みの
SendMessagePacketを参考に作成する関数を呼び出すようにします。
一時バッファに格納したデータの読み出し関数をGetRecvDataから
GetDataForDistributeChatに変更します。

2.すべてのスレッドの情報(クラスのインスタンス)を知っている単一のスレッド
(ここではメインスレッド)でパケット化された情報を読みだし、対応する送受信
スレッドの送信バッファに渡す関数SerializeSendDataを作ります。
(*)実装済みのGetRecvDataAndSetSendDataをSerializeSendDataに変更することに
します。
送信したいデータの取得関数をGetRecvDataからGetDataForDistributeChatに変更
します。

3.送受信スレッドは送信バッファにデータがあれば送信を実行します。
(*)ここはすでに実装されています。

【SendRecvThread.h】 #pragma once #include “ThreadJob.h” #include “define.h” class CMySyncObject; // このクラスの使用することを宣言 typedef struct { SOCKET fdClient; // 接続済みソケット(acceptの結果) CMySyncObject *pCMySyncObject; // 同期オブジェクト } ConnectionInfoRec; class CSendRecvThread : public CThreadJob { public: CSendRecvThread(ConnectionInfoRec *pConInfo); // パラメータをコンストラクタで渡す ~CSendRecvThread(); // 基底クラスの関数をオーバーライドする // C++11で明示的にoverrideを書くことが出来るようになりました // 基底クラスの当該関数にvirtualが書いていないとエラーを出してくれます UINT DoWork() override; // DoRecvで実施している内容を記述 BOOL SetSendData(char *pcData, int iSize); // 送信データの設定 // int GetRecvData(char **ppcData); // ★削除 これは下の関数に変更された int GetDataForDistributeChat(char **ppcData); // ★1.一時バッファに格納されたチャットパケットの取得 BOOL IsZombie(); // このスレッドはゾンビ状態か private: ConnectionInfoRec *m_pConInfo; // コンストラクタで渡されるパラメータを格納 // このスレッド実行中領域が確保されていること BOOL m_fIamZombie; // ゾンビ状態かどうかを保持 char *m_pSendData; // 送信データ格納用エリア int m_iSendDataSize; // 送信データ格納用エリアのデータサイズ char *m_pRecvData; // チャットパケットのための一時バッファ int m_iRecvDataSize; // 一時バッファのデータサイズ int GetSendData(char **ppcData); // 送信データの取得 // BOOL SetRecvData(char *pcData, int iSize); // ★削除 これは下の関数に変更された BOOL SetDataForDistributeChat(char *pcData, int iSize); // ★1.チャットパケットを一時バッファに格納する BOOL AnalyzeDataRecv(BYTE *pbData, int iSize); BOOL RecvMessagePacket(BYTE *pbData, int iSize); BOOL SendMessagePacket(char *pcData, int iSize); };

【SendRecvThread.cpp】 #include “SendRecvThread.h” #include “MySyncObject.h” // CMySyncObjectを使うため //============================================== // function // コンストラクタ // parameter // ConnectionInfoRec *pConInfo [in]機能に必要な情報 // return // なし //============================================== CSendRecvThread::CSendRecvThread(ConnectionInfoRec *pConInfo) { m_pConInfo = pConInfo; m_fIamZombie = FALSE; m_pSendData = NULL; // 送信したいデータを格納するエリア m_iSendDataSize = 0; // 送信したいデータのサイズ m_pRecvData = NULL; // チャットパケットのための一時バッファ m_iRecvDataSize = 0; // 一時バッファのデータサイズ } //============================================== // function // デストラクタ // parameter // なし // return // なし //============================================== CSendRecvThread::~CSendRecvThread() { SAFE_FREE(m_pSendData) // 送信したいデータを格納するエリアを開放します m_iSendDataSize = 0; SAFE_FREE(m_pRecvData) // チャットパケットのための一時バッファを開放します m_iRecvDataSize = 0; } #define RCVBUFSIZE (1024) // 一回に読む最大受信サイズ //============================================== // function // 機能を記述した関数 // parameter // なし // return // 0:正常 -1:エラー発生 //============================================== UINT CSendRecvThread::DoWork() この関数に変更はありません //============================================== // function // 送信データの設定 // parameter // char *pcData [in]送信データ // int iSize [in]データ長 // return // TRUE/FALSE //============================================== BOOL CSendRecvThread::SetSendData(char *pcData, int iSize) この関数に変更はありません //============================================== // function // 送信データの取得 // parameter // char **ppcData [in/out]送信データ // return // データ長 //============================================== int CSendRecvThread::GetSendData(char **ppcData) この関数に変更はありません //============================================== // function // ★1.チャットパケットを一時バッファに格納する // parameter // char *pcData [in]チャットメッセージパケット // int iSize [in]パケット長 // return // TRUE/FALSE //============================================== //BOOL CSendRecvThread::SetRecvData(char *pcData, int iSize) BOOL CSendRecvThread::SetDataForDistributeChat(char *pcData, int iSize) { BOOL fRet = FALSE; m_pConInfo->pCMySyncObject->Lock(); // 前回のデータを読みだしていないときはエラーにする if (m_iRecvDataSize == 0) { m_pRecvData = (char *)calloc(iSize, sizeof(char)); memcpy(m_pRecvData, pcData, iSize); m_iRecvDataSize = iSize; fRet = TRUE; } m_pConInfo->pCMySyncObject->UnLock(); return(fRet); } //============================================== // function // ★1.一時バッファに格納されたチャットパケットの取得 // parameter // char **ppcData [in/out]チャット分配用データ // return // データ長 //============================================== //int CSendRecvThread::GetRecvData(char **ppcData) int CSendRecvThread::GetDataForDistributeChat(char **ppcData) { int iSize = 0; m_pConInfo->pCMySyncObject->Lock(); if (m_iRecvDataSize > 0) { *ppcData = (char *)calloc(m_iRecvDataSize, sizeof(char)); memcpy(*ppcData, m_pRecvData, m_iRecvDataSize); iSize = m_iRecvDataSize; SAFE_FREE(m_pRecvData) // 読み出しを行ったことをセット m_iRecvDataSize = 0; } m_pConInfo->pCMySyncObject->UnLock(); return(iSize); } //============================================== // function // このスレッドはゾンビ状態か // 別スレッドから参照される // parameter // なし // return // 0:正常 -1:エラー発生 //============================================== BOOL CSendRecvThread::IsZombie() この関数に変更はありません //============================================== // function // 受信データの解析 // parameter // BYTE *pbData [in]受信データ // int iSize [in]受信データサイズ // retun // TRUE/FALSE //============================================== BOOL CSendRecvThread::AnalyzeDataRecv(BYTE *pbData, int iSize) この関数に変更はありません //============================================== // function // メッセージコマンドの受信 // parameter // char *pcData [in]メッセージコマンドパケット // int iSize [in]データサイズ // retun // なし //============================================== BOOL CSendRecvThread::RecvMessagePacket(BYTE *pbData, int iSize) { BOOL fRet = FALSE; HeaderRec *pHeader; MsgDataRec *pMsgData; int iMsgSize; char *pszMsg = NULL; pHeader = (HeaderRec *)pbData; iMsgSize = ntohs(pHeader->wDataLen); // データが足りないときはエラー if (iSize < iMsgSize + sizeof(HeaderRec)) goto L_END; pMsgData = (MsgDataRec *)pbData; // NULLターミネート分を追加して確保 pszMsg = (char *)calloc(iMsgSize + 1, sizeof(char)); memcpy(pszMsg, pMsgData->bMsgData, iMsgSize); m_pConInfo->pCMySyncObject->Lock(); fprintf(stderr, “Msg:recv %s\n”, pszMsg); m_pConInfo->pCMySyncObject->UnLock(); // ★1.チャット送信のためにメッセージデータをパケット化してセット SendMessagePacket(pszMsg, iMsgSize); SAFE_FREE(pszMsg) fRet = TRUE; L_END: return(fRet); } //============================================== // function // ★1.メッセージパケットの送信 // parameter // char *pcData [in]UTF-8データ(NULLターミネートなし) // int iSize [in]データサイズ // retun // なし //============================================== BOOL CSendRecvThread::SendMessagePacket(char *pcData, int iSize) { MsgDataRec *pMsgData = NULL; int iPacketSize = sizeof(HeaderRec) + iSize; // パケットサイズ BOOL fRet = FALSE; pMsgData = (MsgDataRec *)calloc(iPacketSize, sizeof(BYTE)); // パケット全体のエリアを確保 memcpy(pMsgData->header.bMagicData, MAGIC_STRING, strlen(MAGIC_STRING)); pMsgData->header.wCommand = htons(CMD_MSG_DATA); pMsgData->header.wDataLen = htons(iSize); memcpy(pMsgData->bMsgData, pcData, iSize); // ★1.チャット送信のためにメッセージパケットデータをセット // fRet = SetRecvData((char *)pMsgData, iPacketSize); fRet = SetDataForDistributeChat((char *)pMsgData, iPacketSize); SAFE_FREE(pMsgData) return(fRet); }

【SimpleServer.cpp】 #include “stdThread.h” #include “SendRecvThread.h” // 送受信スレッドを使うため #include “MySyncObject.h” // 同期オブジェクトを使うため #ifdef _MSC_VER // Windowsのとき #define DISABLE_C4996 __pragma(warning(push)) __pragma(warning(disable:4996)) #define ENABLE_C4996 __pragma(warning(pop)) #else // Linuxのとき #define DISABLE_C4996 #define ENABLE_C4996 #endif #define MAXPENDING (5) // 接続バックログ数(保留中の接続のキューの最大長) #define RCVBUFSIZE (1024) // 一回に読む最大受信サイズ #define MAX_SOCKET_NUM (20) // 接続待用ソケットの最大数 #define MAX_CONNECTION_NUM (10) // 最大受け入れ接続数 // 関数の宣言 BOOL CreateAndBindSocket(WORD wPort); // 接続待ち用ソケットの作成と名前付け BOOL DestroySocket(SOCKET &fd); // 切断とソケットの破棄 BOOL DoListen(); // ソケットを接続待ちにする BOOL DoAccept(); // 接続の受容(接続済みソケットを作成する) void Stop(); // すべてのソケットを破棄する BOOL CheckKey(); // キー入力の検査 BOOL KillZombei(); // ゾンビ状態のスレッドを破棄する //BOOL GetRecvDataAndSetSendData(); // ★2.削除 次の関数に変更 BOOL SerializeSendData(); // ★2.データをシリアライズし各送受信スレッドの送信バッファに格納する // 変数の宣言 SOCKET m_fdServer[MAX_SOCKET_NUM]; // 待機用ソケット int m_iSockCount; // 待機中ソケットの数 CSendRecvThread *m_pCSendRecvThread[MAX_CONNECTION_NUM] = { NULL }; ConnectionInfoRec *m_pConInfo[MAX_CONNECTION_NUM] = { NULL }; CMySyncObject *m_pCMySyncObject = NULL; int main(int argc, char *argv[]) この関数に変更はありません //============================================== // function // 待機用ソケットの作成とbindの実施 // parameter // WORD wPort [in]ポート番号 // return // TRUE/FALSE //============================================== BOOL CreateAndBindSocket(WORD wPort) この関数に変更はありません //============================================== // function // 待機用ソケットを接続待ち状態にする // parameter // なし // return // TRUE/FALSE //============================================== BOOL DoListen() この関数に変更はありません //============================================== // function // 接続要求の受け入れ(接続済みソケットの作成) // 送受信スレッドの管理 // parameter // なし // return // TRUE/FALSE //============================================== BOOL DoAccept() { BOOL fRet = FALSE; sockaddr_storage ClntAddr; // 接続クライアントのアドレス情報 socklen_t iClntLen; // 接続クライアントのアドレス情報のサイズ char szHostAddr[NI_MAXHOST]; int ii; pollfd fds[MAX_SOCKET_NUM] = {0}; int jj; // 最大接続数まで送受信スレッドを動かす SOCKET fdClient; // そのためにacceptの結果を記憶する fprintf(stderr, “DoAccept()\n”); for (ii = 0; ii < m_iSockCount; ++ii) { fds[ii].fd = m_fdServer[ii]; fds[ii].events = POLLIN; // 受信イベントを設定 } while (1) { KillZombei(); // ゾンビ状態のスレッドは破棄する // GetRecvDataAndSetSendData(); SerializeSendData(); // ★2.送信データのシリアライズ poll(fds, m_iSockCount, 10); // キー入力で中断 if (CheckKey()) { fprintf(stderr, ” OK:Abort by key\n”); break; } for (ii = 0; ii < m_iSockCount; ++ii) { if (fds[ii].revents & POLLERR) // エラー発生 DispErrorMsg(“Err:DoAccept”); else if(fds[ii].revents & POLLIN) // 受信イベント発生 { iClntLen = sizeof(ClntAddr); // 接続先アドレス情報を格納する構造体のサイズ // 受容したときのソケットを記憶し、送受信スレッドを作るか切断するかに使用 if ((fdClient = accept(m_fdServer[ii], (sockaddr *)&ClntAddr, &iClntLen)) == INVALID_SOCKET) { // 失敗時次の接続要求を処理する DispErrorMsg(“Err:accept”); continue; } else { // 成功時fdClientには接続済みのソケットが格納されている // 相手の情報から、IPアドレスを調べる if (getnameinfo((struct sockaddr *) &ClntAddr, (socklen_t)iClntLen, szHostAddr, sizeof(szHostAddr), NULL, 0, NI_NUMERICHOST) == 0) { fprintf(stderr, “%s\n”, szHostAddr); } // 接続数に余裕があればCSendRecvThreadのインスタンスを作成し実行 // なければ、切断する fRet = FALSE; for (jj = 0; jj < MAX_CONNECTION_NUM; ++jj) { if (m_pCSendRecvThread[jj] == NULL) { m_pConInfo[jj] = (ConnectionInfoRec *)calloc(1, sizeof(ConnectionInfoRec)); m_pConInfo[jj]->pCMySyncObject = m_pCMySyncObject; m_pConInfo[jj]->fdClient = fdClient; m_pCSendRecvThread[jj] = new CSendRecvThread(m_pConInfo[jj]); m_pCSendRecvThread[jj]->Begin(); fRet = TRUE; break; } } if (fRet == FALSE) DestroySocket(fdClient); } } } } return(fRet); } //============================================== // function // TCPソケットの破棄 // parameter // SOCKET &fd [in/out]破棄するソケット // return // TRUE/FALSE //============================================== BOOL DestroySocket(SOCKET &fd) この関数に変更はありません //============================================== // function // 切断、ソケットの破棄、受信スレッドの破棄 // parameter // なし // return // なし //============================================== void Stop() この関数に変更はありません //============================================== // function // キー入力チェック // 標準入力は0なのでselectのままでOKですが、練習のためにpollに // parameter // なし // return // TRUE:入力あり //============================================== BOOL CheckKey() この関数に変更はありません //============================================== // function // 切断済みのCSendRecvThreadを破棄する // parameter // なし // return // TRUE:破棄した/FALSE:破棄すべきものがなかった //============================================== BOOL KillZombei() この関数に変更はありません //============================================== // function // ★2.ゾンビ状態でないCSendRecvThreadに送信すべきデータがあれば // シリアライズして送信バッファに格納する // ここではメッセージパケットだけなのでパケットサイズなど // 考えずにセットしています。 // parameter // なし // return // TRUE:セットすべきデータがあった/FALSE:なかった //============================================== //BOOL GetRecvDataAndSetSendData() BOOL SerializeSendData() { BOOL fRet = FALSE; int ii, iRecvSize = 0, iRecvIndex = -1; char *pcRecvData = NULL; // チャットデータの分配 // ゾンビ状態でない受信したClient以外のすべてに送信データとしてセットする for (ii = 0; ii < MAX_CONNECTION_NUM; ++ii) { if (m_pCSendRecvThread[ii] != NULL) { if (m_pCSendRecvThread[ii]->IsZombie() == FALSE) { // ★2.チャットデータ用の一時バッファからデータを読み込む // iRecvSize = m_pCSendRecvThread[ii]->GetRecvData(&pcRecvData); iRecvSize = m_pCSendRecvThread[ii]->GetDataForDistributeChat(&pcRecvData); if (iRecvSize > 0) { fRet = TRUE; iRecvIndex = ii; break; } } } } if (fRet == TRUE) { for (ii = 0; ii < MAX_CONNECTION_NUM; ++ii) { if ((m_pCSendRecvThread[ii] != NULL) && (ii != iRecvIndex)) { if (m_pCSendRecvThread[ii]->IsZombie() == FALSE) { m_pCSendRecvThread[ii]->SetSendData(pcRecvData, iRecvSize); } } } } SAFE_FREE(pcRecvData) return(fRet); }

これでサーバ側は完成です。
次にSimpleClientTransFile(Windows)にチャットメッセージパケット受信機能を
追加してみましょう。
これは、サーバの受信部分とほとんど同じなので簡単ですね。
受信データの解析関数AnalyzeDataRecvを作ってそこでチャットパケットを取り出し
RecvMessagePacketに渡して表示をする。
違いは表示する際に文字コードをS-JISに変換するところだけです。
(変換については元のソースにあるのでそれをRecvMessagePacketに移動するだけですね)

【SendRecvThread.h】 #pragma once #include “ThreadJob.h” #include “define.h” // ★パケットを取り扱うので class CMySyncObject; // このクラスの使用することを宣言 typedef struct { SOCKET fdClient; // 接続済みソケット(connectの結果) CMySyncObject *pCMySyncObject; // 同期オブジェクト } ConnectionInfoRec; class CSendRecvThread : public CThreadJob { public: CSendRecvThread(ConnectionInfoRec *pConInfo); // パラメータをコンストラクタで渡す ~CSendRecvThread(); // 基底クラスの関数をオーバーライドする // C++11で明示的にoverrideを書くことが出来るようになりました // 基底クラスの当該関数にvirtualが書いていないとエラーを出してくれます UINT DoWork() override; // DoSendで実施している内容を記述 BOOL SetSendData(char *pcData, int iSize); // 送信データの設定 BOOL IsZombie(); // このスレッドはゾンビ状態か private: ConnectionInfoRec *m_pConInfo; // コンストラクタで渡されるパラメータを格納 // このスレッド実行中領域が確保されていること BOOL m_fIamZombie; // ゾンビ状態かどうかを保持 char *m_pSendData; // 送信データ格納用エリア int m_iSendDataSize; // 送信データ格納用エリアのデータサイズ int GetSendData(char **ppcData); // 送信データの取得 BOOL AnalyzeDataRecv(BYTE *pbData, int iSize); // ★受信データの解析 BOOL RecvMessagePacket(BYTE *pbData, int iSize);// ★チャットメッセージパケットの処理 };

【SendRecvThread.cpp】 #include “SendRecvThread.h” #include “MySyncObject.h” // CMySyncObjectを使うため //============================================== // function // コンストラクタ // parameter // ConnectionInfoRec *pConInfo [in]機能に必要な情報 // return // なし //============================================== CSendRecvThread::CSendRecvThread(ConnectionInfoRec *pConInfo) この関数に変更はありません //============================================== // function // デストラクタ // parameter // なし // return // なし //============================================== CSendRecvThread::~CSendRecvThread() この関数に変更はありません #define RCVBUFSIZE (1024) // 一回に読む最大受信サイズ //============================================== // function // 機能を記述した関数(DoSend, DoRecvの内容を記述する) // parameter // なし // return // 0:正常 -1:エラー発生 //============================================== UINT CSendRecvThread::DoWork() { BOOL fRet = TRUE; fd_set wfds, rfds; struct timeval tv; char *pcData = NULL; // 未送信データ int iSendSize = 0; // 未送信データサイズ char szRecvBuffer[RCVBUFSIZE + 1]; // 受信バッファ int iRecvSize; while (!m_fStopFlag) { // 未送信のデータがなければ送信したいデータがあるか調べる if (iSendSize == 0) iSendSize = GetSendData(&pcData); tv.tv_sec = 0; tv.tv_usec = 10 * 1000; // 10msec FD_ZERO(&rfds); FD_ZERO(&wfds); // 受信データ検査用fd_setは常にセットする FD_SET(m_pConInfo->fdClient, &rfds); // 未送信データがあるときだけ送信可能検査用fd_setにセットする if (iSendSize > 0) FD_SET(m_pConInfo->fdClient, &wfds); select(FD_SETSIZE, &rfds, &wfds, NULL, &tv); // タイムアウトまでSleepと同等 // 受信処理(DoRecv) if (FD_ISSET(m_pConInfo->fdClient, &rfds)) // 受信データがあればrecv実施 { memset(szRecvBuffer, 0, sizeof(szRecvBuffer)); if ((iRecvSize = recv(m_pConInfo->fdClient, szRecvBuffer, RCVBUFSIZE, 0)) <= 0) { m_pConInfo->pCMySyncObject->Lock(); if (iRecvSize == 0) DispErrorMsg(“Disconnected”); else DispErrorMsg(“Err:recv”); m_pConInfo->pCMySyncObject->UnLock(); fRet = FALSE; break; } else { // ★受信データの解析 if (AnalyzeDataRecv((BYTE *)szRecvBuffer, iRecvSize) == FALSE) { m_pConInfo->pCMySyncObject->Lock(); DispErrorMsg(“Err:Packet format”); m_pConInfo->pCMySyncObject->UnLock(); fRet = FALSE; break; } } } // 送信処理(DoSend) if (FD_ISSET(m_pConInfo->fdClient, &wfds)) // 送信可能ならsend実施 { if (send(m_pConInfo->fdClient, pcData, iSendSize, 0) != iSendSize) { DispErrorMsg(“Err:send”); fRet = FALSE; break; } SAFE_FREE(pcData) // 未送信データなしにセット iSendSize = 0; } } m_pConInfo->pCMySyncObject->Lock(); m_fIamZombie = TRUE; m_pConInfo->pCMySyncObject->UnLock(); SAFE_FREE(pcData) iSendSize = 0; return((fRet == TRUE) ? 0 : -1); } //============================================== // function // 送信データの設定 // parameter // char *pcData [in]送信データ // int iSize [in]データ長 // return // TRUE/FALSE //============================================== BOOL CSendRecvThread::SetSendData(char *pcData, int iSize) この関数に変更はありません //============================================== // function // 送信データの取得 // parameter // char **ppcData [in/out]送信データ // return // データ長 //============================================== int CSendRecvThread::GetSendData(char **ppcData) この関数に変更はありません //============================================== // function // このスレッドはゾンビ状態か // 別スレッドから参照される // parameter // なし // return // 0:正常 -1:エラー発生 //============================================== BOOL CSendRecvThread::IsZombie() この関数に変更はありません //============================================== // function // ★受信データの解析 // parameter // BYTE *pbData [in]受信データ // int iSize [in]受信データサイズ // retun // TRUE/FALSE //============================================== BOOL CSendRecvThread::AnalyzeDataRecv(BYTE *pbData, int iSize) { BOOL fRet = FALSE; HeaderRec *pHeader; WORD wCmd; if (iSize < sizeof(HeaderRec)) // ヘッダサイズに満たないときはエラー goto L_END; pHeader = (HeaderRec *)pbData; // ヘッダ部の解析 if (memcmp(pHeader->bMagicData, MAGIC_STRING, strlen(MAGIC_STRING)) != 0) goto L_END; wCmd = ntohs(pHeader->wCommand); fprintf(stderr, “CMD:%d\n”, wCmd); switch (wCmd) { case CMD_MSG_DATA: fRet = RecvMessagePacket(pbData, iSize); break; default: // 知らないコマンド break; } L_END: return(fRet); } //============================================== // function // ★メッセージコマンドの受信 // parameter // char *pcData [in]メッセージコマンドパケット // int iSize [in]データサイズ // retun // なし //============================================== BOOL CSendRecvThread::RecvMessagePacket(BYTE *pbData, int iSize) { BOOL fRet = FALSE; HeaderRec *pHeader; MsgDataRec *pMsgData; int iMsgSize; char *pszMsg = NULL; LPBYTE pbDest = NULL; pHeader = (HeaderRec *)pbData; iMsgSize = ntohs(pHeader->wDataLen); // データが足りないときはエラー if (iSize < iMsgSize + sizeof(HeaderRec)) goto L_END; pMsgData = (MsgDataRec *)pbData; // NULLターミネート分を追加して確保 pszMsg = (char *)calloc(iMsgSize + 1, sizeof(char)); memcpy(pszMsg, pMsgData->bMsgData, iMsgSize); // UTF-8で受信したので、SJISに変換して表示する ConvUtf8toSJis((LPBYTE)pszMsg, NULL, &iSize); pbDest = (LPBYTE)calloc(iSize, sizeof(BYTE)); ConvUtf8toSJis((LPBYTE)pszMsg, pbDest, &iSize); m_pConInfo->pCMySyncObject->Lock(); fprintf(stderr, “Msg:recv %s\n”, pbDest); m_pConInfo->pCMySyncObject->UnLock(); SAFE_FREE(pbDest) SAFE_FREE(pszMsg) fRet = TRUE; L_END: return(fRet); }

これでクライアント側も完成です。
ビルドして動かしてみましょう。
クライアントの受信でCMDとメッセージが表示されていることがわかります。
<SimpleClientTransFile.exe shimodaPi1 10000
Index:0
HopLimitOrg 128
HopLimitNew 10
q:quit m:メッセージ送信 : CMD:100
Msg:recv aaaaa

SimpleServerTransFile(Windows)、SimpleClientTransFile(Linux)も同様に
できます。
今回のプロジェクトは、次のリンクからダウンロードできます。

SimpleServerTransFile(Windows, Linux)
SimpleClientTransFile(Windows, Linux)