クラシックTCPサーバー

私の最初のTCPサーバーは数年前に作成されました。 作成の基礎は、R。Stevensonによる「Unix-Professional Programming」です。TCPサーバーを作成する方法はいくつかあります。 この投稿では、古典的なTCPサーバーについてお話したいと思います。







従来のTCPサーバーを構築する場合、3つのコンポーネントを区別できます。



ソケットを使用する場合、以下を行う必要があります。



コードを検討し、コメントを見てください:

1 struct sockaddr_in addr;

2 struct sockaddr_un local;

3

4 int err,len;

5

6 //

7 if( -1 == ( ls = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP ) )) {

8 perror( "Socket can not created!\n");

9 return ;

10 }

11

12 // SO_REUSEADDR

13 // . bind()

14 setsockopt( ls, SOL_SOCKET, SO_REUSEADDR, &rc, sizeof(rc) );

15 memset( &addr , 0, sizeof(addr) );

16

17 //

18 addr.sin_family = AF_INET;

19 addr.sin_port = htons (port);

20 addr.sin_addr.s_addr = INADDR_ANY ;

21

22 //

23 if ( err = bind( ls, (struct sockaddr*) &addr, sizeof(addr) ) < 0 ) {

24 close(ls);

25 perror( "bind error!\n%s ", gai_strerror(err) );

26 return ;

27 }

28

29

30 //

31 if ( listen( ls, 25) < 0) { ;

32 close(ls);

33 perror( "listen error!\n");

34 return ;

35 }

36

37 //

38 pid_t pid = Demonize();

39

40 if ( pid==-1 ) {

41 close(ls);

42 perror( "demonize error!\n");

43 return;

44 }

45

46 //

47 while (true) {

48 // ,

49 rc = accept( ls, NULL, NULL );

50 //

51 pid = fork();

52

53 if ( pid < 0) syslog( LOG_ERR, " fork abort" );

54 if (pid == 0 ) { //

55 close( ls ); //

56

57 process(rc); // ,

58 //

59 close(rc);

60 return;

61 } else {

62 //

63 close( rc ); //

64 }

65

66 } // end while









そして今、いくつかの説明:



行7-10のリスニングソケットへのハンドルを作成します。



#include <sys / socket.h>

intソケット(intドメイン、int型、intプロトコル);



ドメイン定数:

AF_INET-IPソケット

AF_UNIX-UNIXソケット



型定数:

SOCK_STREAM-永続的なソケット

SOCK_DGRAM-データグラムソケット



intプロトコル定数:

IPPROTO_TCP-プロトコルタイプ



ソケット関数は、リスニングソケットのハンドル番号を返します。 結果が0未満の場合、システムエラーです。



12〜28行目ソケットにアドレスを割り当てます。



#include <sys / socket.h>

int setsockopt(int socket、int level、int option_name、const void * option_value、socklen_t option_len);

ソケットオプションを設定します。

SO_REUSEADDRは、bind()関数にローカルアドレスを再利用します

操作の結果が0未満の場合、システムエラーです。



行18〜20は、sockaddr_in構造体のリスニングアドレスとポートを設定します。

addr.sin_family = AF_INET; //ドメインINETのタイプ

addr.sin_port = htons(ポート); //ポートを割り当てます

addr.sin_addr.s_addr = INADDR_ANY; //着信アドレス



バインド関数は、アドレスを名前のないソケットに関連付けます。

#include <sys / socket.h>

int bind(int socket、const struct sockaddr * address、socklen_t address_len);

int socket-ソケット記述子

address-sockaddr構造体のアドレス

address_len-sockaddr構造体のサイズ

操作の結果が0未満の場合、システムエラーです。



Line 31-35のソケットを聞き始めます。 サーバーはリッスン機能を使用して、接続を確立する要求を受け入れることを宣言します。



#include <sys / socket.h>

int listen(int socket、int backlog);

int socket-ソケット記述子

int backlog-保留中の接続要求の最大キュー

操作の結果が0未満の場合、システムエラーです。



行38〜44、プロセスのデモ。 詳細については デーモンプロセスに関する記事。



47〜66行目、接続処理



49行目、accept関数は要求を受け入れ、接続に変換します。

#include <sys / socket.h>

int accept(int socket、struct sockaddr * restrict address、socklen_t * restrict address_len);

int socket-待機しているソケットのハンドル。接続に接続されておらず、後続の接続を待機し続けます。

address-最後にクライアントアドレスが保存されるソケットアドレスの構造へのポインタ

address_len-アドレス構造を配置するためのバッファーのサイズ; addressとaddress_lenがNULLの場合、クライアントアドレスには関心がありません。

accept関数は、接続に関連付けられたソケットへのハンドルを返します。失敗した場合は-1を返します。



接続を処理して以下を受け入れることができますが、原則として、接続は長時間処理され、サーバーは待機できません。したがって、接続が確立された後、子プロセス行51がforkコマンドによって生成されます。 次に、親プロセスで接続記述子を閉じ、子プロセスで待機ソケット記述子を閉じて、接続を処理するユーザー定義関数を呼び出します。

接続処理は、読み取り/書き込みコマンドによって実行されます。

ソケットへの書き込み:書き込み(rc、buff、len)

ソケットから読み取る:読み取り(rc、buff、len)

rc-接続ハンドル

buff-読み取り/書き込みバッファ

lenは長いバッファーです。

接続処理の最後に、ソケットハンドルrcを閉じる必要があります:close(rc)。



含まれていなかった他の記事には、フォークコマンドの説明、シグナル処理、プロセスのデモなど、個別に記載されるものが含まれます。



All Articles