使用 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)
我正在尝试为文件传输实现 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)