元の出典:「Windows Networkプログラミングテクノロジー」第8章完了したポートモデル
元の本にはCコードが伴うため、Delphiコードに翻訳しました。
その中で、winsock2.pasはhttp://jungla.dit.upm.es/~bti/files/winsock2.pasをダウンロードする必要はありません
プログラムの完了;
{$ apptypeコンソール}
用途
sysutils、
winsock2 in 'winsock2.pas'、
'mains.pas'のメイン;
始める
主要();
終わり。
//モジュール名:iocmmplt.cpp
//
// 説明:
//
//このサンプルは、シンプルなエコーサーバーWinsockを開発する方法を示しています
//完了ポートI/Oモデルを使用します
//サンプルはコンソールスタイルのアプリケーションとして実装され、単純に印刷
//メッセージが確立され、サーバーから削除されたとき。
//アプリケーションはポート5150のTCP接続をリッスンし、それらを受け入れます
//このアプリケーションがクライアントからデータを受信したとき
//単にechos(これが私たちがそれをエコーサーバーと呼ぶ理由です)
//クライアントが接続を閉じるまで、それは元のフォームです。
//
// 2005-2-5
// cppはジョンソンによってデルフィパスに変換されます
//
ユニットメイン;
インタフェース
Windows、winsock2、winsock、sysutilsを使用します。
const
port = 5150;
data_bufsize = 8192;
タイプ
lpvoid = pointer;
lpper_io_operation_data = ^ per_io_operation_data;
per_io_operation_data = packedレコード
オーバーラップ:オーバーラップ。
DataBuf:twsabuf;
バッファ:charの配列[0..data_bufsize];
bytessend:dword;
bytesrecv:dword;
終わり;
lpper_handle_data = ^ per_handle_data;
per_handle_data = packed Record
ソケット:tsocket;
終わり;
手順メイン。
実装
function serverworkerthread(completeportid:lpvoid):dword;
手順printf(fmt:string; num:integer);
始める
writeln(format(fmt、[num]));
終わり;
手順メイン。
var
InternetAddr:sockaddr_in;
聞く:tsocket;
受け入れる:tsocket;
compleateport:thandle;
SystemInfo:system_info;
Perhandledata:lpper_handle_data;
Periodata:lpper_io_operation_data;
I:整数;
recvbytes:dword;
フラグ:dword;
threadid:dword;
wsadata:twsadata;
Ret:dword;
ThreadHandle:Thandle;
始める
ret:= wsastartup($ 0202、wsadata);
if(ret <> 0)then
始める
printf( 'wsastartupはエラー%d'、retで失敗しました);
出口;
終わり;
// I/O完了ポートを設定します。
compleateport:= createioCompletionport(invalid_handle_value、0、0、0);
if(completeport = 0)then
始める
printf( 'createioCompletionportがエラーで失敗しました:%d'、getLasterRor());
出口;
終わり;
//システム上にあるプロセッサの数を決定します。
getsystemInfo(systemInfo);
//で利用可能なプロセッサの数に基づいてワーカースレッドを作成します
//システムごとに2つのワーカースレッドを作成します。
i:= 0 to SystemInfo.dwNumberOfProcessors * 2-1 do
始める
//サーバーワーカースレッドを作成し、完了ポートをスレッドに渡します。
ThreadHandle:= createThread(nil、0、@serverworkerthread、pointer(compleateport)、
0、threadid);
if(threadhandle = 0)then
始める
printf( 'createThread()はエラー%d'で失敗しました、getLasterRor());
出口;
終わり;
//スレッドハンドルを閉じます
CloseHandle(threadhandle);
終わり;
//リスニングソケットを作成します
聞く:= wsasocket(af_inet、sock_stream、0、nil、0、wsa_flag_overlapp);
if(聞き= invalid_socket)then
始める
printf( 'wsasocket()はエラー%d'、wsagetlasterror()で失敗しました。
出口;
終わり;
InternetAddr.sin_family:= af_inet;
InternetAddr.sin_addr.s_addr:= htonl(inaddr_any);
InternetAddr.sin_port:= htons(port);
if(bind(listen、internetaddr、sizeof(internetaddr))= socket_error)then
始める
printf( 'bind()はエラー%d'、wsagetlasterror()で失敗しました。
出口;
終わり;
//リスニング用のソケットを準備します
if(winsock.listen(listen、5)= socket_error)then
始める
printf( '聞き()エラー%d'、wsagetlasterror())で失敗しました。
出口;
終わり
それ以外
始める
printf( 'サーバーはport =%d ...'、port)で聞きます);
終わり;
//接続を受け入れ、完了ポートに割り当てます。
while(true)します
始める
受け入れる:= wsaaccept(聞く、nil、nil、nil、0);
if(accept = socket_error)then
始める
printf( 'wsaaccept()はエラー%d'、wsagetlasterror()で失敗しました。
出口;
終わり;
//ソケットに接続するソケット情報構造を作成する
Perhanddedata:= lpper_handle_data(GlobalAlloc(gptr、sizeof(per_handle_data)));
if(perhandledata = nil)then
始める
printf( 'globalAlloc()はエラー%d'、wsagetlasterror()で失敗しました。
出口;
終わり;
//受け入れられたソケットを元の完了ポートに関連付けます。
printf( 'ソケット番号%d接続'、Accept);
perhanddedata.socket:= accept;
if(createioCompletionPort(compeid、complection、compleateport、dword(perhanddedata)、0)= 0)then
始める
printf( 'createioCompletionport()はエラー%d'、wsagetlasterror()で失敗しました。
出口;
終わり;
// I/Oソケット情報構造ごとに作成して、
//以下のWSARECV呼び出し。
Periodata:= lpper_io_operation_data(GlobalAlloc(gptr、sizeof(per_io_operation_data)));
if(inperionata = nil)then
始める
printf( 'globalAlloc()はエラー%d'、wsagetlasterror()で失敗しました。
出口;
終わり;
Zeromemory( @sipuritata.Overlapped、sizeof(オーバーラップ));
Periodata.bytessend:= 0;
Periodata.bytesrecv:= 0;
Periodata.databuf.len:= data_bufsize;
Periodata.databuf.buf:= @periodata.buffer;
フラグ:= 0;
if(wsarecv(Accept、 @(periodata.databuf)、1、@recvbytes、 @flags、
@(Periodata.Overlapped)、nil)= socket_error)then
始める
if(wsagetlasterror()<> error_io_pending)
始める
printf( 'wsarecv()はエラー%d'、wsagetlasterror()で失敗しました。
出口;
終わり
終わり;
終わり;
終わり;
function serverworkerthread(compleateportid:lpvoid):dword;
var
compleateport:thandle;
bytestransferred:dword;
//オーバーラップ:貧困;
Perhandledata:lpper_handle_data;
Periodata:lpper_io_operation_data;
sendbytes、recvbytes:dword;
フラグ:dword;
始める
compleateport:= thandle(compleateportid);
結果:= 0;
while(true)します
始める
if(getqueuedCompleTionStatus(completeport、byTestransferred、
dword(handdedata)、poverlapped(inperionata)、infinite)= false)その後
始める
printf( 'getqueuedcompletionstatusはエラー%d'、getlasterror()で失敗しました。
出口;
終わり;
//最初にチェックして、ソケットでエラーが発生したかどうかを確認します。
//ソケットを閉じてsocket_information構造をクリーンアップします
//ソケットに関連付けられています。
if(bytestransferred = 0)then
始める
printf( '閉じたソケット%d/'、perhandledata.socket);
if(clossocket(perhandledata.socket)= socket_error)then
始める
printf( 'clossocket()はエラー%d'、wsagetlasterror()で失敗しました。
出口;
終わり;
GlobalFree(dword(perhandledata));
GlobalFree(DWORD(Periodata));
続く;
終わり;
// BYTESRECVフィールドがゼロに等しいかどうかを確認してください
//これは、wsarecvコールが完了したばかりなので、bytesrecvフィールドを更新することを意味します
//完了したwsarecv()callからのbyTestransferred値を使用します。
if(periodata.bytesrecv = 0)then
始める
cipthata.bytesrecv:= byTestransferred;
Periodata.bytessend:= 0;
終わり
それ以外
始める
Periodata.bytessend:= riedata.bytessend + byTestransferred;
終わり;
if(periveata.bytesrecv> feetionata.bytessend)
始める
//別のwsasend()リクエストを投稿します。
// wsasend()は、要求されたすべてのバイトを送信するためにガウルントされていないため、
//受信したすべてのバイトが送信されるまで、wsasend()コールの投稿を続けます。
Zeromemory(@(ingiverata.Overlapped)、sizeof(overlapped));
Periodata.databuf.buf:= riversata.buffer + periodata.bytessend;
Periodata.databuf.len:= periodata.bytesrecv -cipthata.bytessend;
if(wsasend(perhanddedata.socket、 @(periodata.databuf)、1、@sendbytes、0、
@(Periodata.Overlapped)、nil)= socket_error)then
始める
if(wsagetlasterror()<> error_io_pending)
始める
printf( 'wsasend()はエラー%d'、wsagetlasterror()で失敗しました。
出口;
終わり;
終わり;
終わり
それ以外
始める
Periodata.bytesrecv:= 0;
//別のWSARECV()リクエストを投稿するバイトがもうありません。
フラグ:= 0;
Zeromemory(@(ingiverata.Overlapped)、sizeof(overlapped));
Periodata.databuf.len:= data_bufsize;
Periodata.databuf.buf:= @periodata.buffer;
if(wsarecv(perhanddedata.socket、 @(periodata.databuf)、1、@recvbytes、 @flags、
@(Periodata.Overlapped)、nil)= socket_error)then
始める
if(wsagetlasterror()<> error_io_pending)
始める
printf( 'wsarecv()はエラー%d'、wsagetlasterror()で失敗しました。
出口;
終わり;
終わり;
終わり;
終わり;
終わり;
終わり。