使用 UDP 进行文件传输(套接字 API)时,检测何时停止从服务器接收数据( from() )

detect when to stop receiving data ( recfrom() ) from server when using UDP for file tranfer (sockets API)

我正在尝试为文件传输实现 UDP 版本(出于学习目的,我不关心可靠性 :))。

目前在TCP版本中,我在服务器端有以下代码:

while (1)
{
    /* read file in chunks of 256 bytes */
    unsigned char buff[256] = {0};
    int nread = fread(buff, 1, 256, fp);
    /* If read was success, send data. */
    if (nread > 0)
    {
        printf("Sending \n");
        send(connfd, buff, nread, 0);
    }
    if (nread < 256)
    {
        if (feof(fp))
            printf("End of file\n");
        if (ferror(fp))
            printf("Error reading\n");
        break;
    }
}
close(connfd); // client-specific socket
close(serverfd); // listening socket

在 TCP 版本的客户端,我有这个

while ((bytesReceived = recv(sockfd, recvBuff, sizeof(recvBuff), 0)) > 0) 
{
    fwrite(recvBuff, 1, bytesReceived, fp); 
}

这段代码运行完美。我认为客户端的 while 循环终止的原因是当服务器进程在调用 close(serverfd) 时有序关闭时 recv() returns 0。在删除 'close(serverfd)' 时,客户端进程挂起,我认为这是由于默认情况下 recv() 被阻止。

现在开始我的 UDP 实现:

服务器端:

char send_buf[256];
while((bytes_read = fread(send_buf, sizeof(char), 256, fp))>0)
{
    res = sendto(server_socket, send_buf, sizeof(send_buf), 0, (struct sockaddr*)&si_server, sizeof(si_server));
    if(res == -1) die("send failed");
}
if(!feof(fp)) die("error in reading file");

close(server_socket);

客户端:

while((bytes_rcvd = recvfrom(client_socket, recv_buf, sizeof(recv_buf), 0, (struct sockaddr*)&si_server, &si_server_len)) > 0)
{
    fwrite(recv_buf, 1, bytes_rcvd, fp);
}

close(client_socket);

在此UDP版本中,客户端始终挂起。我无法弄清楚,客户端 while 循环的终止条件是什么。显然对于 UDP,在关闭服务器套接字时,recfrom() 不会 return 0(与 TCP 不同)。我如何确定何时停止调用 recvfrom()?

谢谢!!

使用 UDP 进行大小管理的三种方法:

  • 为每条消息指定一个大小,并在您提交的内容前加上这样的大小 - 您将继续阅读,直到读完 size 个字节。
  • 在单个 UDP 数据包中发送整个消息 - 如果消息大小小于 MTU 大小则非常可行(最好避免超过 MTU 的消息并且不可能超过最大 unsigned short 的消息)
  • 在消息中引入一个终止符,它描述了消息的结尾。通常不切实际。

在您的情况下,选项 2 似乎可行。您的消息大小为 256 字节 - 完全符合标准 MTU - 并且可以一次发送。您必须首先 assemble 缓冲区中的消息,然后发送整个缓冲区。

请注意,我认为您从文件中读取数据的循环已损坏。如果文件中的字节数较少,您只能读取比请求更少的字节数,但在这种情况下,您的 eof 检查将 return 为真。得知您观察到您的文件读取循环执行了不止一次,我会感到非常惊讶。

这是我最初为一个现已关闭的问题编写的一些代码:

最初只有 TCP,但我[只是]添加了[基本的] UDP 支持。

它在所有通信前加上固定大小的前缀 struct,指定有效载荷的长度。 struct 还有一个 command/type 字段,描述需要做什么。

请注意,我已经测试了 UDP 模式。

但是......代码[最初是面向流的]确实对结构和有效负载进行了单独的send/recv调用。

这对多个客户端散布数据包的效果不太好。所以,你需要考虑如何加强这一点。

单个 传出消息中发送 struct 有效负载 [使用 sendmsg 而不是 sendto] 以 固定 最大负载缓冲区大小作为开始。

此外,您还必须决定服务器是否会维护某些每个客户端的状态(例如)正在传输的文件的打开文件描述符和文件位置。


代码在几个单独的文件中:

