linux下的简单文件服务器和客户端程序
本文是我的一次作业,由于花了很多精力,记下来以后可能还会用到。代码部分是从老师那拷贝的,作业是实现代码中没有实现的put和delete命令对文件的操作。我对代码的理解都做了标注,有点乱,但阅读方便。服务端代码server.c#include"unp.h"#include#include#includevoid ftpserv(int sockfd);void sig_ch
·
本文是我的一次作业,由于花了很多精力,记下来以后可能还会用到。代码部分是从老师那拷贝的,作业是实现代码中没有实现的put和delete命令对文件的操作。我对代码的理解都做了标注,有点乱,但阅读方便。本程序的命令要求
Dir/ls
后接字符串,列出服务器的某个目录的内容
Get
后接两个字符串,下载远程文件到本地目录
Delete
后接字符串,删除远程文件
Put
后接字符串,上传文件到远程目录
服务端和客户端都有一个user文件夹保存上传和下载的文件
本文全部代码和资源在http://download.csdn.net/detail/u012296503/9513179可下载
服务端代码server.c
#include "unp.h"
#include <string.h>
#include <unistd.h>
#include <dirent.h>
void ftpserv(int sockfd);
void sig_chld(int signo);
ssize_t Readline2(int fd, void *ptr, size_t maxlen);
int main(int argc, char **argv)
{
int listenfd, connfd;
pid_t childpid;
// 套接字地址结构的长度,一般为unit32_t
socklen_t clilen;
// 网际套接字地址结构
struct sockaddr_in cliaddr, servaddr;
void sig_chld(int);
// family是AF_INET表示地址族,type是SOCK_STREAM表示字节流套接字,protocol是0表示TCP传输协议
// listenfd是套接字描述符
listenfd = Socket(AF_INET, SOCK_STREAM, 0);
/*
以下代码,一直到listen函数,表示在待捆绑到该TCP套接字的网际接口套接字地址结构中填入
通配地址(INADDR_ANY)和服务器的端口号为21000.捆绑通配地址是在告知系统,要是系统是
多宿主机,我们将接受目的地址为任何本地接口的连接。在选择通配地址和指定端口号时,内核
选择IP地址,进程指定端口号。
*/
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(21000);
// bind将一个本地协议地址赋予一个套接字,对于TCP服务器,就限定了该套接字只接受那些目的地为
// 为这个IP地址的客户连接。
Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));
// listen将该套接字装换成一个监听套接字,导致套接字从CLOSED状态转变成LISTEN状态。
Listen(listenfd, LISTENQ);
// 当SIGCHLD信号发生,函数sig_chld就会被调用
Signal(SIGCHLD, sig_chld); /* must call waitpid() */
for ( ; ; )
{
clilen = sizeof(cliaddr);
if ( (connfd = accept(listenfd, (SA *) &cliaddr, &clilen)) < 0)
{
if (errno == EINTR)
continue; /* back to for() */
else
err_sys("accept error");
}
if ( (childpid = Fork()) == 0)
{ /* child process */
Close(listenfd); /* close listening socket */
ftpserv(connfd); /* process the request */
exit(0);
}
Close(connfd); /* parent closes connected socket */
}
}
void ftpserv(int sockfd)
{
ssize_t n;
char buf[MAXLINE];
char sendline[MAXLINE];
char wdpath[200];
// char * getcwd(char * buffer, size_t size);会将当前的工作目录绝对路径复制到参数buffer所指的内存空间,
// 参数size 为buffer的空间大小。获得当前目录
if(getcwd(wdpath,200)==NULL)return ;
strcat(wdpath,"/user");
char dirname[1000];
char dirlen[7];
DIR *pdir;
struct dirent *pent;
if ((pdir = opendir(wdpath)) == NULL)
{
fprintf(stderr, "open dir failed.\n");
return;
}
again:
while ( (n = Readline2(sockfd, buf, MAXLINE)) > 0)
{
//Writen(sockfd, buf, n);
fprintf(stderr,"n=%d\nbuf=%s",n,buf);
/*作用于字符串buf,以包含在" \n"中的字符为分界符,将buf切分成一个个子串;
cmd指向第一个子串。要想遍历子串需要一个循环
while(cmd != NULL){
printf("%s",cmd);
cmd = strtok(NULL," \n"); //指针后移
}
*/
char * cmd=strtok(buf," \n");
if( strcmp(cmd,"dir") ==0 || strcmp(cmd,"ls")==0)
{
char * pdirname=dirname;
int lines=0;
// 从目录流中读取所有的目录项,并拷贝该目录结构的d_name字段
while (1)
{
pent = readdir(pdir);
if (pent == NULL) break;
// fprintf(stderr, "%5d %s\n",pent->d_ino, pent->d_name);
strcpy(pdirname,pent->d_name);
strcat(pdirname,"\r\n");
// 输出目录下的各个文件
fprintf(stderr, "%d %s",strlen(pdirname), pdirname);
lines++;
pdirname += strlen(pent->d_name)+2;
}
*pdirname=0;
// 将lines打印到字符串中
sprintf(dirlen,"%d\n",lines);
fprintf(stderr,"lines %d\n",lines);
// 将dirlen写入套接字,以便客户端可以读取,此处写入行数
Writen(sockfd, dirlen, strlen(dirlen));
// 将目录中的文件名写入套接字中,以便客户端读取
Writen(sockfd,dirname,strlen(dirname));
// 目录项归起始位置,以便下次dir命令重新读取目录项。
rewinddir(pdir);
}
else if(strcmp(cmd,"get")==0)
{
char filename[100];
// 拼接目录
strcpy(filename,wdpath);
strcat(filename,"/");
// strtok(NULL," \n")是get后面的文件名
strcat(filename, strtok(NULL," \n"));
FILE *fp=0;
if((fp=fopen(filename,"rb"))==NULL)
{
fprintf(stderr,"failed open file %s",filename);
return;
}
fprintf(stderr, "open file %s.\n",filename);
long filelen;
/*
定义函数 int fseek(FILE * stream,long offset,int whence);
函数说明 fseek()用来移动文件流的读写位置。参数stream为已打开的文件指针,参数offset为根据参数whence来移动读写位置的位移数。
参数 whence为下列其中一种:
SEEK_SET从距文件开头offset位移量为新的读写位置。SEEK_CUR 以目前的读写位置往后增加offset个位移量。
SEEK_END将读写位置指向文件尾后再增加offset个位移量。
当whence值为SEEK_CUR 或SEEK_END时,参数offset允许负值的出现。
*/
fseek(fp,0,SEEK_END);
/*
定义函数 long ftell(FILE * stream);
函数说明 ftell()用来取得文件流目前的读写位置。参数stream为已打开的文件指针。
返回值 当调用成功时则返回目前的读写位置,若有错误则返回-1,errno会存放错误代码。
错误代码 EBADF 参数stream无效或可移动读写位置的文件流。
*/
filelen=ftell(fp);
// rewind()用来把文件流的读写位置移至文件开头
rewind(fp);
char flen[7];
sprintf(flen,"%d\n",(int)filelen);
fprintf(stderr,"filesize %s\n",flen);
// 服务器端算出文件的大小,并写入套接字以供客户端读取
Writen(sockfd, flen, strlen(flen));
// 同时显示出来文件的大小
fprintf(stderr, "sent filesize=%s",flen);
long left=filelen;
/*
.一般调用形式
fread(buffer,size,count,fp);
fwrite(buffer,size,count,fp);
说明
(1)buffer:是一个指针,对fread来说,它是读入数据的存放地址。对fwrite来说,是要输出数据的地址。
(2)size:要读写的字节数;
(3)count:要进行读写多少个size字节的数据项;
(4)fp:文件型指针。
*/
// 如果文件大小大于MAXLINE就分次读,然后写入套接字。
while(left>MAXLINE)
{
fread(sendline,MAXLINE,1,fp);
Writen(sockfd, sendline, MAXLINE);
left-=MAXLINE;
}
// 对于剩下的部分再处理
if(left<=MAXLINE)
{
fread(sendline,left,1,fp);
Writen(sockfd, sendline, left);
}
fprintf(stderr, "file sent done.\n");
fclose(fp);
} /**/
else if(strcmp(cmd,"put")==0){
if(Readline2(sockfd,recvline,MAXLINE) == 0){
err_quit("str_cli: client terminated prematurely");
}
int bytes = atoi(recvline);
fprintf(stderr,"file length = %d.\n",bytes);
int left = bytes;
FILE *fp = 0;
if(left > 0){
char filename[100];
strcpy(filename,wdpath);
strcat(filename,"/");
strcat(filename,strtok(NULL," \n"));
if((fp = fopen(filename,"wb")) == NULL){
fprintf(stderr,"failed open file %s",filename);
return;
}
fprintf(stderr,"file create: %s.\n",filename);
}
while(left > MAXLINE){
Readn(sockfd,recvline,MAXLINE);
left -= MAXLINE;
fwrite(recvline,MAXLINE,1,fp);
}
if(left <= MAXLINE){
Readn(sockfd,recvline,left);
fwrite(recvline,left,1,fp);
}
fprintf(stderr,"file writen done.\n");
fclose(fp);
}
else if(strcmp(cmd,"delete")==0){
char filename[100];
char name[30];
strcpy(filename,wdpath);
strcat(filename,"/");
strcat(filename,strtok(NULL," \n"));
printf("filename is %s\n",filename);
sprintf(name,"%s\n",filename);
Writen(sockfd,name,strlen(name));
if(remove(filename) == 0){
fprintf(stderr,"file delete successfully.\n");
}else{
fprintf(stderr,"file don't exist.\n");
}
}
if (n < 0 && errno == EINTR)
goto again;
else if (n < 0)
err_sys(" read error");
//closedir(pdir);
}
fprintf(stderr,"n=%d\nbuf=%s",n,buf);
}
void sig_chld(int signo)
{
pid_t pid;
int stat;
while ( (pid = waitpid(-1, &stat, WNOHANG)) > 0)
printf("child %d terminated\n", pid);
return;
}
ssize_t
readline(int fd, void *vptr, size_t maxlen)
{
ssize_t n, rc;
char c, *ptr;
ptr = vptr;
for (n = 1; n < maxlen; n++) {
again:
if ( (rc = read(fd, &c, 1)) == 1) {
*ptr++ = c;
if (c == '\n')
break; /* newline is stored, like fgets() */
} else if (rc == 0) {
*ptr = 0;
return(n - 1); /* EOF, n - 1 bytes were read */
} else {
if (errno == EINTR)
goto again;
return(-1); /* error, errno set by read() */
}
}
*ptr = 0; /* null terminate like fgets() */
return(n);
}
/* end readline */
ssize_t
Readline2(int fd, void *ptr, size_t maxlen)
{
ssize_t n;
if ( (n = readline(fd, ptr, maxlen)) < 0)
err_sys("readline error");
return(n);
}
客户端client.c
#include <stdio.h>
#include <stdlib.h>
#include "unp.h"
void ftp_cli(FILE *fp, int sockfd);
ssize_t Readline2(int fd, void *ptr, size_t maxlen);
int main(int argc, char **argv)
{
int sockfd;
// 网际套接字地址结构
struct sockaddr_in servaddr;
if (argc != 2) err_quit("usage: tcpcli <IPaddress>");
sockfd = Socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(21000);
// 将IP地址从数值形式转化为表达式形式
Inet_pton(AF_INET, argv[1], &servaddr.sin_addr);
// 调用connect函数是激发TCP的三路握手过程
Connect(sockfd, (SA *) &servaddr, sizeof(servaddr));
ftp_cli(stdin, sockfd); /* do it all */
exit(0);
}
void ftp_cli(FILE *fp, int sockfd)
{
char sendline[MAXLINE], recvline[MAXLINE];
char cmd [MAXLINE];
// 获得当前目录
char wdpath[200];
if(getcwd(wdpath,200)==NULL)return ;
strcat(wdpath,"/user");
for ( ; ; )
{
fprintf(stderr, "myftpclient-->");
// 使用fputs和fgets从文件指针或者标准输入输出设备读写行数据。
if (Fgets(sendline, MAXLINE, fp) == NULL) return;
else fputs(sendline,stdout);
strcpy(cmd,sendline);
//int i;
//for(i=0;i<strlen();i++)
//fprintf(stderr,"%d\n",(int)(sendline[i]));
char * pcmd=strtok(cmd," \n");
if(strcmp(pcmd,"dir")==0||strcmp(pcmd,"ls")==0)
{
// 将输入的字符串序列写入套接字,以便在服务器端读取
Writen(sockfd, sendline, strlen(sendline));
// 从服务器读取行数在recvline中
if (Readline2(sockfd, recvline, MAXLINE) == 0)
err_quit("str_cli: server terminated prematurely");
// 将字符串转化为整数
int lines=atoi(recvline);
int i;
for(i=0;i<lines;i++)
{
Readline2(sockfd, recvline, MAXLINE);
fprintf(stderr,recvline);
}
}
else if(strcmp(pcmd,"get")==0)
{
Writen(sockfd, sendline, strlen(sendline));
if (Readline2(sockfd, recvline, MAXLINE) == 0)
err_quit("str_cli: server terminated prematurely");
int bytes=atoi(recvline);
fprintf(stderr, "file length = %d.\n",bytes);
int left=bytes;
FILE *fp=0;
if(left>0)
{
char filename[100];
strcpy(filename,wdpath);
strcat(filename,"/");
// strtok(NULL," \n")是get后面的文件名
strcat(filename, strtok(NULL," \n"));
if((fp=fopen(filename,"wb"))==NULL)
{
fprintf(stderr,"failed open file %s",filename);
return;
}
// 打印出文件路径
fprintf(stderr, "file create: %s.\n",filename);
}
// 从套接字中读出数据然后写入文件中
while(left>MAXLINE)
{
Readn(sockfd, recvline, MAXLINE);
left-=MAXLINE;
fwrite(recvline,MAXLINE,1,fp);
}
if(left<=MAXLINE)
{
Readn(sockfd, recvline, left);
//fprintf(stderr,recvline);
fwrite(recvline,left,1,fp);
}
fprintf(stderr, "file writen done.\n");
fclose(fp);
}
else if(strcmp(pcmd,"quit")==0){
fprintf(stderr, "See you!.\n");
return;
}<pre name="code" class="plain"><span style="white-space:pre"> </span>else if(strcmp(pcmd,"delete")==0){
Writen(sockfd,sendline,strlen(sendline));
if (Readline2(sockfd, recvline, MAXLINE) == 0){
err_quit("str_cli: server terminated prematurely");
}else
fprintf(stderr,"file %s delete successfully.\n",recvline);
}else if(strcmp(pcmd,"put") == 0){
Writen(sockfd, sendline, strlen(sendline));
char filename[100];
strcpy(filename,wdpath);
strcat(filename,"/");
strcat(filename,strtok(NULL," \n"));
FILE *fp = 0;
if((fp = fopen(filename,"rb")) == NULL){
fprintf(stderr,"failed open file %s",filename);
return;
}
fprintf(stderr,"open file %s.\n",filename);
long filelen;
fseek(fp,0,SEEK_END);
filelen = ftell(fp);
rewind(fp);
char flen[7];
sprintf(flen,"%d\n",(int)filelen);
fprintf(stderr,"filesize %s\n",flen);
Writen(sockfd,flen,strlen(flen));
fprintf(stderr,"sent filesize=%s",flen);
long left = filelen;
while(left > MAXLINE){
fread(sendline,MAXLINE,1,fp);
Writen(sockfd,sendline,MAXLINE);
left -= MAXLINE;
}
if(left <= MAXLINE){
fread(sendline,left,1,fp);
Writen(sockfd,sendline,left);
}
fprintf(stderr,"file send done.\n");
fclose(fp);
}else{
fprintf(stderr, "usage:cmd [args].\ncmd=\tdir\tls\tget\tquit\n");
}
}
}
ssize_t
readline(int fd, void *vptr, size_t maxlen)
{
ssize_t n, rc;
char c, *ptr;
ptr = vptr;
for (n = 1; n < maxlen; n++) {
again:
if ( (rc = read(fd, &c, 1)) == 1) {
*ptr++ = c;
if (c == '\n')
break; /* newline is stored, like fgets() */
} else if (rc == 0) {
*ptr = 0;
return(n - 1); /* EOF, n - 1 bytes were read */
} else {
if (errno == EINTR)
goto again;
return(-1); /* error, errno set by read() */
}
}
*ptr = 0; /* null terminate like fgets() */
return(n);
}
/* end readline */
ssize_t
Readline2(int fd, void *ptr, size_t maxlen)
{
ssize_t n;
if ( (n = readline(fd, ptr, maxlen)) < 0)
err_sys("readline error");
return(n);
}
执行部分截图
服务端
客户端
更多推荐
已为社区贡献1条内容
所有评论(0)