社区微信群开通啦,扫一扫抢先加入社区官方微信群
社区微信群
Linux,GCC
客户端和服务端的TCP文件传输,客户端可以上传文件到服务端,也可以从服务端下载文件,还可以查看和修改服务端的工作目录(临时),查看客户端当前目录。
效果图参考页尾,那么话不多说,直接上码(只有部分,完整请访问码云)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <fcntl.h>
#include <pthread.h>
#include "tools.h"
#define pf printf
char cmd[20] = {};
// 开始运行
void *start_run(void *arg);
// 客户端上传
void c_up(int *clifd);
// 客户端下载
void c_down(int *clifd);
// 返回文件列表
void c_list(int *clifd);
typedef struct LS
{
char mode[15]; // 文件的模式
int dir_num; // 是否目录或目录中包含目录的数量
char user[20]; // 文件的用户名
char group[20]; // 文件的组名
long size; // 文件的字节数
char time[30]; // 文件的最后修改时间
int st_mode; // 文件类型和权限
char name[20]; // 文件名
} LS;
// 主函数
int main()
{
pf("服务器创建socket...n");
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (0 > sockfd)
{
perror("socket");
return -1;
}
pf("准备地址...n");
struct sockaddr_in addr = {};
addr.sin_family = AF_INET;
// 端口 IP 自行修改
addr.sin_port = htons(60000);
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
socklen_t len = sizeof(addr);
pf("绑定socket与地址...n");
if (bind(sockfd, (struct sockaddr *)&addr, len))
{
perror("bind");
return -1;
}
pf("设置监听...n");
if (listen(sockfd, 5))
{
perror("listen");
return -1;
}
pf("等待客户端连接...n");
for (;;)
{
struct sockaddr_in addrcli = {};
// int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
int *clifd = (int*)malloc(sizeof(int));
*clifd = accept(sockfd, (struct sockaddr *)&addrcli, &len);
if (0 > *clifd)
{
perror("accept");
continue;
}
pthread_t pid;
// 创建线程函数 int pthread_create(pthread_t *restrict tidp,const pthread_attr_t *restrict_attr,void*(*start_rtn)(void*),void *restrict arg);
//第一个参数为指向线程标识符的指针。
//第二个参数用来设置线程属性。
//第三个参数是线程运行函数的地址。
//最后一个参数是运行函数的参数。
pthread_create(&pid, NULL, start_run, (void *)clifd);
}
return 0;
}
// 开始运行
void *start_run(void *arg)
{
int *clifd = (int *)arg;
char up[20] = "我想上你";
char down[20] = "我想下你";
char see[20] = "我想看你";
char quit[20] = "我要走了";
int c_size = 0;
for (;;)
{
c_size = read(*clifd, cmd, sizeof(cmd));
if (strcmp(up, cmd) == 0)
{
pf("收到客户端的上传指令n");
c_up(clifd);
memset(cmd, 0, 20);
}
else if (strcmp(down, cmd) == 0)
{
pf("收到客户端的下载指令n");
c_down(clifd);
memset(cmd, 0, 20);
}
else if (strcmp(see, cmd) == 0)
{
pf("收到客户端的目录指令n");
c_list(clifd);
memset(cmd, 0, 20);
}
else if (strcmp(quit, cmd) == 0)
{
pf("收到服务端的退出指令n");
pthread_exit(0);
return (void *)NULL;
}
}
//char *str = "我死了";
//pthread_exit(str);
}
// 上传
void c_up(int *clifd)
{
int flag = 0;
int r_size = 0;
int w_size = 0;
char buf[1024] = {};
w_size = write(*clifd, "success", 8);
read(*clifd, buf, 10);
if(strncmp(buf, "error", 10) == 0)
{
printf("收到客户端返回error,接收终止n");
return;
}
else if(strncmp(buf, "success", 10) == 0)
{
printf("收到客户端返回success,继续接收n");
}
else
{
printf("收到客户端异常数据:%s,接收终止n", buf);
return;
}
char filename[50] = {};
memset(filename, 0, sizeof(filename));
int f_size = read(*clifd, filename, sizeof(filename));
pf("收到文件名:%sn", filename);
usleep(100000);
w_size = write(*clifd, "success", 8);
pf("发送success给客户端n", r_size);
int fd = open(filename, O_CREAT | O_RDWR, 0777);
do
{
memset(buf, 0, sizeof(buf));
r_size = read(*clifd, buf, sizeof(buf));
pf("[收到字节数:%d ", r_size);
w_size = write(fd, buf, r_size);
pf("写入文件字节数:%d ", w_size);
usleep(10000);
w_size = write(*clifd, "success", 8);
pf("发送success给客户端 ]n");
flag++;
} while (r_size == 1024);
sleep(1);
if (flag > 0)
{
char result[20] = "success";
pf(" 文件传输完毕 返回客户端successnn");
write(*clifd, result, strlen(result) + 1);
}
else
{
char result[20] = "error";
pf(" 文件传输失败 返回客户端errornn");
write(*clifd, result, strlen(result) + 1);
}
close(fd);
return;
}
// 下载
void c_down(int *clifd)
{
DIR *dir;
dir = opendir(".");
char list[1024] = {};
struct dirent *dirent;
int r_size = 0;
int w_size = 0;
char buf[1024] = {};
char buf2[20] = {};
char filename[50] = {};
char filename2[51] = {};
usleep(10000);
w_size = write(*clifd, "success", 8);
usleep(10000);
// 获取目录下所有文件名
while ((dirent = readdir(dir)) != NULL)
{
strcat(list, dirent->d_name);
strcat(list, " ");
}
pf("当前目录列表:%sn", list);
pf("strlen(list):%dn", strlen(list));
int l_size = write(*clifd, list, strlen(list)+1);
pf("发送当前下载目录列表给客户端n");
pf("等待接收文件名...n");
int f_size = read(*clifd, filename, sizeof(filename));
//pf("filename:%sn", filename);
strncpy(filename2, filename, 50);
strcat(filename2, " ");
if (strstr(list, filename2) == NULL || strncmp(filename2, " ", 1) == 0 || strncmp(filename2, " ", 2) == 0)
{
char result[6] = "error";
pf("文件:%s 不存在,下载终止n", filename);
write(*clifd, result, strlen(result));
return;
}
else
{
char result[8] = "success";
pf("文件:%s 存在,开始传输文件内容n", filename);
write(*clifd, result, strlen(result));
int fd = open(filename, O_RDONLY);
//设置文件读写位置为文件尾部
lseek(fd, 0, SEEK_END);
// 获取文件字节数(尾部位置)
off_t end_pos = lseek(fd, 0, SEEK_CUR);
//pf("end_pos:%dn", end_pos);
//设置文件读写位置为文件头部
lseek(fd, 0, SEEK_SET);
usleep(1000000);
do
{
memset(buf, 0, sizeof(buf));
r_size = read(fd, buf, sizeof(buf));
pf("[读取文件字节数:%d ", r_size);
w_size = write(*clifd, buf, r_size);
pf("发送字节数:%d ", w_size);
read(*clifd, result, sizeof(result));
if(strncmp(result, "success", 10) == 0)
{
pf("成功收到客户端端返回的success]n");
}
usleep(10000);
off_t cur_pos = lseek(fd, 0, SEEK_CUR);
//pf("cur_pos:%dn", cur_pos);
if(cur_pos == end_pos && w_size == 1024)
{
char end[1] = " ";
pf("[读取文件字节数:1 ");
w_size = write(*clifd, end, sizeof(end));
pf("发送字节数:%d ", w_size);
read(*clifd, buf2, sizeof(buf2));
if(strncmp(buf2, "success", 10) == 0)
{
pf("成功收到客户端端返回的success]n");
}
else
{
pf("收到客户端返回的异常数据:%s]n", buf2);
}
break;
}
} while (r_size == 1024);
usleep(1000000);
pf(" 文件:%s 发送完毕n", filename);
close(fd);
}
return;
}
// 文件列表
void c_list(int *clifd)
{
DIR *dir;
dir = opendir(".");
char list[1024] = {};
struct dirent *dirent;
while ((dirent = readdir(dir)) != NULL)
{
strcat(list, dirent->d_name);
strcat(list, " ");
}
int l_size = write(*clifd, list, strlen(list) + 1);
memset(list, 0, 1024);
char dirname[20] = {};
int d_size = read(*clifd, dirname, sizeof(dirname));
pf("收到客户端的数据:%sn", dirname);
if(strncmp(dirname, "...", 20) == 0)
{
closedir(dir);
return;
}
if (strncmp(dirname, ".", 20) == 0)
{
dir = opendir(".");
while ((dirent = readdir(dir)) != NULL)
{
strcat(list, dirent->d_name);
strcat(list, " ");
}
l_size = write(*clifd, list, strlen(list) + 1);
}
else if (strncmp(dirname, "..", 20) == 0)
{
chdir("..");
dir = opendir(".");
while ((dirent = readdir(dir)) != NULL)
{
strcat(list, dirent->d_name);
strcat(list, " ");
}
l_size = write(*clifd, list, strlen(list) + 1);
}
else
{
int re = chdir(dirname);
if (re == -1)
{
char result[20] = "目录名错误";
int err = write(*clifd, result, strlen(result) + 1);
}
else
{
dir = opendir(".");
while ((dirent = readdir(dir)) != NULL)
{
strcat(list, dirent->d_name);
strcat(list, " ");
}
l_size = write(*clifd, list, strlen(list) + 1);
}
}
closedir(dir);
return;
}
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <fcntl.h>
#include <errno.h>
#include "tools.h"
#define pf printf
int sockfd = 0;
typedef struct LS
{
char mode[15]; // 文件的模式
int dir_num; // 是否目录或目录中包含目录的数量
char user[20]; // 文件的用户名
char group[20]; // 文件的组名
long size; // 文件的字节数
char time[30]; // 文件的最后修改时间
int st_mode; // 文件类型和权限
char name[20]; // 文件名
} LS;
// 菜单
void menu(void);
// 上传
void upload(void);
// 下载
void download(void);
// 显示服务器目录和文件
void s_list(void);
// 显示客户端目录和文件
void c_list(void);
// 退出程序
void quit(void);
// 主函数
int main()
{
pf("服务器创建socket...n");
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (0 > sockfd)
{
perror("socket");
return -1;
}
pf("准备地址...n");
struct sockaddr_in addr = {};
addr.sin_family = AF_INET;
// 端口自己修改
addr.sin_port = htons(60000);
// IP自行修改
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
socklen_t len = sizeof(addr);
pf("绑定连接服务器...n");
if (connect(sockfd, (struct sockaddr *)&addr, len))
{
perror("connect");
return -1;
}
menu(); // 加载菜单
close(sockfd);
return 0;
}
// 加载菜单
void menu(void)
{
for (;;)
{
system("clear");
pf("*** 局域网文件传输客户端 ***n");
pf(" 1、上传n");
pf(" 2、下载n");
pf(" 3、查看/修改服务端目录n");
pf(" 4、查看客户端目录n");
pf(" 0、退出n");
pf("--------------------------n");
// 我的自定义函数 在tools.c里面 get_cmd
switch (get_cmd('0', '4'))
{
case '1':
upload();
break;
case '2':
download();
break;
case '3':
s_list();
break;
case '4':
c_list();
break;
case '0':
quit();
return;
}
}
}
// 上传
void upload(void)
{
char up[20] = "我想上你";
write(sockfd, up, strlen(up) + 1);
// 打印当前目录文件
c_list();
int r_size = 0;
int w_size = 0;
char buf[1024] = {};
char buf2[20] = {};
r_size = read(sockfd, buf, sizeof(buf));
if(strncmp(buf, "success", 10) != 0)
{
pf("收到服务端异常数据n");
getch();
return;
}
char pathname[100] = {};
char *filename = malloc(50);
memset(filename, 0, 50);
while(1)
{
pf("请输入文件名:");
get_str(pathname, 100);
if(!strncmp(pathname, "..", 3) || !strncmp(pathname, ".", 3))
{
pf(".或..不是普通文件,请重新输入!n");
continue;
}
break;
}
int fd = open(pathname, O_RDONLY);
struct stat stat = {};
int fs = fstat(fd, &stat);
long file_size = 0;
file_size = stat.st_size;
if (fd == -1)
{
pf("文件不存在n");
write(sockfd, "error", 6);
getch();
}
else
{
write(sockfd, "success", 8);
usleep(100000);
if (strrchr(pathname, '/') == NULL)
{
strncpy(filename, pathname, 50);
}
else
{
filename = strrchr(pathname, '/');
filename += 1;
}
pf("发送文件名:%s 至服务端n", filename);
write(sockfd, filename, strlen(filename) + 1);
usleep(100000);
memset(buf, 0, sizeof(buf));
r_size = read(sockfd, buf, sizeof(buf));
if(strncmp(buf, "success", 10) != 0)
{
pf("收到服务端异常数据n");
getch();
return;
}
else
{
pf("收到服务端返回successn");
}
sleep(1);
//设置文件读写位置为文件尾部
lseek(fd, 0, SEEK_END);
// 获取文件字节数(尾部位置)
off_t end_pos = lseek(fd, 0, SEEK_CUR);
//pf("end_pos:%dn", end_pos);
//设置文件读写位置为文件头部
lseek(fd, 0, SEEK_SET);
do
{
//pf(" 进入while循环...n");
r_size = read(fd, buf, 1024);
pf("[读取文件字节数:%d ", r_size);
w_size = write(sockfd, buf, r_size);
pf("发送字节数:%d ", w_size);
read(sockfd, buf2, sizeof(buf2));
if(strncmp(buf2, "success", 10) == 0)
{
pf("成功收到服务端返回的success]n");
}
usleep(10000);
off_t cur_pos = lseek(fd, 0, SEEK_CUR);
//pf("cur_pos:%dn", cur_pos);
if(cur_pos == end_pos && w_size == 1024)
{
char end[1] = " ";
pf("[读取文件字节数:1 ");
w_size = write(sockfd, end, sizeof(end));
pf("发送字节数:%d ", w_size);
read(sockfd, buf2, sizeof(buf2));
if(strncmp(buf2, "success", 10) == 0)
{
pf("成功收到服务端返回的success]n");
}
else
{
pf("收到服务端返回的异常数据:%s]n", buf2);
}
break;
}
} while (r_size == 1024);
close(fd);
char result[20] = {};
read(sockfd, result, sizeof(result));
if(strncmp(buf2, "success", 10) == 0)
{
pf("成功收到服务端返回值:%s,服务器接收文件成功n", result);
}
else if(strncmp(buf2, "error", 10) == 0)
{
pf("成功收到服务端返回值:%s,服务器接收文件异常n", result);
}
else
{
pf("收到服务端返回值:%s,数据异常n", result);
}
getch();
}
return;
}
// 下载
void download(void)
{
int r_size = 0;
int w_size = 0;
char buf[1024] = {};
char filename[50] = {};
char list[1024] = {};
char down[20] = "我想下你";
write(sockfd, down, strlen(down) + 1);
r_size = read(sockfd, buf, sizeof(buf));
if(strncmp(buf, "success", 10) != 0)
{
pf("收到服务端异常数据n");
getch();
return;
}
else
{
pf("服务端成功接收命令n");
}
read(sockfd, list, sizeof(list));
pf("服务端目录列表:%sn", list);
usleep(1000);
while(1)
{
pf("请输入要下载的文件名:");
get_str(filename, 50);
if(!strncmp(filename, "..", 3) || !strncmp(filename, ".", 3))
{
pf(".或..不是普通文件,无法下载,请重新输入!n");
continue;
}
break;
}
write(sockfd, filename, strlen(filename) + 1);
char result[20] = {};
read(sockfd, result, sizeof(result));
if(strncmp(result, "success", 8) == 0)
{
pf("收到服务端发送的数据:%s 文件准备下载n", result);
}
else if(strncmp(result, "error", 8) == 0)
{
pf("收到服务端发送的数据:%s 文件不存在n", result);
getch();
return;
}
else
{
pf("收到服务端发送的数据:%s 数据异常,下载终止n", result);
getch();
return;
}
int fd = open(filename, O_CREAT | O_RDWR, 0777);
do
{
usleep(500);
memset(buf, 0, sizeof(buf));
r_size = read(sockfd, buf, sizeof(buf));
pf("[收到字节数:%d ", r_size);
w_size = write(fd, buf, r_size);
pf("写入文件字节数:%d ", w_size);
w_size = write(sockfd, "success", 8);
pf("发送success给服务端]n");
} while (r_size == 1024);
usleep(1000000);
pf(" 文件:%s 下载完毕n", filename);
close(fd);
getch();
return;
}
// 服务端文件列表
void s_list(void)
{
char see[20] = "我想看你";
write(sockfd, see, strlen(see) + 1);
char list[1024] = {};
read(sockfd, list, sizeof(list));
pf("服务端目录列表: %sn", list);
pf("输入cd+空格+目录,修改服务器工作目录,否则返回上一级n");
char cmd[50] = {};
get_str(cmd, 50);
if (strstr(cmd, "cd ") == NULL)
{
pf("非cd指令,按任意键返回主界面n");
snprintf(cmd, 20, "...");
write(sockfd, cmd, strlen(cmd));
getch();
}
else
{
char *dir = malloc(20);
dir = strrchr(cmd, ' ');
dir += 1;
write(sockfd, dir, strlen(dir) + 1);
read(sockfd, list, sizeof(list));
pf("服务端目录列表: %sn", list);
getch();
}
return;
}
// 客户端文件列表
void c_list(void)
{
pf("当前目录列表:");
DIR *dir;
dir = opendir(".");
struct dirent *dirent;
while ((dirent = readdir(dir)) != NULL)
{
pf(" %s ", dirent->d_name);
}
pf("n");
closedir(dir);
getch();
return;
}
// 退出程序
void quit(void)
{
char buf[100] = {};
pf("告知服务端,我要走了");
char quit[20] = "我要走了";
write(sockfd, quit, strlen(quit) + 1);
usleep(10000);
pf("程序退出n");
return;
}
接着我们运行服务端和客户端
可以看到我们的文件上传成功了,因为篇幅比较大,就简要展示了。
我们将服务端的工作目录修改到上一级目录(也就是客户端同级目录)
指令要求(cd+空格+路径),那么我们就输入cd ..
看下效果
多线程奔溃问题已经做了相应处理,简单测试暂无问题,不过内存泄露倒是个问题0.0
那么功能就介绍完毕了,希望能给大家带来帮助(程序可能有些不足之处,如有建议,望赐教)
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!