プロセス間通信、UDP/IP

システム・プログラムI

                                       電子・情報工学系
                                       新城 靖
                                       <yas@is.tsukuba.ac.jp>

このページは、次の URL にあります。
http://www.hlla.is.tsukuba.ac.jp/~yas/coins/syspro1-1998/1998-06-23
あるいは、次のページから手繰っていくこともできます。
http://www.hlla.is.tsukuba.ac.jp/~yas/coins/
http://www.hlla.is.tsukuba.ac.jp/~yas/index-j.html

■復習

■変数の番地、メモリ・マップ(再掲載)

最初は、 4月21日
----------------------------------------------------------------------
   1:	/*
   2:	        vaddr-print.c -- 変数の番地をしらべるプログラム
   3:	        ~yas/syspro1/cc/vaddr-print.c
   4:	        $Header: vaddr-print.c,v 1.1 97/05/19 23:19:10 yas Exp $
   5:	        Start: 1997/05/19 22:58:49
   6:	*/
   7:	#include <stdlib.h>
   8:	
   9:	int x1=1 ;
  10:	int x2 ;
  11:	
  12:	extern int etext, edata, end ;
  13:	
  14:	main( argc,argv,envp )
  15:	    int argc ;
  16:	    char *argv[] ;
  17:	    char *envp[] ;
  18:	{
  19:	    int x3 ;
  20:	    char *x4p ;
  21:	
  22:	        printf("&main == 0x%x \n",&main );
  23:	        printf("&etext == 0x%x \n",&etext );
  24:	        printf("&edata == 0x%x \n",&edata );
  25:	        printf("&end   == 0x%x \n",&end );
  26:	
  27:	        printf("&x1 == 0x%x (data)\n",&x1 );
  28:	        printf("&x2 == 0x%x (bss)\n",&x2 );
  29:	        printf("&x3 == 0x%x (auto)\n",&x3 );
  30:	        x4p = malloc( 10 );
  31:	        printf("x4p == 0x%x (heap)\n",x4p );
  32:	        x4p = malloc( 10 );
  33:	        printf("x4p == 0x%x (heap)\n",x4p );
  34:	        recursive( 3 );
  35:	}
  36:	
  37:	recursive( n )
  38:	    int n ;
  39:	{
  40:	    int x5 ;
  41:	        printf("&x5 == 0x%x (auto,%d)\n",&x5,n );
  42:	        if( n<=0 )
  43:	            return;
  44:	        recursive( n-1 );
  45:	}
----------------------------------------------------------------------

実行例。
----------------------------------------------------------------------
% cp ~yas/syspro1/cc/vaddr-print.c . [←]
% make vaddr-print [←]
cc     vaddr-print.c   -o vaddr-print
% ./vaddr-print [←]
&main == 0x400ad0
&etext == 0x400cb0
&edata == 0x10002000
&end   == 0x10002000
&x1 == 0x100010d0 (data)
&x2 == 0x10001130 (bss)
&x3 == 0x7fff2f1c (auto)
x4p == 0x10002010 (heap)
x4p == 0x10002028 (heap)
&x5 == 0x7fff2ef4 (auto,3)
&x5 == 0x7fff2ecc (auto,2)
&x5 == 0x7fff2ea4 (auto,1)
&x5 == 0x7fff2e7c (auto,0)
% size vaddr-print [←]
text    data    bss     dec     hex     filename
2933    304     28      3265    cc1     vaddr-print
% []
----------------------------------------------------------------------

■echo-client-udp

UDP/IP のポート番号 7 (echo) では、受け取ったデータをそのまま返すサー ビスを提供している。以下は、このサービスを利用するクライアントである。

