头文件
#ifndef __HEAD_H__
#define __HEAD_H__
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <fcntl.h>
#include <wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <semaphore.h>
#include <signal.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/msg.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/un.h>
#include <sys/time.h>
#include <sys/select.h>
#include <poll.h>
#include <errno.h>
#define PRINT_ERR(msg) \
do{\
fprintf(stderr,"line:%d ",__LINE__);\
perror(msg);\
return -1;\
} while(0)
#define IF(i) if (argc != i) {\
printf("input error,try again\n");\
printf("usage: ./a.out srcfile destfile\n");\
return -1;\
}
#endif
服务器
#include <head.h>
#define PORT 8888
#define IP "192.168.31.159"
struct td
{
int newfd;
struct sockaddr_in cin_t;
} td_t;
void *callback(void *arg);
int do_download(int sfd);
int Upload(int sfd);
int View_Directory(int sfd);
int Login(int sfd);
int Register(int sfd);
int main(int argc, const char *argv[])
{
//创建流式套接字
int sfd = socket(AF_INET, SOCK_STREAM, 0);
if (sfd < 0)
PRINT_ERR("socket");
int reuse = 1;
if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0)
PRINT_ERR("setsockopt");
//填充地址信息的结构体,将IP和端口号绑定到套接字上
struct sockaddr_in sin_t;
sin_t.sin_family = AF_INET;
sin_t.sin_port = htons(PORT);
sin_t.sin_addr.s_addr = inet_addr(IP);
//绑定服务器的ip和端口
if (bind(sfd, (struct sockaddr *)&sin_t, sizeof(sin_t)) < 0)
PRINT_ERR("bind");
//将套接字设置为监听状态
if (listen(sfd, 128) < 0)
PRINT_ERR("listen");
printf("服务器监听成功\n");
socklen_t addrlen = sizeof(td_t.cin_t);
pthread_t tid;
while (1)
{
td_t.newfd = accept(sfd, (struct sockaddr *)&td_t.cin_t, &addrlen);
if (td_t.newfd < 0)
PRINT_ERR("accept");
printf("[%s:%d] newfd:%d 成功连接\n", inet_ntoa(td_t.cin_t.sin_addr), ntohs(td_t.cin_t.sin_port), td_t.newfd);
pthread_create(&tid, NULL, callback, (void *)&td_t);
pthread_detach(tid);
}
close(sfd);
return 0;
}
void *callback(void *arg)
{
struct td *p = (struct td *)arg;
int newfd = p->newfd;
struct sockaddr_in cin_t = p->cin_t;
ssize_t res;
char buf[2];
while (1)
{
bzero(buf, sizeof(buf));
//先只读取前两个字节判断其功能,文件名在功能函数读取
res = recv(newfd, buf, 2, 0);
if (res == 0)
{
printf("客户端断开,关闭newfd:%d\n", newfd);
close(newfd);
return NULL;
}
switch (buf[1])
{
case 1:
printf("[%s:%d] newfd:%d请求下载", inet_ntoa(cin_t.sin_addr),
ntohs(cin_t.sin_port), newfd);
do_download(newfd);
break;
case 2:
printf("[%s:%d] newfd:%d请求上传", inet_ntoa(cin_t.sin_addr),
ntohs(cin_t.sin_port), newfd);
Upload(newfd);
break;
case 3:
printf("[%s:%d] newfd:%d请求查看目录\n", inet_ntoa(cin_t.sin_addr),
ntohs(cin_t.sin_port), newfd);
View_Directory(newfd);
break;
case 4:
printf("[%s:%d] newfd:%d请求登录\n", inet_ntoa(cin_t.sin_addr),
ntohs(cin_t.sin_port), newfd);
Login(newfd);
break;
case 5:
printf("[%s:%d] newfd:%d请求注册\n", inet_ntoa(cin_t.sin_addr),
ntohs(cin_t.sin_port), newfd);
Register(newfd);
break;
default:
break;
}
}
}
//客户端下载
int do_download(int newfd)
{
char buf[514];
ssize_t res;
//读取文件名
res = recv(newfd, buf, 20, 0);
if (res < 0)
PRINT_ERR("recv");
puts(buf);
//打开文件,失败则向客户端发送0,表示出错
int fd = open(buf, O_RDONLY);
if (fd < 0)
{
printf("发送失败,无该文件\n");
*(unsigned short *)buf = htons(0);
if (send(newfd, buf, 2, 0) < 0)
PRINT_ERR("send");
return -1;
}
int num = 1;
int size = 0;
while (1)
{
//封装数据包,前两个字节表示编号,后面是数据
bzero(buf, sizeof(buf));
*(unsigned short *)buf = htons(num++);
res = read(fd, buf + 2, 512);
if (res < 0)
PRINT_ERR("read");
if (res == 0)
{
printf("发送完成,大小为%d\n", size);
break;
}
if (send(newfd, buf, res + 2, 0) < 0)
PRINT_ERR("send");
size += res;
//等待客户端读取完上一个数据包然后在给客户端发送下一个
res = recv(newfd, buf, 1, 0);
if (res < 0)
PRINT_ERR("recv");
}
}
//客户端上传
int Upload(int sfd)
{
char filename[20];
ssize_t res;
char buf[514];
//读取文件名
res = recv(sfd, filename, sizeof(filename), 0);
if (res < 0)
PRINT_ERR("recv");
puts(filename);
int size = 0;
int fd, num = 1;
while (1)
{
//给客户端发送我需要的数据包编号
*(unsigned short *)buf = htons(num);
if (send(sfd, buf, 2, 0) < 0)
PRINT_ERR("send");
bzero(buf, sizeof(buf));
//读取数据
res = recv(sfd, buf, sizeof(buf), 0);
if (res < 0)
PRINT_ERR("recv");
//判断客户端是否发送正确的数据
if (htons(num) == *(unsigned short *)buf)
{
num++;
//如果是第一次接收数据则创建文件
if (size == 0)
{
fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT, 0777);
if (fd < 0)
PRINT_ERR("open");
}
//向文件里写数据并计数
if (write(fd, buf + 2, res - 2) < 0)
PRINT_ERR("write");
size += (res - 2);
if (res < sizeof(buf))
{
printf("接收完成,大小为%d\n", size);
close(fd);
break;
}
}
else
{
printf("客户端无该文件\n");
break;
}
}
return 0;
}
//客户端查看
int View_Directory(int sfd)
{
char buf[300];
DIR *dir;
struct dirent *dt;
struct stat st;
if ((dir = opendir("./")) == NULL)
PRINT_ERR("open dir error");
while ((dt = readdir(dir)) != NULL)
{
//是隐藏文件则忽略
if (dt->d_name[0] == '.')
continue;
stat(dt->d_name, &st);
bzero(buf, sizeof(buf));
//封装数据包,包括文件名和文件大小
snprintf(buf, sizeof(buf), "%-9s %ld\n", dt->d_name, st.st_size);
if (send(sfd, buf, sizeof(buf), 0) < 0)
PRINT_ERR("send");
//等待客户端读取完上一个数据包然后在给客户端发送下一个
if (recv(sfd, buf, 1, 0) < 0)
PRINT_ERR("recv");
}
bzero(buf, sizeof(buf));
*(unsigned short *)buf = htons(1);
//给客户端发送1,表示发送完毕
if (send(sfd, buf, 2, 0) < 0)
PRINT_ERR("send");
closedir(dir);
}
//客户端登录
int Login(int sfd)
{
ssize_t res;
char buf[50];
//读取用户名和密码
res = recv(sfd, buf, sizeof(buf), 0);
if (res < 0)
PRINT_ERR("recv");
printf("用户名:%s 密码:%s\n", buf, buf + strlen(buf) + 1);
FILE *fp;
if ((fp = fopen("usr.txt", "r")) == NULL)
PRINT_ERR("usr.txt");
typedef struct
{
char name[20];
char pass[20];
} usr;
usr *p[50];
int i = 0;
while (1)
{
if ((p[i] = (usr *)malloc(sizeof(usr))) == NULL)
{
printf("内存申请失败\n");
return -1;
}
if (fscanf(fp, "%s%s", p[i]->name, p[i]->pass) < 0)
{
if (errno == 0)
{
break;
}
else
{
PRINT_ERR("fscanf");
}
}
i++;
}
int flag = 0;
for (int j = 0; j < i; j++)
{
if (strcmp(buf, p[j]->name) == 0)
{
flag = 1;
if (strcmp(buf + strlen(buf) + 1, p[j]->pass) == 0)
{
//登录成功发送1
printf("登录成功\n");
buf[0] = 1;
if (send(sfd, buf, 1, 0) < 0)
PRINT_ERR("send");
break;
}
//密码不对发送2
printf("密码不正确\n");
buf[0] = 2;
if (send(sfd, buf, 1, 0) < 0)
PRINT_ERR("send");
break;
}
}
if (flag == 0)
{
//不存在该用户发送0
printf("不存在该用户\n");
buf[0] = 0;
if (send(sfd, buf, 1, 0) < 0)
PRINT_ERR("send");
}
for (int j = 0; j < i; j++)
{
free(p[j]);
p[j] = NULL;
}
fclose(fp);
return 0;
}
//客户端注册
int Register(int sfd)
{
ssize_t res;
char buf[50];
//读取用户名和密码
res = recv(sfd, buf, sizeof(buf), 0);
if (res < 0)
PRINT_ERR("recv");
printf("用户名:%s 密码:%s\n", buf, buf + strlen(buf) + 1);
FILE *fp;
if ((fp = fopen("usr.txt", "a+")) == NULL)
PRINT_ERR("usr.txt");
typedef struct
{
char name[20];
char pass[20];
} usr;
usr *p[50];
int i = 0;
while (1)
{
if ((p[i] = (usr *)malloc(sizeof(usr))) == NULL)
{
printf("内存申请失败\n");
return -1;
}
if (fscanf(fp, "%s%s", p[i]->name, p[i]->pass) < 0)
{
if (errno == 0)
{
break;
}
else
{
PRINT_ERR("fscanf");
}
}
i++;
}
int flag = 0;
for (int j = 0; j < i; j++)
{
if (strcmp(buf, p[j]->name) == 0)
{
printf("注册失败,已有该用户\n");
buf[0] = 0;
//注册失败向客户端发送0
if (send(sfd, buf, 1, 0) < 0)
PRINT_ERR("send");
flag = 1;
break;
}
}
if (flag == 0)
{
fprintf(fp, "%s %s\n", buf, buf + strlen(buf) + 1);
fflush(fp);
printf("注册成功\n");
buf[0] = 1;
//注册成功向客户端发送1
if (send(sfd, buf, 1, 0) < 0)
PRINT_ERR("send");
}
for (int j = 0; j < i; j++)
{
free(p[j]);
p[j] = NULL;
}
fclose(fp);
return 0;
}
客户端
#include <head.h>
#define PORT 8888
#define IP "192.168.31.159"
int do_download(int sfd);
int Upload(int sfd);
int View_Directory(int sfd);
int Login(int sfd);
int Register(int sfd);
int main(int argc, const char *argv[])
{
//创建流式套接字
int sfd = socket(AF_INET, SOCK_STREAM, 0);
if (sfd < 0)
PRINT_ERR("socket");
struct sockaddr_in sin_t;
sin_t.sin_family = AF_INET;
sin_t.sin_port = htons(PORT);
sin_t.sin_addr.s_addr = inet_addr(IP);
if (connect(sfd, (struct sockaddr *)&sin_t, sizeof(sin_t)) < 0)
PRINT_ERR("connect");
printf("成功连接服务器[%s:%d]\n", inet_ntoa(sin_t.sin_addr), ntohs(sin_t.sin_port));
char c;
while (1)
{
system("clear");
printf("*******************\n");
printf("*******1.登录******\n");
printf("*******2.注册******\n");
printf("*******3.退出******\n");
printf("*******************\n");
printf("请输入>>>");
c = getchar();
while (getchar() != 10)
;
switch (c)
{
case '1':
c = Login(sfd);
break;
case '2':
Register(sfd);
break;
case '3':
goto END;
break;
default:
printf("请输入正确的选项\n");
break;
}
if (c == 1)
{
printf("输入任意字符跳转>>>");
while (getchar() != 10)
;
break;
}
printf("输入任意字符清屏>>>");
while (getchar() != 10)
;
}
while (1)
{
system("clear");
printf("*******************\n");
printf("*******1.下载******\n");
printf("*******2.上传******\n");
printf("*******3.查看******\n");
printf("*******4.退出******\n");
printf("*******************\n");
printf("请输入>>>");
c = getchar();
while (getchar() != 10)
;
switch (c)
{
case '1':
do_download(sfd);
break;
case '2':
Upload(sfd);
break;
case '3':;
View_Directory(sfd);
break;
case '4':
goto END;
break;
default:
printf("请输入正确的选项\n");
break;
}
printf("输入任意字符清屏>>>");
while (getchar() != 10)
;
}
END:
close(sfd);
return 0;
}
//下载
int do_download(int sfd)
{
printf("请输入文件名\n");
char filename[20];
fgets(filename, sizeof(filename), stdin);
filename[strlen(filename) - 1] = 0;
char buf[514];
//封装第一个数据包,包括功能编号和文件名
int size = sprintf(buf, "%c%c%s%c", 0, 1, filename, 0);
if (send(sfd, buf, size, 0) < 0)
PRINT_ERR("send");
size = 0;
int fd, num = 1;
ssize_t res;
while (1)
{
//接收服务器数据
bzero(buf, sizeof(buf));
res = recv(sfd, buf, sizeof(buf), 0);
if (res < 0)
PRINT_ERR("recv");
//判断编号是否正确
if (htons(num) == *(unsigned short *)buf)
{
num++;
//如果是第一次接收数据则创建文件
if (size == 0)
{
fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT, 0777);
if (fd < 0)
PRINT_ERR("open");
}
if (write(fd, buf + 2, res - 2) < 0)
PRINT_ERR("write");
size += (res - 2);
//给服务器发送一个字节表示客户端已读取完数据,请求发送下一个数据包
if (send(sfd, buf, 1, 0) < 0)
PRINT_ERR("send");
if (res < 514)
break;
}
else
{
printf("下载失败无该文件\n");
break;
}
}
if (size != 0)
{
printf("下载完成,大小为%d\n", size);
close(fd);
}
return 0;
}
//上传
int Upload(int sfd)
{
printf("请输入文件名\n");
char filename[50];
fgets(filename, sizeof(filename), stdin);
filename[strlen(filename) - 1] = 0;
int t = -1;
for (int i = 0; i < strlen(filename); i++)
{
if (filename[i] == '/')
t = i;
}
char buf[514];
//封装第一个数据包,包括功能编号和文件名
int size = sprintf(buf, "%c%c%s%c", 0, 2, filename + t + 1, 0);
if (send(sfd, buf, size, 0) < 0)
PRINT_ERR("send");
size = 0;
int fd, num = 1;
ssize_t res;
while (1)
{
bzero(buf, sizeof(buf));
res = recv(sfd, buf, 2, 0);
if (res < 0)
PRINT_ERR("recv");
//判断服务器是否发送正确的数据
if (htons(num) == *(unsigned short *)buf)
{
//如果是第一次发送数据则打开文件
if (size == 0)
{
fd = open(filename, O_RDONLY);
//打开文件失败给服务器发送0
if (fd < 0)
{
*(unsigned short *)buf = htons(0);
if (send(sfd, buf, 2, 0) < 0)
PRINT_ERR("send");
PRINT_ERR("open");
}
}
//给每个数据包的头两个字节是编号
bzero(buf, sizeof(buf));
*(unsigned short *)buf = htons(num);
num++;
res = read(fd, buf + 2, sizeof(buf) - 2);
if (res < 0)
PRINT_ERR("read");
if (send(sfd, buf, res + 2, 0) < 0)
PRINT_ERR("send");
size += res;
if (res < sizeof(buf) - 2)
{
printf("上传完毕\n");
break;
}
}
else
{
printf("服务器创建文件出错,上传失败\n");
break;
}
}
if (size != 0)
{
printf("上传完成,大小为%d\n", size);
close(fd);
}
return 0;
}
//查看
int View_Directory(int sfd)
{
char buf[50];
buf[1] = 3;
//发送第一个数据包只有两个字节表示功能
if (send(sfd, buf, 2, 0) < 0)
PRINT_ERR("send");
while (1)
{
bzero(buf, sizeof(buf));
if (recv(sfd, buf, sizeof(buf), 0) < 0)
PRINT_ERR("recv");
//服务器端发送1则表示发送完毕
if (*(unsigned short *)buf == htons(1))
break;
printf("%s", buf);
//给服务器发送一个字节表示客户端已读取完数据,请求发送下一个数据包
if (send(sfd, buf, 1, 0) < 0)
PRINT_ERR("send");
}
}
//登录
int Login(int sfd)
{
char name[20];
char password[30];
char buf[50];
printf("请输入用户名和密码\n");
printf("(用户名和密码用空格隔开)\n");
scanf("%s %s", name, password);
while (getchar() != 10)
;
int size = sprintf(buf, "%c%c%s%c%s%c", 0, 4, name, 0, password, 0);
if (send(sfd, buf, size, 0) < 0)
PRINT_ERR("send");
if (recv(sfd, buf, 1, 0) < 0)
PRINT_ERR("recv");
if (buf[0] == 1)
{
printf("登录成功\n");
return 1;
}
else if (buf[0] == 0)
printf("不存在该用户\n");
else if (buf[0] == 2)
printf("密码不正确\n");
return 0;
}
//注册
int Register(int sfd)
{
char name[20];
char password[30];
char buf[50];
printf("请输入要注册的用户名和密码\n");
printf("(用户名和密码用空格隔开)\n");
scanf("%s %s", name, password);
while (getchar() != 10)
;
int size = sprintf(buf, "%c%c%s%c%s%c", 0, 5, name, 0, password, 0);
if (send(sfd, buf, size, 0) < 0)
PRINT_ERR("send");
if (recv(sfd, buf, 1, 0) < 0)
PRINT_ERR("recv");
if (buf[0] == 1)
printf("注册成功\n");
else
printf("注册失败,已存在该用户\n");
return 0;
}
注册成功
注册重复用户
密码不正确登录
不存在用户名登录
登录成功
下载正确
下载失败
上传本目录文件正确
上传其他目录文件正确
上传错误文件
查询服务器目录文件
退出