Path: sranha!katsu
From: katsu@sra.co.jp (WATANABE Katsuhiro)
Message-ID: <KATSU.90Dec5121321@sran14.sra.co.jp>
Date: 5 Dec 90 12:13:20
Organization: Software Research Associates, Inc.,Japan
In-reply-to: t-ishii@sra.co.jp's message of 27 Nov 90 06:55:06 GMT
Newsgroups: sra.unix
Subject: Re: TCP send buffer size
Distribution: sra
References: <T-ISHII.90Nov27155506@sran300.sra.co.jp>

  渡邊です。やっぱり私の方の間違いみたいです。

記事 <T-ISHII.90Nov27155506@sran300.sra.co.jp> で
t-ishii@sra.co.jp (Tatsuo Ishii) さんいはく

> getsockopt() で SO_SNDBUF を取ってくると、たとえば NEWS OS R3.4 では 
> 4096 と出ます。これは、scoket に対し、1回の write() で 4096 バイトま
> では送信してくれるという意味だと思っているのですが、果たしてこれは正し
> いでしょうか?

  限定された条件下での答えを見つけました。
  「シグナルを捕捉する場合は、(SO_SNDBUF で返ってくる値などに関係なく)
write() が途中で終ってしまうことがある」
  というものです。(ローカルなディスクのファイルに write() する時は
こんなこと起きませんよね?)

  記事の最後に wt.c というテストプログラムをつけました。これは TCP の
well known なサーバーに接続し、ひたすら write() を繰り返してその
返り値を監視するプログラムです。形式は

wt [-i SIGNAL] [-s sbufsize] [-r rbufsize] [-x] service[@host] writesize

となっていて、service は "echo", "discard" などのサービス名、writesize は
write() の3番目の引数、-s と -r で SO_SNDBUF と SO_RCVBUF の値を設定
できます。さらに、-i INT などとすると、SIGINT を捕捉しますが、-i ALRM と
すると特別で、SIGALRM を捕捉する上に1秒間隔のインターバルタイマを設定
します。-x は、write の返り値が writesize より小さかったら即終了させます。
そうそう、関係ないけど SO_DEBUG を指定してあります。実行すると最初の行に
SO_SNDBUF と SO_RCVBUF の値を表示します。

  そこで、
    wt -i alrm discard@遠くの機械 4096 したり、
    wt -i alrm -s 32768 discard 16384 したり、
    root で inetd を renice 20 した後 wt -i alrm discard 4096 したり、
    wt -i alrm echo 4096 すると、
SIGALRM を捕捉した直後に write() が中断されて戻ってきてしまっているのが
観察できます。これが SIGALRM に限ったことではないのは、wt -i all ...
として ^C や ^Z を打ちまくるとわかります。


  一般の場合(シグナルを捕捉しない場合)については相変わらず不明です。


  ↓ 下はプログラムです。



#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <stdio.h>
#include <netdb.h>
#include <signal.h>
#include <strings.h>
#include <ctype.h>

#define PROTODEBUG
#define ALARM_INTERVAL 1

extern char *malloc();
char *emalloc(), *strucpy();
int sighandle();
char *commandName;		/* argv[0] */
int alarm;			/* whether use interval timer or not */
static char *signame[] = {
	"0", "HUP", "INT", "QUIT", "ILL", "TRAP", "IOT", "EMT",
	"FPE", "KILL", "BUS", "SEGV", "SYS", "PIPE", "ALRM", "TERM",
	"URG", "STOP", "TSTP", "CONT", "CHLD", "TTIN", "TTOU", "IO",
	"XCPU", "XFSZ", "VTALRM", "PROF", "WINCH", "29", "USR1", "USR2"};

main(argc, argv)
int argc;
char **argv;
{
    struct sockaddr_in server;
    struct servent *sp;
    struct hostent *hp;
    int s;
    int writeSize, writtenSize, recvBufSize, sendBufSize, sockoptSize;
    char *data;
    char *hostname, *servname;
    struct itimerval timer;
#ifdef PROTODEBUG
    int debugOpt;
#endif
    int xflag;
    int optionc;
    extern int optind;
    extern char *optarg;

    commandName = argv[0];
    alarm = 0;
    xflag = 0;
    recvBufSize = sendBufSize = -1;
    while ((optionc = getopt(argc, argv, "xi:r:s:")) != EOF) {
	switch (optionc) {
	case 'x':
	    xflag = 1;
	    break;
	case 'i':
	    setsignals(optarg);
	    break;
	case 's':
	    sendBufSize = atoi(optarg);
	    break;
	case 'r':
	    recvBufSize = atoi(optarg);
	    break;
	case '?':
	default:
	    usage();
	    exit(1);
	}
    }
    if (optind != argc - 2) {
	usage();
	exit(1);
    }
    servname = emalloc(strlen(argv[optind]) + 1);
    strcpy(servname, argv[optind]);
    if ((hostname = index(servname, '@')) == (char *)NULL) {
	hostname = "localhost";
    } else {
	*hostname++ = '¥0';
    }
    if ((sp = getservbyname(servname, "tcp")) == (struct servent *)NULL) {
	fprintf(stderr,
		"%s: %s/tcp unknown service.¥n", commandName, servname);
	exit(2);
    }
    if ((hp = gethostbyname(hostname)) == (struct hostent *)NULL) {
	fprintf(stderr, "%s: %s: unknown host.¥n", commandName, hostname);
	exit(3);
    }
    free(servname);
    writeSize = atoi(argv[optind + 1]);

    bzero((char *)&server, sizeof(server));
    bcopy(hp->h_addr, (char *)&server.sin_addr, hp->h_length);
    server.sin_family = hp->h_addrtype;
    server.sin_port = sp->s_port;
    if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
	eperror("socket()");
	exit(11);
    }
    if (connect(s, (char *)&server, sizeof(server)) < 0) {
	eperror("connect()");
	exit(12);
    }