----------------------------------------------------------------------
   1:	
   2:	/*
   3:	        echo-client-udp.c -- 文字列を送受信するクライアント(UDP/IP版)
   4:	        ~yas/syspro1/ipc/echo-client-udp.c
   5:	        $Header: /home/lab2/OS/yas/syspro1/ipc/RCS/echo-client-udp.c,v 1.4 1998/06/22 14:23:48 yas Exp $
   6:	        Start: 1997/06/16 21:22:26
   7:	*/
   8:	#include <stdio.h>
   9:	#include <sys/types.h>  /* socket() */
  10:	#include <sys/socket.h> /* socket() */
  11:	#include <netinet/in.h> /* struct sockaddr_in */
  12:	#include <netdb.h>      /* gethostbyname() */
  13:	
  14:	extern void echo_client_udp( char *hostname, int portno );
  15:	extern int udp_port_not_bound();
  16:	extern int sockaddr_in_init( struct sockaddr_in *addr, char *hostname, int portno );
  17:	extern void sockaddr_in_print( struct sockaddr_in *addr );
  18:	extern void sockname_print( int s );
  19:	
  20:	main( int argc, char *argv[] )
  21:	{
  22:	        if( argc != 3 )
  23:	        {
  24:	            fprintf( stdout,"Usage: %s host port\n",argv[0] );
  25:	            exit( -1 );
  26:	        }
  27:	        echo_client_udp( argv[1],atoi(argv[2]) );
  28:	}
  29:	
  30:	void echo_client_udp( char *hostname, int portno )
  31:	{
  32:	    int s, rcount, scount, len, fromlen ;
  33:	    struct sockaddr_in to, from ;
  34:	    char buf[BUFSIZ];
  35:	
  36:	        s = udp_port_not_bound();
  37:	        if( s<0 )
  38:	            exit( -1 );
  39:	        printf("my port is ");  sockname_print( s );
  40:	
  41:	        strncpy( buf,"hello",sizeof(buf) );
  42:	        len = strlen( buf ) + 1 ;
  43:	        sockaddr_in_init( &to, hostname, portno );
  44:	        printf("sending [%s] (%d bytes) to ", buf,len );
  45:	        sockaddr_in_print( &to );
  46:	        if( scount = sendto( s, buf, len, 0, &to, sizeof(to) )!= len )
  47:	        {
  48:	            perror("sendto()");
  49:	            exit( 1 );
  50:	        }
  51:	        printf("after sendto(), my port is ");  sockname_print( s );
  52:	
  53:	        fromlen = sizeof( from );
  54:	        if( (rcount = recvfrom( s, buf, BUFSIZ, 0, &from, &fromlen )) < 0 )
  55:	        {
  56:	            perror("recvfrom()");
  57:	            exit( 1 );
  58:	        }
  59:	        printf("received from "); sockaddr_in_print( &from );
  60:	        buf[rcount] = 0 ;
  61:	        printf("%d bytes received. [%s] \n", rcount, buf );
  62:	
  63:	        close( s );
  64:	}
  65:	
  66:	int udp_port_not_bound()
  67:	{
  68:	    int s ;
  69:	        if( (s = socket(AF_INET, SOCK_DGRAM, 0)) < 0 )
  70:	        {
  71:	            perror("socket");
  72:	            return( -1 );
  73:	        }
  74:	        return( s );
  75:	}
  76:	
  77:	int sockaddr_in_init( struct sockaddr_in *addr, char *hostname, int portno )
  78:	{
  79:	    struct hostent *hostent ;
  80:	        addr->sin_family = AF_INET ;
  81:	        if( (hostent = gethostbyname( hostname )) == NULL )
  82:	        {
  83:	            fprintf(stderr,"unknown host %s\n",hostname );
  84:	            return( -1 );
  85:	        }
  86:	        bcopy( hostent->h_addr, &addr->sin_addr, hostent->h_length );
  87:	        addr->sin_port = htons( portno );
  88:	        return( 0 );
  89:	}
  90:	
  91:	void sockaddr_in_print( struct sockaddr_in *addr )
  92:	{
  93:	    union {
  94:	        int i ;
  95:	        unsigned char byte[4] ;
  96:	    } x ;
  97:	        x.i = addr->sin_addr.s_addr ;
  98:	        printf("sockaddr_in: %d.%d.%d.%d:%d\n",
  99:	               x.byte[0],x.byte[1],x.byte[2],x.byte[3],
 100:	               ntohs( addr->sin_port ));
 101:	}
 102:	
 103:	void sockname_print( int s )
 104:	{
 105:	    struct sockaddr_in addr ;
 106:	    int len ;
 107:	        len = sizeof( addr );
 108:	        if( getsockname( s, &addr, &len )< 0 )
 109:	        {
 110:	            perror("getsockname");
 111:	            exit( -1 );
 112:	        }
 113:	        sockaddr_in_print( &addr );
 114:	}
----------------------------------------------------------------------

クライアント側では、bind() で名前を付けていない。sendto() の時に、自動 的にオペレーティング・システムにより、名前が付けられる。同じことは、 porno=0 で bind() しても可能である。オペレーティング・システムによって 付けられた名前は、後で getsockname() で調べることができる。

明示的に bind() する方法もある。

実行例。


