技术点:
chdir:将工作目录切到当前。
fprintf:拼接字符串。
服务器端:
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <dirent.h>
#include <unistd.h>
#include <pthread.h>
#include <signal.h>
#pragma pack(1)
struct file_info
{
char name[300];
int size; //-1表示为文件夹,非负整数表示普通文件
};
#pragma pack()
struct file_info fi;
const char* file_path; //要发送文件的路径
void* srv_thr(void* arg);
void send_file(int sock_conn, const char* path);
int send_regfile(int sock_conn, const char* path);
int send_dir(int sock_conn, const char* path);
int main(int argc, char** argv)
{
if(argc != 2)
{
fprintf(stderr, "命令语法错误!\n");
return -1;
}
if(access(argv[1], R_OK) != 0)
{
fprintf(stderr, "%s不存在或不可读!\n", argv[1]);
return -1;
}
signal(SIGPIPE, SIG_IGN);
//获取要发送的文件的路径
file_path = argv[1];
//第一步:创建监听套接字
//sock_listen:套接字描述符,用来唯一地标识一个套接字
//socket函数的第一个参数表示地址家族
//AF_INET:使用Internet地址家族,即采用TCP/IP网络标准
//socket函数的第二个参数表示套接字类型,主要有三种类型:
//SOCK_STREAM 即流套接字,用于TCP协议通信
//SOCK_DGRAM 即数据报套接字,用于UDP协议通信
//SOCK_RAW 即原始套接字,用于网络底层通信
//socket函数的第三个参数表示使用的通信协议,0表示使用默认的协议
int sock_listen = socket(AF_INET, SOCK_STREAM, 0);
//设置端口复用
int opt_val = 1;
setsockopt(sock_listen, SOL_SOCKET, SO_REUSEADDR, &opt_val, sizeof(opt_val));
//第二步:给套接字绑定一个地址
struct sockaddr_in myaddr;
myaddr.sin_family = AF_INET; //指定地址家族为Internet地址家族
myaddr.sin_addr.s_addr = INADDR_ANY; //指定IP地址为本机任意地址
myaddr.sin_port = htons(9999); //指定端口号为8888
//htons:将一个short类型数据从主机字节序转化为网络字节序
if(-1 == bind(sock_listen, (struct sockaddr*)&myaddr, sizeof(myaddr)))
{
perror("bind");
exit(-1);
}
//第三步:将套接字设置为监听状态
//listen函数的第二个参数表示监听等待队列的长度,通常设置为5
listen(sock_listen, 5);
//设置接收超时为300ms
struct timeval rcv_timeout;
rcv_timeout.tv_sec = 0;
rcv_timeout.tv_usec = 300000;
pthread_t tid;
int sock_conn;
struct sockaddr_in client_addr;
socklen_t len;
while(1)
{
//第四步:接受客户端连接请求
//accept函数的返回值为一个连接套接字,专门用于对应的连接上和客户端进行通信
//调用accept函数时如果没有客户端连接请求到来,它将阻塞当前线程的执行,直到接收到一个客户端连接请求或者出错才返回
len = sizeof(client_addr);
sock_conn = accept(sock_listen, (struct sockaddr*)&client_addr, &len);
if(sock_conn == -1)
{
perror("accept");
continue;
}
printf("%s:%d已经连接!\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
//设置recv超时时间为300ms
setsockopt(sock_conn, SOL_SOCKET, SO_RCVTIMEO, &rcv_timeout, sizeof(rcv_timeout));
if(0 != pthread_create(&tid, NULL, srv_thr, (void*)sock_conn))
{
perror("pthread_create");
close(sock_conn);
}
}
//第七步:关闭监听套接字
close(sock_listen);
return 0;
}
void* srv_thr(void* arg)
{
pthread_detach(pthread_self());
int sock_conn = (int)arg;
send_file(sock_conn, file_path);
close(sock_conn);
return NULL;
}
//发送文件
void send_file(int sock_conn, const char* path)
{
//获取文件名和所处路径
const char* file_name = NULL;
char file_path[300] = "./";
file_name = strrchr(path, '/');
if(file_name == NULL)
{
file_name = path;
}
else
{
file_name++;
strncpy(file_path, path, file_name - path);
}
//将当前工作目录设置为要发送的文件所在的目录
chdir(file_path);
//判断文件类型
struct stat st;
stat(path, &st);
if(S_ISDIR(st.st_mode))
{
//文件夹
send_dir(sock_conn, file_name);
}
else
{
//非文件夹
send_regfile(sock_conn, file_name);
}
}
//发送普通文件
int send_regfile(int sock_conn, const char* path)
{
//获取文件相对路径
struct file_info fi = {"", 0};
strcpy(fi.name, path);
//获取文件大小
struct stat st;
stat(path, &st);
fi.size = st.st_size;
send(sock_conn, &fi, sizeof(fi), 0);
char msg[1024];
int ret;
FILE* fp =NULL;
fp = fopen(path, "rb");
if(fp == NULL) return 1;
while(!feof(fp))
{
ret = fread(msg, 1, sizeof(msg), fp);
if(send(sock_conn, msg, ret, 0) < 0)
break;
}
fclose(fp);
ret = recv(sock_conn, msg, sizeof(msg)-1, 0);
if(ret > 0)
{
msg[ret] = '\0';
if(strcmp(msg, "ok") == 0)
{
//printf("发送文件成功!\n");
return 0;
}
else if(strcmp(msg, "err") == 0)
{
//printf("发送文件失败!\n");
return 1;
}
else
{
//printf("非法客户端!\n");
return 2;
}
}
else
{
return 3;
}
}
//发送文件夹
int send_dir(int sock_conn, const char* path)
{
struct file_info fi = {"", -1};
int ret;
char reply[10];
int send_flag = 0;
strcpy(fi.name, path);
send(sock_conn, &fi, sizeof(fi), 0);
ret = recv(sock_conn, reply, sizeof(reply) - 1, 0);
if(ret > 0)
{
reply[ret] = '\0';
if(strcmp(reply, "ok") == 0)
{
send_flag = 1;
}
}
if(send_flag != 1) return 1;
DIR* dirp = opendir(path);
struct dirent* de = NULL;
char file_path[300];
struct stat st;
while(de = readdir(dirp))
{
if(strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
continue;
sprintf(file_path, "%s/%s", path, de->d_name);
stat(file_path, &st);
if(S_ISDIR(st.st_mode))
{
//文件夹
if(send_dir(sock_conn, file_path) != 0) return 1;
}
else
{
//非文件夹
if(send_regfile(sock_conn, file_path) != 0) return 1;
}
}
closedir(dirp);
return 0;
}
客户端
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/stat.h>
#include <unistd.h>
#pragma pack(1)//设置为1字节对齐
struct file_info
{
char name[300];
int size;
};
#pragma pack()
int rcv_file(int socket_conn);
int main(int argc,char** argv)
{
if(argc<3 || argc>4)
{
printf("参数有误");
exit(-1);
}
if(argc==4)//到指定目录创建文件夹
{
if(-1==access(argv[3],W_OK))//判断指定目录是否存在
{
perror("当前目录不可写");
exit(-1);
}
if(-1==chdir(argv[3]))
{
perror("设置当前工作目录失败");
return -1;
}
}
int socket_conn=socket(AF_INET,SOCK_STREAM,0);
struct sockaddr_in ser_addr;
ser_addr.sin_family=AF_INET;
ser_addr.sin_addr.s_addr=inet_addr(argv[1]);
ser_addr.sin_port=htons(atoi(argv[2]));
if(-1==connect(socket_conn,(struct sockaddr*)&ser_addr,sizeof(ser_addr)))
{
perror("connect");
exit(-1);
}
//设置接收超时为1秒
struct timeval tv;
tv.tv_sec=1;
tv.tv_usec=0;
setsockopt(socket_conn,SOL_SOCKET,SO_RCVTIMEO,&tv,sizeof(tv));
int i;
while(1)
{
i=rcv_file(socket_conn);
if(i==0)
{
printf("接收成功\n");
break;
}
}
close(socket_conn);
return 0;
}
int rcv_file(int socket_conn)
{
struct file_info fi = {"",0};
int ret;
char buf[1024];
char name[100];
struct stat s;
ret=recv(socket_conn,&fi,sizeof(fi),0);
if(ret==0)//对方断开连接
{
return 0;
}
if(ret!=sizeof(fi))
{
fprintf(stderr," 接收失败\n");
return -1;
}
if(fi.size==-1)//文件夹
{
mkdir(fi.name,0777);
send(socket_conn,"ok",2,0);
}
else if(fi.size>=0)//普通文件
{
FILE* fp=fopen(fi.name,"wb");
int recv_cnt=0;
while(recv_cnt<fi.size)//根据文件大小,判断什么时候接收文件
{
ret=recv(socket_conn,buf,sizeof(buf),0);
if(ret<=0)
{
perror("recv");
break;
}
recv_cnt+=ret;
fwrite(&buf,ret,1,fp);
}
fclose(fp);
if(recv_cnt==fi.size)
{
send(socket_conn,"ok",2,0);
}
else
{
send(socket_conn,"err",3,0);
}
}
else
{
fprintf(stderr,"server err\n");
return -1;
}
}