■ 共通
■ socket
SYNTAX
SOCKET socket (
int af, // ソケットタイプ( アドレスの種類を表すアドレスファミリ )
int type , // ソケットの種類( SOCK_STREAM )
int protocol // (TCP | UDP)
);
RET
0: OK
SOCKET_ERROR: FAIL
DESC
指定したタイプの Socket を作成して SOCKET HANDLE( 整数値 ) を返す。
// TCP ソケットを作成
SOCKET sock = socket( AF_INET, SOCK_STREAM, 0 );
// UDP ソケットを作成
SOCKET sock = socket( AF_INET, SOCK_DGRAM, 0 );
■ bind
SYNTAX
int bind (
SOCKET s ,
const struct sockaddr FAR* name ,
int namelen
);
RET
0: 成功
SOCKET_ERROR: 失敗
DESC
ソケットにローカルIPアドレスとローカルポートを設定する。
Server 側は指定した port で listen するため bind して明示する
Client 側は自動設定にまかせる。
ERROR
EADDRINUSE : ポートが他のソケットで利用されている。
{
SOCKADDR_IN adr = { AF_INET };
adr.sin_port = htons(80);
adr.sin_addr = INADDR_ANY; // ローカルアドレスにワイルドカードを指定
// サーバー側のすべてのIPアドレスでデータを受け取ることができる
SOCKET sock = socket(AF_INET , SOCK_STREAM , 0);
bind( sock , (SOCKADDR *)&adr , sizeof(SOCKADDR) );
}
WARNING
// 同じローカルポートに他のソケットがバインドしている場合はエラーになる
SOCKET sock1 = socket(AF_INET , SOCK_STREAM , 0);
bind( sock1 , (SOCKADDR *)&adr , sizeof(SOCKADDR) );
SOCKET sock2 = socket(AF_INET , SOCK_STREAM , 0);
bind( sock2 , (SOCKADDR *)&adr , sizeof(SOCKADDR) );
■ SOCKADDR_IN
インターネットアドレス形式を表す構造体
union で定義されているが, ほとんどは S_addr に IP Address を表す 32bit の値をいれる
typedef struct sockaddr_in {
short sin_family;
u_short sin_port; // ポート
struct in_addr sin_addr; //
char sin_zero[8]; //
} SOCKADDR_IN;
POINT
sin_port にはサーバがサービスを提供するポート番号を指定する
sin_addr には INADDR_ANY を指定することで固定アドレスでない場合に自動で割り振られる
■ send
SYNTAX
int send(
SOCKET sock,
char *msg, // メッセージのポインタ
int length, // メッセージの長さ ( byte 数 )
int flags // 通常 0 ( | MSG_DONTROUTE,MSG_OOB )
);
RET
N : 実際に送信されたバイト数
SOCKET_ERROR : エラー
DESC
指定したソケット経由でデータを送信する(主に TCP で使用)
flags で 0 を指定するとデフォルトの振る舞いをする。
正確にはソケットの送信バッファへデータがコピーされてすぐに制御を返す。
STATE
ソケットは接続状態( ESTABLISHED )である必要がある。
// HTTP Client が HTTP Server に HTTP Message ( GET / )
{
SOCKADDR_IN adr = { AF_INET };
name.sin_port = htons(80);
SOCKET sock = socket(AF_INET , SOCK_STREAM , 0);
bind( sock , (SOCKADDR *)&adr , sizeof(SOCKADDR) );
const char *msg = "test";
// WARNING
// メッセージは長さを送り, 末尾の '\0' は不要
send( sock, msg, strlen(msg), 0 ) ;
}
■ recv
SYNTAX
int recv(
SOCKET sock,
char* buf,
int length,
int flags // 通常 0 ( MSG_PEEK MSG_OOB )
);
RET
N : 読み込まれたバイト数
SOCKET_ERROR : 失敗
0 : 通信相手がコネクションを閉じた
DESC
指定したソケットで相手からデータを受信をする。( 主にTCPで使用 )
正確にはソケットの受信バッファにあるデータを buf で指定した領域へコピーする。
受信バッファにデータがなければブロックされるため
メインスレッドから実行する場合は何らかの対策が必要。
STATE
ESTABLISHED
接続状態でない時に利用すると SOCKET_ERROR が返る。
WARNING
HTTP Server が相手だとリクエストを投げない( send() をしない )と何もかえってこない
このとき recv() で Block される。
ブロックを回避するには, recv() 用のスレッドを作成したり、非同期ソケットを利用する。
// シンプルに受信する
char buf[1024];
int len = recv(sock, buf, sizeof(buf), 0);
buf[len] = '\0';
printf( "recv size %d\n%s", len, buf );
if ( len == SOCKET_ERROR ) return 1;
WARNING
十分なバッファを WinSock に渡しても
ソケット上のキューにあるデータを全て返してくれない
全てとるには呼出しを繰り返す
char buf[1024 * 4];
memset(buf, 0, sizeof(buf));
int sum = 0;
while ( 1 ) {
int len = recv(sock, buf + sum, sizeof(buf) - sum, 0);
sum += len;
// 0 がきたら相手が CLOSE したことを意味する。
if ( len == 0 ) {
printf( "server closed\n" );
break;
}
// 相手が close した状態で recv() をしたり, サーバが落ちていると -1 が返る。
if ( len == SOCKET_ERROR ) {
break;
}
}
FD_CLOSE がうけれるなら, len == 0 の処理は不要
逆に FD_CLOSE Event がくるときに , recv をすると, len = 0 がかえってくる
0 == recv() は FD_CLOSE をチェックしているに等しい
// Thread にわたす 受信処理
void* waitReceiveThread(void* pParam)
{
char buf[1024];
int len;
while(1)
{
// データ受信待ち
len = recv(sock,buf,1024,0);
printf("Received %d bytes\n",len);
// 相手が閉じたら
if(len == 0)
{
printf("check\n");
// こっちも切る
close(sock);
break;
}
}
return NULL;
}
connect が失敗した状態のソケットで recv( 受信 ) をすると WSAENOTCONN (10057) が返る。
接続されていないソケットを使用したということ。
同じく send() でも発生する。
// no check
connect( sock, (struct sockaddr *)&adr, sizeof(adr) );
int n = recv(m_sock, buf, sz, 0);
if (n < 0) {
printf("ERR: recv : %d\n", WSAGetLastError() );
}
■ closesocket
SYNTAX
int closesocket (
SOCKET sock
);
RET
OK : 0
FAIL: SOCKET_ERROR
DESC
コネクションを切る。
ソケットは通信できない状態へ移行する
使うと Client 側には HOST との接続が切られました という message が届く
■ server.固有
■ listen
SYNTAX
int listen (
SOCKET s , // 受信に使う Socket
int queue // 接続に使うキューの数 ( 最大接続数 )
);
RET
0: 成功
SOCKET_ERROR: 失敗
DESC
サーバのソケットが接続待ち状態( LISTENING )に移行して、
クライアントからの接続要求がキューへ格納できる状態へする。
queue で指定した数だけ最大でリクエストを保存できて、
アクセプト( accept )をするとキューから取り出す。
queue 以上のクライアントからの接続要求は失敗する。
POINT
サーバが listen() しないとクライアント側のコネクト要求( connect() )は失敗する。
{
SOCKADDR_IN adr = { AF_INET };
adr.sin_port = htons(2048);
adr.sin_addr = INADDR_ANY;
SOCKET sock = socket(AF_INET , SOCK_STREAM , 0);
bind( sock , (SOCKADDR *)&adr , sizeof(SOCKADDR) );
// キューの数を5にしてリスン状態にする。
listen( sock, 5 );
}
■ accept
SYNTAX
SOCKET accept (
SOCKET s,
struct sockaddr FAR* addr, // クライアント側のアドレス情報
int FAR* addrlen
);
RET
ClientSocket : 成功
INVALID_SOCKET : 失敗
DESC
クライアントからの接続を受け付ける。
正確には
リスン中のソケットの接続すみリストからひとつを取り出してディスクリプタを割り当てて返す。
リストが空ならばブロックされため、
GUI の MainThread では利用しないこと。
{
SOCKET sockCli = accept( sock, );
// お客さんへメッセージをなげる。
const char buf[] = "いらっしゃい";
send( sockCli, buf, length(buf), 0 );
}
■ client.固有
■ connect
SYNTAX
int connect(
SOCKET,
struct sockaddr *adr,
int size
);
RET
SOCKET_ERROR : 接続に失敗
0 : 成功
DESC
指定したサーバに接続する。
不正なアドレスを指定すると失敗する。
ERROR
ETIMEOUT : 指定した時間にハンドシェイクが完了しなかった。
WARNING
Server でリスン( listen() )してない場合はすべての接続要求は失敗する。
逆に listen() していれば accept() をしなくても接続要求は成功する。
ただしリストが埋まっていれば失敗する。
struct sockaddr_in adr = { AF_INET };
adr.sin_port = htons( 80 );
adr.sin_addr.S_un.S_addr = inet_addr( "127.0.0.1" );
int ret = connect( sock, (struct sockaddr *)&adr, sizeof(adr) );
if ( ret == SOCKET_ERROR ) {
printf( "fail connect\n" );
}
host が存在して ping などで接続できるが, 一定時間がたっても応答がない場合は
20秒ほど経過してから 10060 ( WSAETIMEDOUT ) が返る。
ケースとしてクライアントが指定したポートでリスンしてないなどがある
adr.sin_port = htons( 8888 );
int ret = connect( sock, (struct sockaddr *)&adr, sizeof(adr) );
if ( ret != 0 ) {
printf("FAIL connect : WSAGetLastError %d\n", WSAGetLastError());
}
■ 変換
■ htons
SYNTAX
u_short htons(
u_short
);
DESC
short int をネットワークバイトオーダーに変換
SYNTAX
u_short htons (
u_short hostshort // 変換する 16 ビットホストバイトオーダ
);
);
RET
指定した値のネットワークバイトオーダー
DESC
16 ビットホストバイトオーダーをネットワークバイトオーダーに変換する
{
struct sockaddr_in srcAddr;
// short 型の Data のときに使う
srcAddr.sin_port = htons( port );
}
■ htonl
SYNTAX
u_short htonl (
u_long hostshort // 変換する 32 bit HostByteOrder
);
RET
指定した値の NetworkByteOrder
DESC
htons の long (32bit) Version
{
struct sockaddr_in srcAddr;
srcAddr.sin_addr.s_addr = htonl( INADDR_ANY );
}
■ ntohs
SYNTAX
u_short ntohs(
u_short netshort
);
DESC
short の整数を ホストバイトオーダー に変換する
Client の port 情報を変換する際に便利
■ getHostByName
SYNTAX
struct hostent * getHostByName( char *name );
DESC
ホスト名( 又は dot表記のIPアドレス )から 32bit IPAddress をえる
逆に IP からホスト名とりたい場合は getHostByAddr() を使う
DNS にサーバー名とアドレスを登録しておく必要がある。
RET
!NULL : 変換されたホスト情報へのポインタ
NULL : 不正なホスト名( dot表記IPアドレス )を指定した。
POINT
IPアドレスでもあて先を指定できるが、ISPの変更があった場合に影響を受けないように
ホスト名を指定しておくと良い。
LPHOSTENT host = getHostByName( "google.co.jp" );
POINT
// ホスト情報を格納した構造体 hostent を返す。
typedef struct hostent {
char *h_name; // ホスト名
char **h_aliases; // ホストの別名
char **h_addr_list; // 32bit IPアドレスのポインタ。プログラムはこの値を指定すること。
};
■ getHostByAddr
SYNTAX
struct hostent * getHostByAddr( char *addr, int len, int type );
DESC
IP Address から Host情報をえる
■ inet_addr
SYNTAX
int inet_addr(
const char FAR * // 文字列表現アドレスへのポインタ ( "xxx.xxx.x.x" )
);
RET
アドレスの数値表現: 成功
INADDR_NONE : 失敗
DESC
ドット表記のIPv4アドレス文字列を 4byte の整数(ネットワークバイトオーダー)へ変換
BAD
// ドメイン名はダメ
inet_addr( "library.city.chuo.tokyo.jp" )
OK
// dot 表記を渡すこと
inet_addr( "210.233.111.187" );
■ inet_ntoa
SYNTAX
char * inet_ntoa (
struct in_addr in // アドレスが格納されている in_addr 構造体
);
RET
文字列へのポインタ : 成功
NULL : 失敗
DESC
バイナリ表現の IPアドレスを 10進数のドット表記文字列( "127.0.0.1" など )に変換。
■ WinSock.固有
■ WSAStartup
SYNTAX
int WSAStartup (
WORD wVersionRequested, // 使用したい version
LPWSADATA lpWSAData); // WinSock の実装情報を取得する.
RET
0: OK
!0: FAIL
DESC
DLL を初期化する
■ WSACleanup
SYNTAX
int WSACleanup ();
RET
0: OK
!0: FAIL
DESC
WinSock の Resource を解放する
■ WSAGetLastError
SYNTAX
int WSAGetLastError()
DESC
最後( 直近 )に発生したエラーコードを返す。
■ 非同期.API
■ WSAWaitForMultipleEvents
SYNTAX
DWORD WSAWaitForMultipleEvents(
1, // Event数
&hEvent, // EventObject
FALSE,
5*1000, // 待ち時間 msec
FALSE
);
DESC
Socket に関連つけられたネットワークイベントを指定時間まつ
(connect | recv | cloes )をまつ
// Accept を指定する
//
WSAWaitForMultipleEvents()
// Client が接続してきた !
if ( FD_ACCEPT ) {
}
■ WSAEnumNetworkEvents
SYNTAX
nRet = WSAEnumNetworkEvents(
Socket,
hEvent,
&events
);
DESC
発生したNetworkイベントの種類を調べる
■ WSAEventSelect
SYNTAX
nRet = WSAEventSelect(
Socket, // 関連づける Socket
hEvent, // Event
FD_READ|FD_CONNECT|FD_CLOSE // 通知をうける Event の種類を設定
);
RET
0 : 成功
!0 : ERRORCODE
DESC
ソケットを[ 非ブロッキング ]にし[ ネットワークイベント ]を関連付ける
イベントがおきると イベントオブジェクトが Signal 状態になる
yfWaitAll [in]
A value that specifies the wait type
wait の type を指定する
If TRUE
the function returns when the state of all objects in the lphEvents array is signaled
If FALSE
the function returns when any of the event objects is signaled
( すべての Event に対してかえす )
In the latter case
the return value minus WSA_WAIT_EVENT_0 indicates the index of the event object
whose state caused the function to return
If more than one event object became signaled during the call
this is the array index to the signaled event object
with the smallest index value of all the signaled event objects
なんか false でよさげ
fAlertable [in]
〈物・事が〉変えられる, 改められる.
A value that specifies whether the thread is placed in an alertable wait state
so the system can execute I/O completion routines
If TRUE
the thread is placed in an altertable wait state
and
WSAWaitForMultipleEvents can return when the system executes
an I/O completion routine
In this case
WSA_WAIT_IO_COMPLETION is returned and the
event that was being waited on is not signaled yet
The application must call the WSAWaitForMultipleEvents function again
If FALSE
the thread is not placed in an altertable wait state and I/O completion routines
are not executed
■ データ型
■ sockaddr
DESC
汎用のアドレス構造体
特定のアドレスファミリ固有ではない。
struct sockaddr {
unsigned short sa_family;
char sa_data[14]
};
■ sockaddr_in
DESC
インターネットアドレスファミリの構造体
POINT
sockaddr 構造体をインターネットファミリ向けに定義しなおしただけのため、
sockaddr を受け取る関数にキャストして渡すことができる。
設定する値はすべてネットワークバイトオーダーであること。
struct sockaddr {
unsigned short sin_family;
unsigned short sin_port; // ポート
struct in_addr sin_addr; // ip
char sin_zero[8]; // 未使用領域
};