----------------------------------------------------------------------
% ./echo-client-udp adonis1 7 [←]
my port is sockaddr_in: 0.0.0.0:0
sending [hello] (6 bytes) to sockaddr_in: 130.158.86.1:7
after sendto(), my port is sockaddr_in: 0.0.0.0:8013
received from sockaddr_in: 130.158.86.1:7
6 bytes received. [hello] 
% ./echo-client-udp adonis11 7 [←]
my port is sockaddr_in: 0.0.0.0:0
sending [hello] (6 bytes) to sockaddr_in: 130.158.86.11:7
after sendto(), my port is sockaddr_in: 0.0.0.0:8014
received from sockaddr_in: 130.158.86.11:7
6 bytes received. [hello] 
% ./echo-client-udp localhost 7 [←]
my port is sockaddr_in: 0.0.0.0:0
sending [hello] (6 bytes) to sockaddr_in: 127.0.0.1:7
after sendto(), my port is sockaddr_in: 0.0.0.0:8015
received from sockaddr_in: 127.0.0.1:7
6 bytes received. [hello] 
% []
----------------------------------------------------------------------

■echo-server-udp

UDP/IP のポート番号 7 (echo) では、受け取ったデータをそのまま返すサー ビスを提供している。以下は、これと同じような機能を提供するサーバである。

----------------------------------------------------------------------
   1:	
   2:	/*
   3:	        echo-server-udp.c -- 文字列を送受信するサーバ(UDP/IP版) 
   4:	        ~yas/syspro1/ipc/echo-server-udp.c
   5:	        $Header: /home/lab2/OS/yas/syspro1/ipc/RCS/echo-server-udp.c,v 1.3 1998/06/22 14:24:17 yas Exp $
   6:	        Start: 1997/06/16 21:22:26
   7:	*/
   8:	#include <stdio.h>
   9:	#include <sys/types.h>  /* socket() */
  10:	#include <sys/socket.h> /* socket() */
  11:	#include <netinet/in.h> /* struct sockaddr_in */
  12:	#include <netdb.h>      /* gethostbyname() */
  13:	
  14:	extern void echo_server_udp( int portno );
  15:	extern int udp_port_bind( int portno );
  16:	extern int sockaddr_in_init( struct sockaddr_in *addr, char *hostname, int portno );
  17:	extern void sockaddr_in_print( struct sockaddr_in *addr );
  18:	extern void sockname_print( int s );
  19:	
  20:	main( int argc, char *argv[] )
  21:	{
  22:	    int portno ;
  23:	        if( argc >= 3 )
  24:	        {
  25:	            fprintf( stdout,"Usage: %s host port\n",argv[0] );
  26:	            exit( -1 );
  27:	        }
  28:	        if( argc == 2 )
  29:	            portno = atoi( argv[1] );
  30:	        else
  31:	            portno = getuid();
  32:	        echo_server_udp( portno );
  33:	}
  34:	
  35:	void echo_server_udp( int portno )
  36:	{
  37:	    int s, rcount, scount, addrlen ;
  38:	    struct sockaddr_in addr ;
  39:	    char buf[BUFSIZ];
  40:	
  41:	        s = udp_port_bind( portno );
  42:	        if( s<0 )
  43:	            exit( -1 );
  44:	        printf("my port is ");  sockname_print( s );
  45:	
  46:	        while( 1 )
  47:	        {
  48:	            addrlen = sizeof( addr );
  49:	            if( (rcount = recvfrom( s, buf, BUFSIZ, 0, &addr, &addrlen )) < 0 )
  50:	            {
  51:	                perror("recvfrom()");
  52:	                exit( 1 );
  53:	            }
  54:	            buf[rcount] = 0 ;
  55:	            printf("received %d bytes [%s] from ",rcount, buf );
  56:	            sockaddr_in_print( &addr );
  57:	            printf("sending back [%s] (%d bytes) to ", buf, rcount );
  58:	            sockaddr_in_print( &addr );
  59:	            if( scount = sendto( s, buf, rcount, 0, &addr, addrlen )!= rcount )
  60:	            {
  61:	                perror("sendto()");
  62:	                exit( 1 );
  63:	            }
  64:	        }
  65:	}
  66:	
  67:	int udp_port_bind( int portno )
  68:	{
  69:	    struct hostent *hostent ;
  70:	    struct sockaddr_in addr ;
  71:	    int addr_len ;
  72:	    int s ;
  73:	
  74:	        if( (s = socket(AF_INET, SOCK_DGRAM, 0)) < 0 )
  75:	        {
  76:	            perror("socket");
  77:	            return( -1 );
  78:	        }
  79:	
  80:	        addr.sin_family = AF_INET ;
  81:	        addr.sin_addr.s_addr = INADDR_ANY ;
  82:	        addr.sin_port = htons( portno );
  83:	
  84:	        if( bind(s,&addr,sizeof(addr)) < 0 )
  85:	        {
  86:	            perror( "bind: " );
  87:	            fprintf(stderr,"port number %d is already used. kill another program.", portno );
  88:	            return( -1 );
  89:	        }
  90:	        return( s );
  91:	}