#ifdef PROTODEBUG
    debugOpt = 1;
    if (setsockopt(s, SOL_SOCKET, SO_DEBUG,
		   (char *)debugOpt, sizeof(debugOpt)) < 0) {
	eperror("setsockopt(,,SO_DEBUG,,)");
	exit(13);
    }
#endif
    if (sendBufSize >= 0) {
	if (setsockopt(s, SOL_SOCKET, SO_SNDBUF,
		       (char *)&sendBufSize, sizeof(sendBufSize)) < 0) {
	    eperror("setsockopt(,,SO_SNDBUF,,)");
	    exit(14);
	}
    }
    if (recvBufSize >= 0) {
	if (setsockopt(s, SOL_SOCKET, SO_RCVBUF,
		       (char *)&recvBufSize, sizeof(recvBufSize)) < 0) {
	    eperror("setsockopt(,,SO_RCVBUF,,)");
	    exit(15);
	}
    }
    sockoptSize = sizeof(sendBufSize);
    if (getsockopt(s, SOL_SOCKET, SO_SNDBUF,
		   (char *)&sendBufSize, &sockoptSize) < 0) {
	eperror("getsockopt(,,SO_SNDBUF,,)");
	exit(16);
    }
    sockoptSize = sizeof(recvBufSize);
    if (getsockopt(s, SOL_SOCKET, SO_RCVBUF,
		   (char *)&recvBufSize, &sockoptSize) < 0) {
	eperror("getsockopt(,,SO_RCVBUF,,)");
	exit(17);
    }
    printf("sendBufSize:%d recvBufSize:%d¥n", sendBufSize, recvBufSize);

    data = emalloc(writeSize);
    if (alarm) {
	timer.it_interval.tv_sec = ALARM_INTERVAL;
	timer.it_interval.tv_usec = 0;
	timer.it_value.tv_sec = ALARM_INTERVAL;
	timer.it_value.tv_usec = 0;
	setitimer(ITIMER_REAL, &timer, (struct itimerval *)0);
    }
    while (1) {
	printf("Trying to write(,,%d)...", writeSize);
	fflush(stdout);
	writtenSize = write(s, data, writeSize);
	printf("%d chars written.¥n", writtenSize);
	fflush(stdout);
	if ((writtenSize < writeSize) && xflag) {
	    fprintf(stderr, "%s: write() is given up.¥n", commandName);
	    exit(128);
	}
    }
}

sighandle(sig)
int sig;
{
    fprintf(stderr, "SIG%s is caught...", signame[sig]);
    fflush(stderr);
}

eperror(errFunc)
char *errFunc;
{
    fprintf(stderr, "%s: ", commandName);
    perror(errFunc);
}

usage()
{
    fprintf(stderr,
	    "Usage:%s [-i SIGNAL] [-s sbufsize] [-r rbufsize] [-x] service[@host] writesize¥n",
	    commandName);
}

setsignals(name)
char *name;
{
    int i;
    char *uname;

    uname = emalloc(strlen(name) + 1);
    strucpy(uname, name);
    if (!(strcmp(uname, "ALL"))) {
	for (i = 1; i < NSIG; i++) {
	    signal(i, sighandle);
/*
	    if (i == SIGALRM) {
		alarm = 1;
	    }
*/
	}
    } else {
	for (i = 1; i < NSIG; i++) {
	    if (!(strcmp(uname, signame[i]))) {
		signal(i, sighandle);
		if (i == SIGALRM) {
		    alarm = 1;
		}
		free(uname);
		return;
	    }
	}
	fprintf(stderr,
            "%s: illegal signal name. (should be 'ALL' or one of `kill -l`)¥n",
             commandName);
	exit(1);
    }
    free(uname);
}

/* make a capitalized copy of src */
char *strucpy(dest, src)
char *dest, *src;
{
    char *p;

    p = dest;
    do {
	if (islower(*src)) {
	    *p++ = toupper(*src);
	} else {
	    *p++ = *src;
	}
    } while (*src++);
    return dest;
}

/* malloc with error handling */
char *emalloc(size)
unsigned size;
{
    char *ptr;

    if ((ptr = malloc(size)) == (char *)NULL) {
	fprintf(stderr, "%s: cannot allocate memory.¥n", commandName);
	exit(9);
    }
    return ptr;
}

--
----____----____
渡邊克宏@ソフトウェア工学研旧所