客户端
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#define SEARCH 1 //查目录
#define DOWN 2 //下载
#define FAIL 3 //失败
#define SUCCESS 4 //成功
#define END 5 //查询目录结束
#define UP 6 //上传
struct msg
{
int type; //要执行的操作
int flag; //记录成功或者失败
char buff[128];
char fname[50];
};
//查询目录
int search_serverdir(int sockfd, struct msg info)
{
info.type = SEARCH;
if(send(sockfd, &info, sizeof(info), 0)<0)
{
perror("send");
return -1;
}
while (1)
{
int res=recv(sockfd, &info, sizeof(info), 0);
if(res<0)
{
perror("recv");
return -1;
}
if(0==res)
{
printf("服务器断开连接\n");
break;
}
if (info.flag == END)
{
break;
}
printf("%s \n", info.buff);
}
printf("\n");
return 0;
}
//下载文件
int ftp_download(int sockfd, struct msg info)
{
int res;
info.type = DOWN;
printf("请输入要下载的文件名:");
fgets(info.fname, sizeof(info.fname), stdin);
info.fname[strlen(info.fname) - 1] = '\0';
send(sockfd, &info, sizeof(info), 0);
res=recv(sockfd, &info, sizeof(info), 0);
if(res<0)
{
perror("recv");
return -1;
}
if(0==res)
{
printf("服务器断开连接\n");
return -1;
}
if (info.flag == FAIL)
{
printf("服务器打开文件失败\n");
return -1;
}
if (info.flag == SUCCESS)
printf("正在下载.....\n");
FILE *fp;
fp = fopen(info.fname, "w");
if (fp == NULL)
{
perror("fopen");
return -1;
}
while (1)
{
bzero(info.buff,sizeof(info.buff));
res = recv(sockfd, info.buff, sizeof(info.buff), 0);
if (res < 0)
{
perror("recv");
return -1;
}
fwrite(info.buff, 1, res, fp);
if (res < sizeof(info.buff))
break;
}
printf("下载成功\n");
fclose(fp);
return 0;
}
//上传文件
int upload_ftp(int sockfd, struct msg info)
{
int res;
info.type = UP;
START:
printf("请输入要上传的文件名:");
fgets(info.fname, sizeof(info.fname), stdin);
info.fname[strlen(info.fname) - 1] = '\0';
if(access(info.fname,F_OK)!=0)
{
printf("文件不存在\n");
goto START;
}
send(sockfd, &info, sizeof(info), 0);
res=recv(sockfd, &info, sizeof(info), 0);
if(res<0)
{
perror("recv");
return -1;
}
if(0==res)
{
printf("服务器断开连接\n");
return -1;
}
if (info.flag == FAIL)
{
printf("服务器出现文件上传错误!\n");
return -1;
}
if (info.flag == SUCCESS)
printf("正在上传中....\n");
FILE *fp;
char name[100];
sprintf(name, "./%s", info.fname);
fp = fopen(name, "r");
if (fp == NULL)
{
perror("fopen:");
return -1;
}
while (1)
{
bzero(info.buff,sizeof(info.buff));
res = fread(info.buff, 1, sizeof(info.buff), fp);
if (send(sockfd, info.buff, res, 0) < 0)
{
perror("send");
return -1;
}
if (res < sizeof(info.buff) && feof(fp) != 0) //文件结尾
break;
}
printf("上传成功\n");
fclose(fp);
}
int main(int argc, const char *argv[])
{
//创建套接字
int sockfd;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1)
{
perror("socket");
return -1;
}
//绑定信息
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(8888);
sin.sin_addr.s_addr = inet_addr("192.168.31.90");
// connect
if (connect(sockfd, (struct sockaddr *)&sin, sizeof(sin)) < 0)
{
perror("connect");
return -1;
}
char c = 0;
struct msg info;
while (1)
{
system("clear");
printf("=======================TCP文件传输客户端============================\n");
printf("-----------------------------功能菜单------------------------------\n");
printf("\t\t\t1.查询文件\n");
printf("\t\t\t2.下载文件\n");
printf("\t\t\t3.上传文件\n");
printf("\t\t\t0.退出系统\n");
printf("-------------------------------------------------------------------\n");
printf("请选择你要执行的操作:");
c = getchar();
while (getchar() != 10)
;
switch (c)
{
case '1':
search_serverdir(sockfd, info);
break;
case '2':
ftp_download(sockfd, info);
break;
case '3':
upload_ftp(sockfd, info);
break;
case '0':
close(sockfd);
return 0;
default:
printf("输入错误,请重新输入\n");
}
printf("点击任意处清屏:");
while (getchar() != 10)
;
}
return 0;
}
服务器
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <dirent.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include <errno.h>
#define SEARCH 1
#define DOWN 2
#define FAIL 3
#define SUCCESS 4
#define END 5
#define UP 6
struct msg
{
int type;
int flag;
char buff[128];
char fname[50];
};
//回收僵尸进程
typedef void (*sighandler_t)(int);
void hander(int sig)
{
while (waitpid(-1, NULL, WNOHANG) > 0)
;
}
//将指定目录中的所有文件名发给客户端
int search_serverdir(int acceptfd, struct msg info)
{
DIR *dp = opendir("./download/");
if (NULL == dp)
{
perror("open dir");
return -1;
}
struct dirent *dir = NULL;
while (1)
{
dir = readdir(dp);
if (NULL == dir)
{
if (0 == errno)
{
//printf("读取完毕\n");
break;
}
else
{
perror("readdir error");
}
}
if (dir->d_name[0] != '.') //去掉隐藏文件
{
bzero(info.buff,sizeof(info.buff));
strcpy(info.buff, dir->d_name);
if (send(acceptfd, &info, sizeof(info), 0) < 0)
{
perror("send");
return -1;
}
}
}
info.flag = END; // readdir完成
if (send(acceptfd, &info, sizeof(info), 0) < 0) //把标志位发出
{
perror("send");
return -1;
}
}
//将某文件中的内容传给客户端
int ftp_download(int acceptfd, struct msg info)
{
FILE *fp;
char name[100];
sprintf(name, "./download/%s", info.fname);
fp = fopen(name, "r");
if (fp == NULL)
{
info.flag = FAIL; //服务器打开文件失败
if (send(acceptfd, &info, sizeof(info), 0) < 0)
{
perror("send");
return -1;
}
return -1;
}
else
{
info.flag = SUCCESS; //服务器打开文件成功
send(acceptfd, &info, sizeof(info), 0);
}
int res;
while (1)
{
bzero(info.buff, sizeof(info.buff));
res = fread(info.buff, 1, sizeof(info.buff), fp);
if (send(acceptfd, info.buff, res, 0) < 0)
{
perror("send");
return -1;
}
if (res < sizeof(info.buff) && feof(fp) != 0) //读到文件结尾
break;
}
fclose(fp);
return 0;
}
//接收客户端上传的文件内容
int upload_ftp(int connfd, struct msg info)
{
FILE *fp;
char name[100];
sprintf(name, "./download/%s", info.fname);
//新建文件
fp = fopen(name, "w");
if (fp == NULL)
{
info.flag = FAIL; //打开文件失败
if (send(connfd, &info, sizeof(info), 0) < 0)
{
perror("send");
return -1;
}
return -1;
}
else
{
info.flag = SUCCESS;
if (send(connfd, &info, sizeof(info), 0) < 0)
{
perror("send");
return -1;
}
}
//接收上传文件的内容
int res;
while (1)
{
bzero(info.buff, sizeof(info.buff));
res = recv(connfd, info.buff, sizeof(info.buff), 0);
if (res < 0)
{
perror("recv");
return -1;
}
fwrite(info.buff, 1, res, fp);
if (res < sizeof(info.buff)) //写完
break;
}
fclose(fp);
return 0;
}
int main(int argc, const char *argv[])
{
sighandler_t s = signal(SIGCHLD, hander);
if (SIG_ERR == s)
{
perror("signal");
return -1;
}
int sockfd;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
//允许端口快速重用
int reuse = 1;
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0)
{
perror("setsockopt");
return -1;
}
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(8888);
sin.sin_addr.s_addr = inet_addr("192.168.31.90");
if (bind(sockfd, (struct sockaddr *)&sin, sizeof(sin)) == -1)
{
perror("bind");
return -1;
}
if (listen(sockfd, 10) == -1)
{
perror("listen");
return -1;
}
int acceptfd;
struct sockaddr_in cin;
socklen_t len = sizeof(cin);
pid_t pid;
struct msg info;
int res;
while (1)
{
acceptfd = accept(sockfd, (struct sockaddr *)&cin, &len);
if (acceptfd < 0)
{
perror("accept");
return -1;
}
pid = fork();
if (pid < 0)
{
perror("fork");
return -1;
}
else if (0 == pid)
{
close(sockfd);
while (1)
{
res = recv(acceptfd, &info, sizeof(info), 0);
if (res < 0)
{
perror("recv");
return -1;
}
if (0 == res)
{
printf("客户端退出\n");
goto end;
}
switch (info.type)
{
case SEARCH:
search_serverdir(acceptfd, info);
break;
case DOWN:
ftp_download(acceptfd, info);
break;
case UP:
upload_ftp(acceptfd, info);
break;
}
}
}
else
{
close(acceptfd);
}
}
end:
return 0;
}
测试
查看服务器目录文件
下载文件
上传文件
小结
在实现的过程中,遇到了一些问题,最让人头大的还是TCP的粘包现象,在传输过程中,有很大的问题,我先前定义一个较大的数组,把结构体的信息放在数组中发送,接收时再把数组内容强转为结构体。也可以避免粘包现象,但是下载和上传功能实现不是太好。最终我修改了结构体的大小,也没有发生粘包现象,各功能均可以实现。但是总感觉不是很好。