<以下省略>
----------------------------------------------------------------------

実行例。

サーバ側。サーバは、終了しないので、最後に、^CDel を押して、割り込みを掛けて終了させる。


----------------------------------------------------------------------
% ./echo-server-udp  [←]
my port is sockaddr_in: 0.0.0.0:1231
received 6 bytes [hello] from sockaddr_in: 130.158.86.11:5890
sending back [hello] (6 bytes) to sockaddr_in: 130.158.86.11:5890
received 6 bytes [hello] from sockaddr_in: 130.158.86.1:8023
sending back [hello] (6 bytes) to sockaddr_in: 130.158.86.1:8023
received 6 bytes [hello] from sockaddr_in: 127.0.0.1:8024
sending back [hello] (6 bytes) to sockaddr_in: 127.0.0.1:8024
^C
% []
----------------------------------------------------------------------
クライアント側(その1)。

----------------------------------------------------------------------
% ./echo-client-udp adonis1 1231 [←]
my port is sockaddr_in: 0.0.0.0:0
sending [hello] (6 bytes) to sockaddr_in: 130.158.86.1:1231
after sendto(), my port is sockaddr_in: 0.0.0.0:5890
received from sockaddr_in: 130.158.86.1:1231
6 bytes received. [hello] 
% []
----------------------------------------------------------------------
クライアント側(その2)。

----------------------------------------------------------------------
% ./echo-client-udp adonis1 1231 [←]
my port is sockaddr_in: 0.0.0.0:0
sending [hello] (6 bytes) to sockaddr_in: 130.158.86.1:1231
after sendto(), my port is sockaddr_in: 0.0.0.0:8023
received from sockaddr_in: 130.158.86.1:1231
6 bytes received. [hello] 
%
----------------------------------------------------------------------
クライアント側(その3)。

----------------------------------------------------------------------
% ./echo-client-udp localhost 1231 [←]
my port is sockaddr_in: 0.0.0.0:0
sending [hello] (6 bytes) to sockaddr_in: 127.0.0.1:1231
after sendto(), my port is sockaddr_in: 0.0.0.0:8024
received from sockaddr_in: 127.0.0.1:1231
6 bytes received. [hello] 
% []
----------------------------------------------------------------------

■練習問題と課題

★練習問題66 malloc()、freeの仕組み

malloc() と free() の動きを調べなさい。 malloc() と free() の動きから、その仕組みを想像しなさい。

malloc() は、brk() システム・コール、または、sbrk システム・コールでオ ペレーティング・システムからメモリの割り当てを受けている。brk(), sbrk() は、プロセスのデータ・セグメントの終わりを変化させるシステム・ コールである。

★練習問題67 浅いコピーと深いコピー

main(int argc, argv[]) の引数である argv をコピーするプログラムを作りなさい。コピーの方法としては、次の2種類が 考えられる。

ヒント:malloc() でメモリを割り当てて、内容をコピーする。コピーの方法 では、浅いコピーでは、文字列(文字の配列の先頭番地)をコピーする。深い コピーでは、strlen() +1 バイト malloc() して、番地を保存して strncpy() する。配列の終わりの 0 を忘れないように。

strdup() というライブラリ関数を使うと便利。

★練習問題68 UDP/IPで送れるデータの大きさの上限

UDP/IP では、一度に送ることができるデータの大きさにには、実装上の制限 が付いている。これがいくつかを調べるプログラムを作りなさい。

★練習問題69 udprelay

UDP/IP のデータを中継するようなプログラムを作りなさい。 単方向だけでなく、双方向で中継するようにしなさい。

このようなプログラムの例として、udprelay と呼ばれるプログラムがある。

★練習問題70 UDP/IPでのアクセス制御

UDP/IP のサーバ、または、中継プログラムで、クライアントの IP アドレス によってアクセスを許したりエラーを発生させたりしなさい。

■課題提出方法


↑[もどる] [課題提出方法] ←[6月9日] ・[6月16日] →[6月23日]
[sized_oi/stream/initport]
Last updated: 1998/06/22 23:57:00
Yasushi Shinjo / <yas@is.tsukuba.ac.jp>