#!/usr/bin/perl
# FILE: ovcbin/ovcext.pm 755
# ovcbin/ovcext.pm -- ovrcat archive extractor
#
# this is a self extracting archive
# after the __DATA__ line, files are separated by:
#   % filename

ovcext_cmd(@ARGV);
exit(0);

sub ovcext_cmd
{
    my(@argv) = @_;
    local($xfdata);
    local($xfdiv,$divcur,%ovcdiv_lookup);

    $pgmtail = "ovcext";
    ovcinit();
    ovcopt(\@argv,qw(opt_go opt_f opt_t));

    $xfdata = "ovrcat::DATA";
    $xfdata = \*$xfdata;

    ovceval($xfdata);

    ovcfifo($zipflg_all);

    ovcline($xfdata);

    $code = ovcwait();

    ovcclose($xfdata);

    ovcdiv();

    ovczipd_spl()
        if ($zipflg_spl);
}

sub ovceval
{
    my($xfdata) = @_;
    my($buf,$err);

    {
        $buf = <$xfdata>;
        chomp($buf);

        last unless ($buf =~ s/^%\s+([\@$;])//);

        eval($buf);

        $err = $@;
        unless ($err) {
            undef($buf);
            last;
        }

        chomp($err);
        $err = " (" . $err . ")"
    }

    sysfault("ovceval: bad options line -- '%s'%s\n",$buf,$err)
        if (defined($buf));
}

sub ovcline
{
    my($xfdata) = @_;
    my($buf);
    my($tail);

    while ($buf = <$xfdata>) {
        chomp($buf);

        if ($buf =~ /^%\s+(.+)$/) {
            $tail = ;
            ovcdiv($tail);
            next;
        }

        print($xfdiv $buf,"\n")
            if (ref($xfdiv));
    }

}

sub ovcdiv
{
    my($ofile) = @_;
    my($mode);
    my($xfcur);
    my($err,$prt);

    ($ofile,$mode) = split(" ",$ofile);

    $mode = oct($mode);
    $mode &= 0777;

    {
        unless (defined($ofile)) {
            while ((undef,$divcur) = each(%ovcdiv_lookup)) {
                close($divcur->{div_xfdst});
            }
            last;
        }

        $ofile = ovctail($ofile);

        $divcur = $ovcdiv_lookup{$ofile};
        if (ref($divcur)) {
            $xfdiv = $divcur->{div_xfdst};
            last;
        }
        undef($xfdiv);

        if (-e $ofile) {
            msg("ovcdiv: file '%s' already exists -- ",$ofile);

            unless ($opt_f) {
                msg("rerun with -f to force\n");
                last;
            }

            msg("overwriting!\n");
        }

        unless (defined($err)) {
            ovcmkdir()
                if ($ofile =~ m,^(.+)/[^/]+$,);
        }

        msg("$pgmtail: %s %s",ovcnogo("extracting"),$ofile);
        msg(" chmod %3.3o",$mode)
            if ($mode);
        msg("\n");

        last unless ($opt_go);
        last if (defined($err));

        $xfcur = ovcopen(">$ofile");

        $divcur = {};
        $ovcdiv_lookup{$ofile} = $divcur;

        if ($mode) {
            chmod($mode,$xfcur);
            $divcur->{div_mode} = $mode;
        }

        $divcur->{div_xfdst} = $xfcur;
        $xfdiv = $xfcur;
    }
}

sub ovcinit
{

    {
        last if (defined($ztmp));
        $ztmp = "/tmp/ovrcat_zip";

        $PWD = $ENV{PWD};

        $quo_2 = '"';

        $ztmp_inp = $ztmp . "_0";
        $ztmp_out = $ztmp . "_1";
        $ztmp_perl = $ztmp . "_perl";

        ovcunlink();

        $ovcdbg = ($ENV{"ZPXHOWOVC"} != 0);
    }
}

sub ovcunlink
{

    _ovcunlink($ztmp_inp,1);
    _ovcunlink($ztmp_out,1);
    _ovcunlink($ztmp_perl,($pgmtail ne "ovcext") || $opt_go);
}

sub _ovcunlink
{
    my($file,$rmflg) = @_;
    my($found,$tag);

    {
        last unless (defined($file));

        $found = (-e $file);

        $tag //= "notfound"
            unless ($found);
        $tag //= $rmflg ? "cleaning" : "keeping";

        msg("ovcunlink: %s %s ...\n",$tag,$file)
            if (($found or $ovcdbg) and (! $ovcunlink_quiet));

        unlink($file)
            if ($rmflg and $found);
    }
}

sub ovcopt
{
    my($argv) = @_;
    my($opt);

    while (1) {
        $opt = $argv->[0];
        last unless ($opt =~ s/^-/opt_/);

        shift(@$argv);
        $$opt = 1;
    }
}

sub ovctail
{
    my($file,$sub) = @_;
    my(@file);

    $file =~ s,^/,,;
    @file = split("/",$file);

    $sub //= 2;

    @file = splice(@file,-$sub)
        if (@file >= $sub);

    $file = join("/",@file);

    $file;
}

sub ovcmkdir
{
    my($odir) = @_;
    my(@lhs,@rhs);

    @rhs = split("/",$odir);

    foreach $rhs (@rhs) {
        push(@lhs,$rhs);

        $odir = join("/",@lhs);

        if ($opt_go) {
            next if (-d $odir);
        }
        else {
            next if ($ovcmkdir{$odir});
            $ovcmkdir{$odir} = 1;
        }

        msg("$pgmtail: %s %s ...\n",ovcnogo("mkdir"),$odir);

        next unless ($opt_go);

        mkdir($odir) or
            sysfault("$pgmtail: unable to mkdir '%s' -- $!\n",$odir);
    }
}

sub ovcopen
{
    my($file,$who) = @_;
    my($xf);

    $who //= $pgmtail;
    $who //= "ovcopen";

    open($xf,$file) or
        sysfault("$who: unable to open '%s' -- $!\n",$file);

    $xf;
}

sub ovcclose
{
    my($xfp) = @_;
    my($ref);
    my($xf);

    {
        $ref = ref($xfp);
        last unless ($ref);

        if ($ref eq "GLOB") {
            close($xfp);
            last;
        }

        if ($ref eq "REF") {
            $xf = $$xfp;
            if (ref($xf) eq "GLOB") {
                close($xf);
                undef($$xfp);
            }
        }
    }

    undef($xf);

    $xf;
}

sub ovcnogo
{
    my($str) = @_;

    unless ($opt_go) {
        $str = "NOGO-$str";
        $nogo_msg = 1;
    }

    $str;
}

sub ovcdbg
{

    if ($ovcdbg) {
        printf(STDERR @_);
    }
}

sub msg
{

    printf(STDERR @_);
}

sub msgv
{

    $_ = join(" ",@_);
    print(STDERR $_,"\n");
}

sub sysfault
{

    printf(STDERR @_);
    exit(1);
}

sub ovcfifo
{
}

sub ovcwait
{
    my($code);

    if ($pid_fifo) {
        waitpid($pid_fifo,0);
        $code = $? >> 8;
    }

    $code;
}

sub prtstr
{
    my($val,$fmtpos,$fmtneg) = @_;

    {
        unless (defined($val)) {
            $val = "undef";
            last;
        }

        if (ref($val)) {
            $val = sprintf("(%s)",$val);
            last;
        }

        $fmtpos //= "'%s'";

        if (defined($fmtneg) && ($val <= 0)) {
            $val = sprintf($fmtneg,$val);
            last;
        }

        $val = sprintf($fmtpos,$val);
    }

    $val;
}

sub prtnum
{
    my($val) = @_;

    $val = prtstr($val,"%d");

    $val;
}

END {
    msg("$pgmtail: rerun with -go to actually do it\n")
        if ($nogo_msg);
    ovcunlink();
}

1;
package ovrcat;
__DATA__
% ;
% inetsftp/client.c
// client.c -- client program

#include <inetsftp/inetsftp.h>

int
cli_upload(int argc,char **argv)
{
    int ridx = 0;
    int lidx = 0;
    struct stat st;
    char *file;
    int code = 0;

    if (argc >= 2)
        ridx = 1;

    do {
        file = argv[lidx];
        if (stat(file,&st) < 0) {
            code = errno;
            fprintf(stderr,"cli_upload: '%s' -- %s\n",file,strerror(errno));
            break;
        }

        file = argv[ridx];
        int len = strlen(file) + 1;
        xmsgsnd(CMD_UPLOAD,file,len);

        file = argv[lidx];
        return send_file(file);
    } while (0);

    return code;
}

int
cli_dnload(int argc,char **argv)
{
    int ridx = 0;
    int lidx = 0;
    struct stat st;
    char *file;
    int code = 0;

    if (argc >= 2)
        lidx = 1;

    do {
        file = argv[lidx];
        if (stat(file,&st) >= 0) {
            code = EEXIST;
            fprintf(stderr,"cli_dnload: local '%s' -- %s\n",
                file,strerror(code));
            break;
        }

        file = argv[ridx];
        int len = strlen(file) + 1;
        xmsgsnd(CMD_DNLOAD,file,len);

        file = argv[lidx];
        return recv_file(file);
    } while (0);

    return code;
}

int
cli_doexec(int argc,char **argv)
{
    int sep = 0;
    const char *src;
    char *dst;
    char buf[SIZE];
    xmsg_t xmsg;
    int code = 0;

    dst = buf;

    for (;  argc > 0;  --argc, ++argv) {
        src = *argv;

        if (sep)
            *dst++ = ' ';
        sep = 1;

        strcpy(dst,src);
        dst += strlen(src);
    }

    *dst++ = 0;

    xmsgsnd(CMD_EXEC,buf,dst - buf);

    while (1) {
        xmsgrcv(&xmsg,buf);

        if (xmsg.xmsg_cmd == CMD_EOF) {
            code = xmsg.xmsg_len;
            break;
        }

#if 1
        fputs(buf,stdout);
#else
        fwrite(buf,1,xmsg.xmsg_len - 1,stdout);
#endif
    }

    return code;
}

int
shcmd(int argc,char **argv)
{
    char *cmd = *argv;
    int code = 1;

    do {
        if (argc <= 0) {
            code = 0;
            break;
        }

        dbgprt("shcmd: ARGC %d\n",argc);
        for (char **av = argv;  *av != NULL;  ++av)
            dbgprt("shcmd: ARGV '%s'\n",*av);

        if (strcmp(cmd,"put") == 0) {
            argc = avslide(argc,argv,0);
            if (argc < 2) {
                fprintf(stderr,"shcmd: not enough arguments\n");
                break;
            }
            code = cli_upload(argc,argv);
            break;
        }

        if (strcmp(cmd,"get") == 0) {
            argc = avslide(argc,argv,0);
            if (argc < 2) {
                fprintf(stderr,"shcmd: not enough arguments\n");
                break;
            }
            code = cli_dnload(argc,argv);
            break;
        }

        code = cli_doexec(argc,argv);
    } while (0);

    return code;
}

int
doshell(void)
{
    char *cp;
    int argc;
    char **av;
    char *argv[1000];
    char buf[SIZE];

    while (1) {
        printf("cmd> ");
        fflush(stdout);

        cp = fgets(buf,sizeof(buf),stdin);
        if (cp == NULL)
            break;

        av = argv;
        while (1) {
            cp = strtok((av == argv) ? buf : NULL," \t\n");
            if (cp == NULL)
                break;
            *av++ = cp;
        }
        *av = NULL;
        argc = av - argv;

        shcmd(argc,argv);
    }

    return 0;
}

int
client(int argc,char **argv)
{
    char *ip = "127.0.0.1";
    int port = 8080;
    int c;

    // create socket for client with data type of stream for TCP
    // and check if the file descriptor in below zero
    sock_fd = socket(AF_INET, opt_udp ? SOCK_DGRAM : SOCK_STREAM, 0);
    if (sock_fd < 0) {
        perror("Socket error");
        exit(1);
    }

    dbgprt("Socket created\n");

    // family of the address and port number
    sock_addr.sin_family = AF_INET;
    sock_addr.sin_port = port;

    // ip address of the host
    sock_addr.sin_addr.s_addr = inet_addr(ip);

    // connect to the server
    addr_size = sizeof(sock_addr);
    c = connect(sock_fd, (struct sockaddr *) &sock_addr, addr_size);

    // check the connection
    if (c == -1) {
        perror("Connection error");
        exit(1);
    }
    dbgprt("Connected to server\n");

    if (argc > 0)
        shcmd(argc,argv);
    else
        doshell();

    return 0;
}

int
main(int argc,char **argv)
{

    argc = initopt(argc,argv);
    return client(argc,argv);
}
% inetsftp/common.c
// common.c -- common code

#define _INETSFTP_GLO_
#include <inetsftp/inetsftp.h>

ssize_t
xerr(const char *reason)
{
    int err;

    err = errno;
    fprintf(stderr,"%s: error -- %s\n",reason,strerror(err));
    ipshow((struct sockaddr *) &sock_addr,addr_size);
    errno = err;

    err = -err;

    return err;
}

void
ipshow(const struct sockaddr *sock_addr,socklen_t sock_size)
{
    unsigned char *raw = (unsigned char *) sock_addr;

    fprintf(stderr,"ipshow: sock_size=%u sock_addr=",sock_size);

    for (int idx = 0;  idx < sizeof(*sock_addr);  ++idx) {
        if (idx > 0)
            fprintf(stderr,".");
        fprintf(stderr,"%2.2X",raw[idx]);
    }

    fprintf(stderr,"\n");
}

ssize_t
xrecv(void *buf,size_t buflen)
{
    ssize_t xlen;
    ssize_t totlen = 0;

    for (;  buflen > 0;  buflen -= xlen, buf += xlen, totlen += xlen) {
        if (opt_udp)
            xlen = recvfrom(sock_fd,buf,buflen,0,
                (struct sockaddr *) &sock_addr,&addr_size);
        else
            xlen = recv(sock_fd,buf,buflen,0);

        if (xlen == 0)
            break;

        if (xlen < 0) {
            totlen = xerr("xrecv");
            break;
        }
    }

    return totlen;
}

ssize_t
xsend(const void *buf,size_t buflen)
{
    ssize_t xlen;
    ssize_t totlen = 0;

    for (;  buflen > 0;  buflen -= xlen, buf += xlen, totlen += xlen) {
        if (opt_udp)
            xlen = sendto(sock_fd,buf,buflen,0,
                (struct sockaddr *) &sock_addr,addr_size);
        else
            xlen = send(sock_fd,buf,buflen,0);

        if (xlen == 0)
            break;

        if (xlen < 0) {
            totlen = xerr("xsend");
            break;
        }
    }

    return totlen;
}

ssize_t
xmsgrcv(xmsg_t *xmsg,void *buf)
{
    ssize_t len;
    char junk[SIZE];

    len = xrecv(xmsg,sizeof(*xmsg));
    if (len <= 0) {
        printf("xmsgrcv: EXIT\n");
        exit(1);
    }

    do {
        len = xmsg->xmsg_len;
        if (len <= 0)
            break;

        switch (xmsg->xmsg_cmd) {
        case CMD_EOF:  // len is error code
            break;

        default:
            if (buf == NULL)
                buf = junk;
            xrecv(buf,len);
            break;
        }
    } while (0);

    return len;
}

ssize_t
xmsgsnd(int cmd,const void *buf,int len)
{
    xmsg_t xmsg = { 0 };

    xmsg.xmsg_cmd = cmd;
    xmsg.xmsg_len = len;

    xsend(&xmsg,sizeof(xmsg));

    do {
        if (len == 0)
            break;
        if (buf == NULL)
            break;

        if (cmd == CMD_EOF) {
            len = 0;
            break;
        }

        len = xsend(buf,len);
    } while (0);

    return len;
}

int
send_file(const char *file)
{
    FILE *xfsrc;
    ssize_t xlen;
    unsigned char buf[SIZE];
    int code = 0;

    do {
        xfsrc = fopen(file,"r");

        if (xfsrc == NULL) {
            xmsgsnd(CMD_NOEXIST,NULL,0);
            break;
        }

        while (1) {
            xlen = fread(buf,1,SIZE,xfsrc);
            if (xlen <= 0)
                break;
            xmsgsnd(CMD_PAYLOAD,buf,xlen);
        }

        xmsgsnd(CMD_EOF,NULL,0);

        fclose(xfsrc);
    } while (0);

    return code;
}

int
recv_file(const char *file)
{
    FILE *xfdst;
    xmsg_t xmsg;
    unsigned char buf[SIZE];
    int code = 0;

    xfdst = fopen(file,"w");

    while (1) {
        xmsgrcv(&xmsg,buf);

        if (xmsg.xmsg_cmd == CMD_EOF)
            break;

        if (xmsg.xmsg_cmd == CMD_NOEXIST) {
            unlink(file);
            code = ENOENT;
            break;
        }

        if (xfdst != NULL)
            fwrite(buf,1,xmsg.xmsg_len,xfdst);
    }

    if (xfdst != NULL)
        fclose(xfdst);

    return code;
}

int
initopt(int argc,char **argv)
{

    // skip program name
    argc = avslide(argc,argv,0);

    while (argc > 0) {
        char *cp = *argv;
        if (*cp != '-')
            break;

        cp += 2;
        switch (cp[-1]) {
        case 'u':
            opt_udp = ! opt_udp;
            break;
        }

        argc = avslide(argc,argv,0);
    }

    return argc;
}

int
avslide(int argc,char **argv,int argidx)
{

    --argc;

    for (;  argidx < argc;  ++argidx)
        argv[argidx] = argv[argidx + 1];

    argv[argidx] = NULL;

    return argc;
}
% inetsftp/server.c
// server.c -- server program

#include <inetsftp/inetsftp.h>

cld_t *
reapall(void)
{
    cld_t *cldcur;
    cld_t *cldfree = NULL;
    int status;

    // reap all completed children
    while (1) {
        pid_t pid = wait(&status);
        if (pid <= 0)
            break;

        for (FORALLCLD(cldcur)) {
            if (cldcur->cld_pid == pid) {
                cldcur->cld_status = status;
                cldcur->cld_pid = 0;
                break;
            }
        }
    }

    // find free child slot
    for (FORALLCLD(cldcur)) {
        if (cldcur->cld_pid == 0) {
            cldfree = cldcur;
            break;
        }
    }

    return cldfree;
}

int
svr_doexec(char *cmd)
{
    FILE *xfsrc;
    char *cp;
    char buf[SIZE];
    int code = 0;

    do {
        xfsrc = popen(cmd,"r");
        if (xfsrc == NULL) {
            code = errno;
            break;
        }

        while (1) {
            cp = fgets(buf,sizeof(buf),xfsrc);
            if (cp == NULL)
                break;

            int len = strlen(buf) + 1;
            xmsgsnd(CMD_PAYLOAD,buf,len);
        }

        fclose(xfsrc);
    } while (0);

    xmsgsnd(CMD_EOF,NULL,code);

    return code;
}

int
docld(void)
{
    xmsg_t xmsg;
    char payload[SIZE];
    int stopflg = 0;

    cldcur->cld_pid = getpid();

    while (1) {
        if (stopflg)
            break;

        xmsgrcv(&xmsg,payload);

        stopflg = opt_udp;

        switch (xmsg.xmsg_cmd) {
        case CMD_UPLOAD:
            recv_file(payload);
            break;

        case CMD_DNLOAD:
            send_file(payload);
            break;

        case CMD_EXEC:
            svr_doexec(payload);
            break;

        case CMD_STOP:
            printf("stopping ...\n");
            stopflg = 1;
            break;
        }
    }

    return stopflg;
}

void
server_tcp(void)
{
    int b;

    // now the socket is created and the binding is done, the server must listen
    // for connection requests from clients

    // listen funtion with argument socket file descriptor and set to maximum 10
    // clients
    b = listen(server_fd, MAXCONN);

    // if the listening doesn't work, it must be an error in the binding process
    if (b == 0) {
        printf("Listening...\n");
    }
    else {
        perror("Listening error");
        exit(1);
    }

    while (1) {
        // accept a new connection on the socket with the specified socket file
        // descriptor
        addr_size = sizeof(sock_addr);
        sock_fd = accept(server_fd, (struct sockaddr *) &sock_addr, &addr_size);

        cldcur = reapall();

        cldcur->cld_address = sock_addr;
        cldcur->cld_addrsize = addr_size;

        cldcur->cld_pid = fork();
        if (cldcur->cld_pid == 0) {
            docld();
            exit(0);
            break;
        }
    }
}

void
server_udp(void)
{

    sock_fd = server_fd;
    cldcur = reapall();

    while (1) {
        docld();
    }
}

int
server(int argc,char **argv)
{
    // ip address
    char *ip = "127.0.0.1";
    // port number
    int port = 8080;
    int b;

    // socket file descriptor
    setlinebuf(stdout);
    setlinebuf(stderr);

    // TCP socket, because data type is stream
    server_fd = socket(AF_INET, opt_udp ? SOCK_DGRAM : SOCK_STREAM, 0);
    // if the socket file descriptor is negative is an error in creation of the
    // socket

    // output an error message
    if (server_fd < 0) {
        perror("Error in creating socket");
        exit(1);
    }
    printf("Socket server created\n");

    // family of the server address IPv4
    server_addr.sin_family = AF_INET;

    // port of the server
    server_addr.sin_port = port;
    server_addr.sin_addr.s_addr = inet_addr(ip);

    // bind the socket (port number to ip address)
    b = bind(server_fd, (struct sockaddr *) &server_addr, sizeof(server_addr));
    if (b < 0) {
        perror("Binding error");
        exit(1);
    }
    printf("Binding accomplished\n");

    if (opt_udp)
        server_udp();
    else
        server_tcp();

    return 0;
}

int
main(int argc,char **argv)
{

    argc = initopt(argc,argv);
    return server(argc,argv);
}
% inetsftp/inetsftp.h
// inetsftp.h -- file transfer

#ifndef _inetsftp_h_
#define _inetsftp_h_

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <arpa/inet.h>
#include <sys/stat.h>
#include <sys/wait.h>

#ifdef _INETSFTP_GLO_
#define EXTRN_INETSFTP      /**/
#else
#define EXTRN_INETSFTP      extern
#endif

#if DEBUG || _USE_ZPRT_
#define dbgprt(_fmt...) \
    printf(_fmt)
#else
#define dbgprt(_fmt...) \
    do { } while (0)
#endif

#define SIZE        4096
#define MAXCONN     10

// message header
typedef struct {
    int xmsg_cmd;
    int xmsg_len;
} xmsg_t;

enum {
    CMD_NONE,
    CMD_UPLOAD,                         // upload file
    CMD_DNLOAD,                         // download file
    CMD_PAYLOAD,                        // packet has payload data
    CMD_NOEXIST,                        // file does not exist
    CMD_EXEC,                           // execute command on server
    CMD_EOF,                            // end of payload
    CMD_STOP                            // stop request
};

typedef struct {
    pid_t cld_pid;
    int cld_status;
    struct sockaddr_in cld_address;
    socklen_t cld_addrsize;
} cld_t;

EXTRN_INETSFTP cld_t *cldcur;
EXTRN_INETSFTP cld_t cldlist[MAXCONN];

#define FORALLCLD(_cld) \
    _cld = &cldlist[0];  _cld < &cldlist[MAXCONN];  ++_cld

EXTRN_INETSFTP int opt_udp;
EXTRN_INETSFTP int server_fd;
EXTRN_INETSFTP struct sockaddr_in server_addr;

EXTRN_INETSFTP int sock_fd;
EXTRN_INETSFTP struct sockaddr_in sock_addr;
EXTRN_INETSFTP socklen_t addr_size;

#include <inetsftp/inetsftp.proto>

#endif
% inetsftp/inetsftp.proto
// /home/cae/OBJ/ovrgen/inetsftp/inetsftp.proto -- prototypes

// FILE: /home/cae/preserve/ovrbnc/inetsftp/common.c
// common.c -- common code

    ssize_t
    xerr(const char *reason);

    void
    ipshow(const struct sockaddr *sock_addr,socklen_t sock_size);

    ssize_t
    xrecv(void *buf,size_t buflen);

    ssize_t
    xsend(const void *buf,size_t buflen);

    ssize_t
    xmsgrcv(xmsg_t *xmsg,void *buf);

    ssize_t
    xmsgsnd(int cmd,const void *buf,int len);

    int
    send_file(const char *file);

    int
    recv_file(const char *file);

    int
    initopt(int argc,char **argv);

    int
    avslide(int argc,char **argv,int argidx);
% inetsftp/Makefile
# /home/cae/preserve/ovrbnc/inetsftp -- makefile
PGMTGT += client
PGMTGT += server
CURSRC += common.c
ifndef COPTS
    COPTS += -O2
endif
CFLAGS += $(COPTS)
CFLAGS += -g
CFLAGS += -Wall
CFLAGS += -Werror
CFLAGS += -I..
all: $(PGMTGT)
client: client.c $(CURSRC) $(LIBSRC)
    cc -o client $(CFLAGS) client.c $(CURSRC) $(LIBSRC)
server: server.c $(CURSRC) $(LIBSRC)
    cc -o server $(CFLAGS) server.c $(CURSRC) $(LIBSRC)
clean:
    rm -f $(PGMTGT)