声明:
这篇文章是作为我记录代码之用,记录的代码是一个半成品,只能用作微小文件的传输。我将代码放在这里,一是为分享,二是为保留到之后有时间的时候去完善它,加深我对文件传输的理解。
对解决方法的思考(未实践):
用当前代码传输大文件时,接收方得到了一个同名文件,但大小比源文件小,且内容有乱码。因此推断,传输时,缓冲区可能出现了溢出,于是丢失了部分数据。最终导致接收到的文件的实际容量比源文件小。根据这一观点,我设想:在传输时,将一个大文件,以1024字节为单位拆分,传输前告知接收端要传送的文件的大小,每次只传输1024个字节,并且为每个1024字节的数据包设定一个包序号,发送端得到第n个包的收到回复再发第n+1个包,若为收到,则持续等待。若长时间未收到接受者的收到回复,则重发第n个包。发送端发送结束后,给接受者发送一个信号,提醒客户端“我这边已经发完了!”,接受者接到后,则停止接收数据,并对比收到的文件大小是否正确,正确则传输结束,不符,则删除本地文件(测试时先不做此操作,以便分析问题),然后输出错误提示。
一、服务端部分
服务端的代码分为三个部分:线程池代码——pthread_pool.c,服务端代码——my_server.c和my_server.h,把前面两部分综合起来的代码——main_server.c
main_server.c:
#include "my_server.h"
pthread_rwlock_t rwlock;//读写锁
int main()
{
//初始化
if(0 != pthread_rwlock_init(&rwlock,NULL))
{
perror("rwlock init failed ...");
return -1;
}
P_PPSI p_ppsi = Pthread_Pool_Init();
if(0 != sock_init(&p_ppsi->cmd_sock_fd) )
{
perror("sock init failed...");
return -1;
}
//循环创建连接服务线程
Enter_Add_Pthread_Task(p_ppsi);
close(p_ppsi->cmd_sock_fd);
Pthread_Pool_Free(p_ppsi);
return 0;
}
my_server.h:
#include "my_server.h"
extern pthread_rwlock_t rwlock;//读写锁
int show_list(int stdout_fd ,char *sys_cmd, char * stdout_filename, int client_fd, char * pathname)
{
char stdout_buf[1024] = "\0";//标准输出缓冲
//执行查询当前路径的简洁列表,并将输出结果定向到文件中
memset(sys_cmd,0,100);
sprintf(sys_cmd, "ls %s >%s 2>&1",pathname,stdout_filename);
// printf("%s\n",pathname);
// printf("stdout_filename:%s\n",stdout_filename);
system(sys_cmd);
//将输出结果发送到客户端
lseek(stdout_fd, 0, SEEK_SET);
read(stdout_fd, stdout_buf, sizeof(stdout_buf));
write(client_fd, stdout_buf, strlen(stdout_buf));
return 0;
}
int change_dir(char * buf, char * pathname,int client_fd)
{
memset(pathname, 0, sizeof(pathname));
strcpy(pathname, buf+3);
write(client_fd,pathname,strlen(pathname));
}
//提供下载服务时,需要上锁保护读写操作
/*
接到下载命令后,先查询该文件是否存在
若不存在,则发送“file_not_exist”,并结束当前函数
若存在,则上读锁,并发送TX_ready信号,准备发送文件
准备发送文件时,接收到客户端发送RX_ready信号,则开始循环发送数据,收到RX_cancel信号,则取消发送,结束任务
文件发送结束后则发送TX_end信号
发送出错,则发送send_err信号
*/
int download(char *sys_cmd, char * buf, char * pathname,int client_fd, int stdout_fd,char* stdout_filename)
{
//检查文件是否存在
// lseek(client_fd,8,SEEK_SET);
// read(client_fd,buf,100);
char tmp[100] = "\0";
snprintf(sys_cmd,256,"find %s -type f -name \"%s\" > %s 2>&1",pathname, buf+9, stdout_filename);
system(sys_cmd);
lseek(stdout_fd,0,SEEK_SET);
int ret = read(stdout_fd,tmp,100);
printf("ret = %d tmp:%s\n",ret,tmp);
if( ret == 0 || strstr(tmp,"No such file or directory") != NULL || strstr(tmp,"没有那个文件或目录") != NULL)
{
//文件不存在
write(client_fd,"file_err",strlen("file_err"));
return -1;
}
else
{
//文件存在
if(0 == pthread_rwlock_rdlock(&rwlock))
{
int src_fd = open(buf+9,O_RDWR);
printf("%s\n",buf+9);
if(src_fd <0)
{
perror("打开目标文件失败!");
write(client_fd,"file_err",strlen("file_err"));
return -1;
}
int snd_size = write(client_fd,"TX_ready",strlen("TX_ready"));
printf("snd size = %d\n",snd_size);
//usleep(1000);
//发送文件
// while(1);
memset(tmp,0,sizeof(tmp));
read(client_fd,tmp,100);
printf("客户端:%s\n",tmp);
//客户端取消接收准备
if(tmp == "RX_cancel")
{
close(src_fd);
pthread_rwlock_unlock(&rwlock);
printf("客户端取消接收\n");
return -1;
}
//发送数据
// int ret = 0;
while(1)
{
memset(buf,0,100);
ret = read(src_fd,buf,100);
// printf("%s\n",buf);
if( ret > 0)
{
write(client_fd,buf,100);
usleep(1000);
}
else if(ret == 0)
{
//发送结束
printf("文件发送完毕!\n");
printf("pathname:%s\n",pathname);
memset(buf,0,100);
strcpy(buf,"TX_end");
write(client_fd,buf,sizeof(buf));
sleep(1);
close(src_fd);
pthread_rwlock_unlock(&rwlock);
return 0;
}
else
{
// perror("发送出错!");
printf("发送出错!\n");
close(src_fd);
pthread_rwlock_unlock(&rwlock);
return -1;
}
}
}
}
}
//提供上传服务时,需要上锁保护读写操作
/*
接到上传命令后,先查询该文件是否存在
若不存在,则上写锁创建文件并发送“RX_ready”信号,等待到“TX_ready”信号,开始循环读取写入
若存在,发送“file_aready_exist”信号,结束任务
准备接收文件时,若收到TX_cancel信号,则取消接收,结束任务
接收数据时,若收到“TX_end”信号,则接收结束,解写锁,任务结束
接收出错,则发送rcv_err信号,删除接收文件,结束任务
*/
int upload(char *sys_cmd, char * buf, char * pathname,int client_fd, int stdout_fd,char* stdout_filename)
{
//准备工作
char tmp_buf[1024] = "\0";
char obj_pathname[100] = "\0";
sprintf(obj_pathname,"%s/%s",pathname,buf+7);
//查询本地是否存在同名文件
sprintf(sys_cmd, "> %s", stdout_filename);
system(sys_cmd);
memset(sys_cmd,0,100);
sprintf(sys_cmd,"find %s>%s 2>&1",obj_pathname,stdout_filename);
printf("文件查询命令:%s\n",sys_cmd);
system(sys_cmd);
usleep(200);
memset(sys_cmd,0,100);
lseek(stdout_fd,0,SEEK_SET);
int ret = read(stdout_fd,tmp_buf,sizeof(tmp_buf));
printf("查询结果:ret = %d tmp_buf = %s\n",ret,tmp_buf);
if((strstr(tmp_buf,"没有那个文件或目录") != NULL) || (strstr(tmp_buf,"No such file or directory") != NULL))
{
//不存在同名文件,创建文件,上写锁,发送RX_ready,准备接收文件
if(0 > pthread_rwlock_trywrlock(&rwlock))
{
write(client_fd,"file_err",strlen("file_err"));
}
printf("111\n");
int obj_fd = open(obj_pathname,O_RDWR|O_CREAT,777);
if(obj_fd <0)
{
perror("目标文件创建失败!");
write(client_fd,"file_err",strlen("file_err"));
pthread_rwlock_unlock(&rwlock);
return -1;
}
//准备接收
write(client_fd,"RX_ready",strlen("RX_ready"));
memset(tmp_buf,0, sizeof(tmp_buf));
ret = read(client_fd,tmp_buf,sizeof(tmp_buf));
printf("RX_ready的返回信息:ret = %d tmp_buf = %s\n",ret, tmp_buf);
if(strcmp(tmp_buf,"TX_ready") == 0)
{
//开始接收数据
while(1)
{
memset(tmp_buf,0, sizeof(tmp_buf));
ret = read(client_fd,tmp_buf,sizeof(tmp_buf));
// printf("返回信息:ret = %d tmp_buf = %s\n",ret, tmp_buf);
//发方出错
if(strcmp(tmp_buf,"send_err") == 0)
{
//关闭并删除目标文件
close(obj_fd);
sprintf(tmp_buf,"rm %s", obj_pathname);
system(tmp_buf);
pthread_rwlock_unlock(&rwlock);
return 0;
}else if(strcmp(tmp_buf,"TX_end") == 0)
{
//接收成功
close(obj_fd);
printf("接收成功!\n");
pthread_rwlock_unlock(&rwlock);
return 0;
}
else
{
//读取并写入本地
write(obj_fd,tmp_buf,strlen(tmp_buf));
}
}
}
else if(strcmp(tmp_buf,"TX_cancel") == 0)
{
//取消传输
printf("客户端取消发送!\n");
pthread_rwlock_unlock(&rwlock);
return 0;
}
printf("%s\n",tmp_buf);
close(obj_fd);
pthread_rwlock_unlock(&rwlock);
return 0;
}
else
{
write(client_fd,"file_err",strlen("file_err"));
printf("111\n");
return 0;
//存在同名文件,发送“file_aready_exist”,任务结束
}
}
int getinfo(char *sys_cmd, char * buf, char * pathname,int client_fd, int stdout_fd,char* stdout_filename)
{
char stdout_buf[128] = "\0";//标准输出缓冲
sprintf(sys_cmd, "ls %s/%s -l>%s 2>&1",pathname,buf+8,stdout_filename);
system(sys_cmd);
// while(1);
// usleep(500);
lseek(stdout_fd, 0, SEEK_SET);
if(0 >= read(stdout_fd, stdout_buf, sizeof(stdout_buf)))
{
printf("读取失败!\n");
while(1);
}
printf("stdout_buf:%s\n",stdout_buf);
write(client_fd, stdout_buf, strlen(stdout_buf));
printf("1\n");
return 0;
}
int show_path(int stdout_fd ,int client_fd,char * pathname)
{
char buf[100];
sprintf(buf,"%s\n",pathname);
write(client_fd,buf,strlen(buf));
// char stdout_buf[1024] = "\0";//标准输出缓冲
// write(stdout_fd,pathname,strlen(pathname));
// lseek(stdout_fd, 0, SEEK_SET);
// read(stdout_fd, stdout_buf, 1024);
// write(client_fd, stdout_buf, strlen(stdout_buf));
return 0;
}
int exct_client_cmd(int stdout_fd ,char *sys_cmd, char * buf,int buf_len, char * stdout_filename, int client_fd,char * pathname)
{
char tmp[100] = "\0";
if(0 == strncmp(buf, "ls", 2))
{
show_list(stdout_fd ,sys_cmd, stdout_filename, client_fd, pathname);
}
else if(0 == strncmp(buf, "cd", 2))
{
//查询路径是否存在
sprintf(tmp,"find %s -type d >%s 2>&1",pathname,stdout_filename);
system(tmp);
read(stdout_fd,tmp,100);
if(strstr(tmp,"没有那个文件或目录")||strstr(tmp,"No such file or directory"))
{
write(client_fd, "目录不存在",strlen("目录不存在"));
return -1;
}
//更改路径
change_dir(buf, pathname, client_fd);
}
else if(0 == strncmp(buf, "download", 8))
{
download(sys_cmd, buf, pathname, client_fd, stdout_fd, stdout_filename);
}
else if(0 == strncmp(buf, "upload", 6))
{
upload(sys_cmd, buf, pathname, client_fd, stdout_fd, stdout_filename);
}
else if(0 == strncmp(buf, "getinfo", 7))
{
getinfo(sys_cmd,buf, pathname, client_fd, stdout_fd, stdout_filename);
}
else if(0 == strncmp(buf, "pwd", 3))
{
printf("execute pwd\n");
show_path(stdout_fd, client_fd, pathname);
}
// else if(0 == strncmp(buf, "quit", 4))
// {
// printf("服务退出 \n");
// return 0;
// }
// memset(sys_cmd, 0, sizeof(sys_cmd));
// sprintf(sys_cmd, "%s >%s", buf, stdout_filename);//将命令输出结果重定向到指定文件中
// system(sys_cmd);//将客户端发过来的路径、目录相关命令直接执行
// //如果命令为pwd,则记录新路径
// if(0 == strncmp(sys_cmd, "pwd",3))
// {
// lseek(cmd_sock_fd, 0, SEEK_SET);
// read(stdout_fd, pathname, 100);
// printf("路径更新:%s\n",pathname);
// }
return 0;
}
/*服务线程回调函数
* 根据客户端命令提供相应服务
*
*
*/
void * server_fun(void *arg)
{
// pthread_attr_setdetachstate(pthread_self(), NULL);
int client_fd = (int)arg;
printf("新客户端连接号:%d\n",client_fd);
char sys_cmd[256] = "\0";//系统命令字符串
char stdout_filename[100] = "\0";//标准输出定向文件名
char buf[1024] = "\0";
sprintf(stdout_filename, "stdout%d.txt", client_fd);
int stdout_fd = open(stdout_filename,O_RDWR, O_CREAT | 777);
//初始化路径记录
char pathname[256] = "\0";
sprintf(sys_cmd, "> %s", stdout_filename);
system(sys_cmd);
memset(sys_cmd,0,sizeof(sys_cmd));
sprintf(sys_cmd, "pwd> %s", stdout_filename);
system(sys_cmd);
memset(sys_cmd,0,sizeof(sys_cmd));
read(stdout_fd,pathname,sizeof(pathname));
strcpy(strstr(pathname,"\n"), "\0");
// printf("pathname:%s\n",pathname);
// printf("断点!\n");
//运行主体
while(1)
{
memset(buf, 0, 1024);
if(read(client_fd, buf, 1024) > 0)
{
printf("%d号客户端发来命令:%s\n",client_fd,buf);
}
//清空定向输出文件的内容
memset(sys_cmd,0,sizeof(sys_cmd));
sprintf(sys_cmd, "> %s", stdout_filename);
// printf("sys_cmd:%s stdout_filename:%s\n",sys_cmd,stdout_filename);
system(sys_cmd);
memset(sys_cmd,0,100);
//当前路径输出到文件
// sprintf(sys_cmd, "pwd > %s", stdout_filename);
// system(sys_cmd);
if(strncmp(buf,"quit",4) == 0)
{
printf("服务退出!\n");
close(client_fd);
return 0;
}
//执行客户端发来的命令
exct_client_cmd(stdout_fd ,sys_cmd, buf,1024, stdout_filename, client_fd,pathname);
//设置文件描述符标志位,使其读写不阻塞
// int flags = fcntl(stdout_fd, F_GETFL, 0); //通过设置文件描述符的属性,来设置read函数非阻塞
// if (flags < 0)
// {
// perror("获取文件描述符标志位失败");
// }
// fcntl(stdout_fd, F_SETFL, flags | O_NONBLOCK);
//读取输出流,发送给客户端
usleep(1000);
}
}
int sock_init(int *cmd_socket_fd)
{
struct sockaddr_in netinfo;//ipv4网络信息结构体
//1、申请套接字
*cmd_socket_fd = socket(AF_INET, SOCK_STREAM, 0);
// *data_socket_fd = socket(AF_INET, SOCK_STREAM, 0);
if(*cmd_socket_fd == -1 )
{
perror("socket failed...");
return -1;
}
printf("网络套接字申请成功cmd_socket_fd:[%d]\n",*cmd_socket_fd);
//2、绑定套接字
netinfo.sin_family = AF_INET;
netinfo.sin_port = htons(56666);
netinfo.sin_addr.s_addr = htonl(INADDR_ANY);
if( -1 == bind(*cmd_socket_fd, ( struct sockaddr * )&netinfo, sizeof(netinfo)))//cmd
{
perror("bind failed...");
return -2;
}
printf("绑定套接字成功!\n");
//3、监听套接字
listen(*cmd_socket_fd,5);
return 0;
}
my_server.h:
#ifndef _MY_SERVER_H_
#define _MY_SERVER_H_
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netinet/in.h>
#include <pthread.h>
#include <fcntl.h>
#define PTHREAD_MAX_NUM 3000
#define EXIT_MARK 'Q'
//命令套接字
// int cmd_sock_fd;
// pthread_rwlock_t rwlock;//读写锁
/***************线程池相关******************/
//结构体类型声明
//线程任务链表结构体类型
typedef struct task_link_node
{
void * (*Pthread_Task)(void *arg);
int client_fd;
struct task_link_node * prev;
struct task_link_node * next;
}TASK_NODE, * TASK_LINK;
//线程池状态信息结构体
typedef struct pthread_pool_inf
{
pthread_t TID[PTHREAD_MAX_NUM];
pthread_mutex_t mut;//互斥锁变量
pthread_cond_t con;//条件变量
pthread_t real_time_show_id;//实时监控线程池中线程的状态的线程的id
TASK_LINK task_link_head;//任务链表头结点
int task_num;//当前所剩任务数
int pthread_sum;//线程总数
int pthread_running_num;//活跃状态线程总数
int pthread_sleeping_num;//睡眠状态线程总数
int pthread_working_num;//执行任务的线程总数
int cmd_sock_fd;//监听套接字
int exit_mark;
}PPSI,*P_PPSI;
P_PPSI Pthread_Pool_Init();//线程池初始化
int Pthread_Pool_Free(P_PPSI p_ppsi);//释放线程池资源
void * Pthread_Ctrl(void * arg);//线程控制
TASK_LINK Create_Task_Node();//
TASK_LINK Get_Task_Node(P_PPSI p_ppsi);
int Add_Task_Node(TASK_LINK task_list_head, TASK_LINK new_task_node);
int Destory_Task_List(P_PPSI p_ppsi);
int Enter_Add_Pthread_Task(P_PPSI p_ppsi);
void * Real_Time_Show(void * arg);
void * Pthread_Task(void * arg);
/**********************服务功能相关程序************************/
//显示文件列表
int show_list(int stdout_fd ,char *sys_cmd, char * stdout_filename, int client_fd, char * pathname);
//更改访问目录
int change_dir(char * buf, char * pathname,int client_fd);
//提供下载服务时,需要上锁保护读写操作
/*
接到下载命令后,先查询该文件是否存在
若不存在,则发送“file_not_exist”,并结束当前函数
若存在,则上读锁,并发送TX_ready信号,准备发送文件
准备发送文件时,接收到客户端发送RX_ready信号,则开始循环发送数据,收到RX_cancel信号,则取消发送,结束任务
文件发送结束后则发送TX_end信号
发送出错,则发送send_err信号
*/
int download(char *sys_cmd, char * buf, char * pathname,int client_fd, int stdout_fd,char* stdout_filename);
//提供上传服务时,需要上锁保护读写操作
/*
接到上传命令后,先查询该文件是否存在
若不存在,则上写锁创建文件并发送“RX_ready”信号,等待到“TX_ready”信号,开始循环读取写入
若存在,发送“file_aready_exist”信号,结束任务
准备接收文件时,若收到TX_cancel信号,则取消接收,结束任务
接收数据时,若收到“TX_end”信号,则接收结束,解写锁,任务结束
接收出错,则发送rcv_err信号,删除接收文件,结束任务
*/
int upload(char *sys_cmd, char * buf, char * pathname,int client_fd, int stdout_fd,char* stdout_filename);
//获取文件详细
int getinfo(char *sys_cmd, char * buf, char * pathname,int client_fd, int stdout_fd,char* stdout_filename);
//展示当前访问路径
int show_path(int stdout_fd ,int client_fd,char * pathname);
//执行客户端指令
int exct_client_cmd(int stdout_fd ,char *sys_cmd, char * buf,int buf_len, char * stdout_filename, int client_fd,char * pathname);
//服务程序
void * server_fun(void *arg);
//套接字初始化
int sock_init(int *cmd_socket_fd);
#endif
pthread_pool.c:
#include "my_server.h"
P_PPSI Pthread_Pool_Init()
{
//临时扩大栈空间
// system("ulimit -s 204800");
P_PPSI p_ppsi = (P_PPSI)malloc(sizeof(PPSI));
if (p_ppsi == (P_PPSI)NULL)
{
perror("初始化失败!");
return (P_PPSI)-1;
}
memset(p_ppsi, 0, sizeof(PPSI));
if (pthread_mutex_init(&p_ppsi->mut, NULL) != 0)
{
perror("pthread_mutex_init...");
return (P_PPSI)-1;
}
if (pthread_cond_init(&p_ppsi->con, NULL) != 0)
{
perror("pthread_cond_init...");
return (P_PPSI)-1;
}
//创建链表头
p_ppsi->task_link_head = Create_Task_Node();
if(p_ppsi->task_link_head == (TASK_LINK)-1)
{
printf("创建任务链表头结点失败!\n");
return (P_PPSI)-1;
}
p_ppsi->pthread_running_num = 0;
p_ppsi->pthread_sleeping_num = 0;
p_ppsi->pthread_working_num = 0;
p_ppsi->task_num = 0;
p_ppsi->pthread_sum = 0;
p_ppsi->exit_mark = 1;
for (int lp = 0; lp < PTHREAD_MAX_NUM; lp++)
{
if (pthread_create(&p_ppsi->TID[lp], NULL, Pthread_Ctrl, p_ppsi) != 0)
{
printf("线程数:%d\n",lp);
perror("pthread_create...");
return (P_PPSI)-1;
}
//printf("断点\n");
//上锁
pthread_mutex_lock(&p_ppsi->mut);
p_ppsi->pthread_sum++;
p_ppsi->pthread_running_num++;
//解锁
pthread_mutex_unlock(&p_ppsi->mut);
}
if(pthread_create(&p_ppsi->real_time_show_id, NULL, Real_Time_Show, p_ppsi) != 0)
{
perror("pthread_create...");
return (P_PPSI)-1;
}
return p_ppsi;
}
TASK_LINK Create_Task_Node()
{
TASK_LINK task_node = (TASK_LINK)malloc(sizeof(TASK_NODE));
if(task_node == (TASK_LINK)NULL)
{
perror("malloc failed ...");
return (TASK_LINK)-1;
}
memset(task_node, 0, sizeof(TASK_NODE));
task_node->next = task_node;
task_node->prev = task_node;
return task_node;
}
void * Real_Time_Show(void * arg)
{
P_PPSI p_ppsi = (P_PPSI)arg;
while(p_ppsi->exit_mark)
{
// printf("断点!\n");
sleep(5);
printf("线程总数:%d ---- 任务数:%d ---- 正在执行:%d ---- 活跃:%d ---- 睡眠:%d \n",
p_ppsi->pthread_sum,
p_ppsi->task_num,
p_ppsi->pthread_working_num,
p_ppsi->pthread_running_num,
p_ppsi->pthread_sleeping_num
);
}
}
void * Pthread_Ctrl(void * arg)
{
P_PPSI p_ppsi = (P_PPSI)arg;
while(p_ppsi->exit_mark)
{
//上锁
pthread_mutex_lock(&p_ppsi->mut);
//判断链表是否为空
if(p_ppsi->task_num == 0)
{
p_ppsi->pthread_sleeping_num++;
p_ppsi->pthread_running_num--;
pthread_cond_wait(&p_ppsi->con, &p_ppsi->mut);
p_ppsi->pthread_sleeping_num--;
p_ppsi->pthread_running_num++;
pthread_mutex_unlock(&p_ppsi->mut);
}
else
{
TASK_LINK task_node = Get_Task_Node(p_ppsi);
p_ppsi->task_num--;
p_ppsi->pthread_working_num++;
pthread_mutex_unlock(&p_ppsi->mut);
//执行任务
printf("执行任务\n");
task_node->Pthread_Task(task_node->client_fd);
printf("执行完毕\n");
pthread_mutex_unlock(&p_ppsi->mut);
pthread_mutex_lock(&p_ppsi->mut);//上锁
p_ppsi->pthread_working_num--;
free(task_node);
printf("释放任务节点\n");
pthread_mutex_unlock(&p_ppsi->mut);
printf("任务完成回到等待阶段\n");
}
}
pthread_exit(0);
}
TASK_LINK Get_Task_Node(P_PPSI p_ppsi)//头删获取节点
{
TASK_LINK del_node = p_ppsi->task_link_head->next;
del_node->next->prev = del_node->prev;
del_node->prev->next = del_node->next;
del_node->next = NULL;
del_node->prev = NULL;
return del_node;
}
//循环添加任务节点
int Enter_Add_Pthread_Task(P_PPSI p_ppsi)
{
int client_fd = 0;
struct sockaddr_in clientinfo;
socklen_t addrlen = sizeof(clientinfo);
while(1)
{
// char mark = getchar();
// if(mark == EXIT_MARK)
// {
// break;
// }
pthread_mutex_lock(&p_ppsi->mut);
//创建新的任务节点
TASK_LINK new_task_node = Create_Task_Node();
if(new_task_node == (TASK_LINK)-1)
{
perror("创建新任务节点失败!");
return -1;
}
client_fd = accept(p_ppsi->cmd_sock_fd, (struct sockaddr * ) &clientinfo, &addrlen);
if(client_fd == -1)
{
perror("accept failed...");
while (1);
}
printf("有新客户端连接,客户端IP:%s , 端口号: %d\n",inet_ntoa(clientinfo.sin_addr),
ntohs(clientinfo.sin_port));
new_task_node->Pthread_Task = server_fun;
new_task_node->client_fd = client_fd;
Add_Task_Node(p_ppsi->task_link_head, new_task_node);
p_ppsi->task_num++;
pthread_mutex_unlock(&p_ppsi->mut);
usleep(2000);
if(pthread_cond_broadcast(&p_ppsi->con) != 0)
{
perror("pthread_cond_boradcast failed ...");
return -1;
}
}
return 0;
}
int Add_Task_Node(TASK_LINK task_link_head, TASK_LINK new_task_node)
{
if(task_link_head == (TASK_LINK)NULL)
{
printf("任务头结点异常!\n");
return -1;
}
new_task_node->next = task_link_head->next;
task_link_head->next->prev = new_task_node;
new_task_node->prev = task_link_head;
task_link_head->next = new_task_node;
return 0;
}
int Pthread_Pool_Free(P_PPSI p_ppsi)
{
pthread_mutex_lock(&p_ppsi->mut);
p_ppsi->exit_mark = 0;
pthread_mutex_unlock(&p_ppsi->mut);
pthread_cond_broadcast(&p_ppsi->con);
for(int lp=0; lp<PTHREAD_MAX_NUM; lp++)
{
if(pthread_join(p_ppsi->TID[lp], NULL) != 0)
{
perror("pthread_join failed ...");
}
pthread_mutex_lock(&p_ppsi->mut);
p_ppsi->pthread_sum--;
pthread_mutex_unlock(&p_ppsi->mut);
}
//摧毁链表
Destory_Task_List(p_ppsi);
//摧毁条件变量
pthread_mutex_destroy(&p_ppsi->mut);
pthread_cond_destroy(&p_ppsi->con);
pthread_join(p_ppsi->real_time_show_id, NULL);
free(p_ppsi);
return 0;
}
int Destory_Task_List(P_PPSI p_ppsi)
{
if(p_ppsi->task_link_head == NULL)
{
printf("链表头结点异常!\n");
return -1;
}
else if(p_ppsi->task_num == 0)
{
free(p_ppsi->task_link_head);
}
else
{
for(int lp=0; lp<p_ppsi->task_num; lp++)
{
TASK_LINK del_node = Get_Task_Node(p_ppsi);
free(del_node);
p_ppsi->task_num--;
}
free(p_ppsi->task_link_head);
}
return 0;
}
二、客户端部分
这部分,分为两部分:第一部分——main_client.c,第二部分——my_client.c和my_client.h。看名字应该能看懂各自的作用。
main_client.c:
#include "my_client.h"
int main(void)
{
//初始化
int cmd_sock_fd;
// int data_sock_fd;
char *cmd_list[] = {"ls", "download", "upload", "getinfo", "quit", "pwd"};
char cmd_buf[1024];
char param_buf[100];
char data_buf[1024];
struct sockaddr_in ser_cmd_addr;
if(-1 == sock_init(&cmd_sock_fd))
{
perror("sock init failed ...");
return -1;
}
//连接服务器
ser_cmd_addr.sin_addr.s_addr = inet_addr("192.168.173.41");//inet_addr将一个表示ip地址的字符串转换成uint32_t格式
ser_cmd_addr.sin_family = AF_INET;
ser_cmd_addr.sin_port = htons(56666);
if(-1 == connect(cmd_sock_fd, (struct sockaddr *)&ser_cmd_addr, sizeof(ser_cmd_addr)))
{
perror("connect failed...");
return -1;
}
else
{
printf("已连接服务器!\n");
}
while(1)
{
//清屏
system("clear");
//打印当前路径位置
printf("当前路径:");
memset(cmd_buf,0,sizeof(cmd_buf));
memset(param_buf,0,sizeof(param_buf));
strcpy(cmd_buf, "pwd");
exct_cmd(cmd_sock_fd,cmd_buf,param_buf);
//打印当前文件列表
printf("-------------------文件列表-----------------------\n");
memset(cmd_buf,0,sizeof(cmd_buf));
strcpy(cmd_buf,"ls");
exct_cmd(cmd_sock_fd,cmd_buf,param_buf);
//打印命令提示
printf("-----------------命令提示列表-------------------\n ls 查询当前路径文件列表\n cd 切换路径\n download 下载文件\n upload 上传文件\n getinfo 获取文件详细信息(大小、类型、日期)\n quit 退出客户端\n");
printf("--------------------------------------------------\n");
//接收键盘输入的命令
memset(cmd_buf,0,sizeof(cmd_buf));
memset(param_buf,0,sizeof(param_buf));
printf("请输入命令:");
scanf("%s",cmd_buf);
exct_cmd(cmd_sock_fd,cmd_buf,param_buf);
sleep(1);
}
return 0;
}
my_client.h:
#ifndef _MY_CLIENT_H_
#define _MY_CLIENT_H_
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netinet/in.h>
#include <pthread.h>
#include <fcntl.h>
#define DEFAULT_DOWNLOAD_PATH "/download"
int sock_init(int * cmd_sock_fd);
//发送命令并显示结果
int send_cmd(int cmd_sock_fd, char *cmd_buf, char *param_buf);
//显示当前路径
int show_dir(int cmd_sock_fd,char * cmd_buf, char * param_buf);
//显示文件列表
int show_file(int cmd_sock_fd, char *cmd_buf, char *param_buf);
//切换路径
int change_dir(int cmd_sock_fd, char *cmd_buf, char *param_buf);
//下载
/*
向服务器发送download和文件名
服务端查询文件
文件存在:则收到传输开始信号——TX_ready,客户端选择路径创建文件,
建文件时若发现文件已存在则询问是否覆盖原文件,若不覆盖原文件则取消下载
创建好文件后,发送接收准备信号RX_ready,循环读取数据存放在本地文件中,当接收到TX_end
则接收结束,完成后关闭文件描述符
文件不存在:则收到错误信号file_not_exist,函数结束,返回等待命令阶段
发送出错:send_err信号
*/
int download(int cmd_sock_fd, char *cmd_buf,int cmd_buf_len , char *param_buf);
//上传
int upload(int cmd_sock_fd, char *cmd_buf,int cmd_buf_len, char *param_buf);
//获取文件信息
int getinfo(int cmd_sock_fd, char *cmd_buf, char *param_buf);
//客户端退出,发出quit信号,并释放资源
int quit(int cmd_sock_fd);
//解析并执行命令
int exct_cmd(int cmd_sock_fd, char *cmd_buf, char *param_buf);
#endif
my_client.c:
#include "my_client.h"
char server_path[100] = "\0";
int sock_init(int * cmd_sock_fd)
{
struct sockaddr_in client_info;//ipv4网络信息结构体
// struct sockaddr_in server_info;//ipv4服务端信息结构体
//1、申请套接字
*cmd_sock_fd = socket(AF_INET, SOCK_STREAM, 0);
// *data_sock_fd = socket(AF_INET, SOCK_STREAM, 0);
if(*cmd_sock_fd == -1 )
{
perror("socket failed...");
return -1;
}
printf("网络套接字申请成功cmd_socket_fd:[%d]\n",*cmd_sock_fd);
// printf("网络套接字申请成功cmd_socket_fd:[%d],data_socket_fd:[%d]\n",*cmd_sock_fd,*data_sock_fd);
//2、绑定套接字
//cmd
client_info.sin_family = AF_INET;
client_info.sin_port = htons(55555);//55555用于cmd
client_info.sin_addr.s_addr = htonl(INADDR_ANY);
if( -1 == bind(*cmd_sock_fd, ( struct sockaddr * )&client_info, sizeof(client_info)))
{
perror("bind failed...");
return -2;
}
//data
// client_info.sin_port = htons(55556);//55556用于data
// if( -1 == bind(*data_sock_fd, ( struct sockaddr * )&client_info, sizeof(client_info)))
// {
// perror("bind failed...");
// return -2;
// }
printf("绑定套接字成功!\n");
//3、监听套接字
// listen(*data_sock_fd,5);
// listen(*data_socket_fd,5);
return 0;
}
//发送命令并显示结果
int send_cmd(int cmd_sock_fd, char *cmd_buf, char *param_buf)
{
//发送命令
// printf("已接收到的命令:%s %s\n",cmd_buf,param_buf);
sprintf(cmd_buf,"%s %s",cmd_buf, param_buf);//拼接命令和参数
if( -1 == write(cmd_sock_fd, cmd_buf, strlen(cmd_buf)))
{
perror("send cmd failed...");
}
usleep(50);
//设置文件描述符标志位,使其读写不阻塞
// int flags = fcntl(cmd_sock_fd, F_GETFL, 0); //通过设置文件描述符的属性,来设置read函数非阻塞
// if (flags < 0)
// {
// perror("获取文件描述符标志位失败");
// }
// fcntl(cmd_sock_fd, F_SETFL, flags | O_NONBLOCK);
memset(cmd_buf,0,1024);
read(cmd_sock_fd, cmd_buf, 1024);
printf("%s",cmd_buf);
fflush(stdout);//刷新缓冲区
// memset(cmd_buf,0,sizeof(cmd_buf));
// memset(param_buf,0,sizeof(param_buf));
}
//显示当前路径
int show_dir(int cmd_sock_fd,char * cmd_buf, char * param_buf)
{
send_cmd(cmd_sock_fd,cmd_buf,param_buf);
memset(server_path, 0, sizeof(server_path));
strcpy(server_path, cmd_buf);
}
//显示文件列表
int show_file(int cmd_sock_fd, char *cmd_buf, char *param_buf)
{
send_cmd(cmd_sock_fd,cmd_buf,param_buf);
}
//切换路径
int change_dir(int cmd_sock_fd, char *cmd_buf, char *param_buf)
{
printf("请输入目标路径(绝对路径):\n");
scanf("%s",param_buf);
send_cmd(cmd_sock_fd,cmd_buf,param_buf);
if(strstr(cmd_buf,"目录不存在") == NULL)
{
memset(server_path, 0, sizeof(server_path));
strcpy(server_path, cmd_buf);
}
//切换路径后打印当前路径
// memset(cmd_buf,0,sizeof(cmd_buf));
// memset(param_buf,0,sizeof(param_buf));
// strcpy(cmd_buf,"pwd");
// send_cmd(cmd_sock_fd, cmd_buf, param_buf);
}
//下载
/*
向服务器发送download和文件名
服务端查询文件
文件存在:则收到传输开始信号——TX_ready,客户端选择路径创建文件,
建文件时若发现文件已存在则询问是否覆盖原文件,若不覆盖原文件则取消下载
创建好文件后,发送接收准备信号RX_ready,循环读取数据存放在本地文件中,当接收到TX_end
则接收结束,完成后关闭文件描述符
文件不存在:则收到错误信号file_not_exist,函数结束,返回等待命令阶段
发送出错:send_err信号
*/
int download(int cmd_sock_fd, char *cmd_buf,int cmd_buf_len , char *param_buf)
{
//发送下载命令
printf("请输入目标文件:");
scanf("%s",param_buf);
sprintf(cmd_buf,"%s %s",cmd_buf, param_buf);//拼接命令和参数
if( -1 == write(cmd_sock_fd, cmd_buf, strlen(cmd_buf)))
{
perror("send cmd failed...");
}
usleep(50);
//准备下载
memset(cmd_buf,0,cmd_buf_len);
int ret = read(cmd_sock_fd,cmd_buf,cmd_buf_len);
printf("ret = %d,cmd_buf:%s\n", ret, cmd_buf);
if(strcmp(cmd_buf , "file_err") == 0)
{
printf("读取文件异常,回到等待命令阶段\n");
return -1;
}
else if(strcmp(cmd_buf, "TX_ready") == 0)
{
printf("服务端传输就绪!\n");
//检查本地是否有同名文件
int obj_fd = 0;
char tmp_buf[1024]= "\0";
int tmp_buf_len = 1024;
char tmp_stdout_filename[100] =DEFAULT_DOWNLOAD_PATH;
char tmp_download_path[100] = DEFAULT_DOWNLOAD_PATH;
char tmp_download_filename[100] = "\0";
umask(0000);
while(1)
{
//是否下载在/download路径下?[y/n]
printf("是否下载在默认路径?(默认路径:/download)[y/n]\n");
scanf("%s",tmp_buf);
if(strcmp(tmp_buf,"y") == 0)
{
//是:更改下载路径 是否使用源文件的文件名?[y/n]
while(1)
{
//是否使用源文件的文件名?[y/n]
memset(tmp_buf,0,sizeof(tmp_buf));
printf("是否使用下载文件的原文件名?[y/n]\n");
scanf("%s", tmp_buf);
if(strcmp(tmp_buf,"y") == 0)
{
//y:
memset(tmp_buf,0,sizeof(tmp_buf));
strcpy(tmp_download_filename,param_buf);
sprintf(tmp_buf,"%s/%s",tmp_download_path,param_buf);
obj_fd = open(tmp_buf,O_RDWR|O_CREAT|O_EXCL, 777);
}
else if(strcmp(tmp_buf,"n") == 0)
{
//n:
memset(tmp_buf,0,sizeof(tmp_buf));
printf("自定义文件名:");
scanf("%s",tmp_download_filename);
sprintf(tmp_buf,"%s/%s",tmp_download_path,tmp_download_filename);
obj_fd = open(tmp_buf,O_RDWR|O_CREAT|O_EXCL, 777);
}
else
{
//错误输入
printf("输入错误!请重新输入!\n");
}
if (obj_fd < 0 )
{
perror("指定目录不存在,或已存在同名文件,请重新输入路径和文件名");
break;
}
else if(obj_fd >0)
{
break;
}
}
}
else if(strcmp(tmp_buf, "n") == 0)
{
//更改下载路径
while(1)
{
memset(tmp_buf,0,sizeof(tmp_buf));
printf("下载路径:");
scanf("%s",tmp_buf);
strcpy(tmp_download_path,tmp_buf);
while(1)
{
//是否使用源文件的文件名?[y/n]
memset(tmp_buf,0,sizeof(tmp_buf));
printf("是否使用下载文件的原文件名?[y/n]\n");
scanf("%s", tmp_buf);
if(strcmp(tmp_buf,"y") == 0)
{
//y:
memset(tmp_buf,0,sizeof(tmp_buf));
strcpy(tmp_download_filename,param_buf);
sprintf(tmp_buf,"%s/%s",tmp_download_path,param_buf);
obj_fd = open(tmp_buf,O_RDWR|O_CREAT|O_EXCL, 777);
}
else if(strcmp(tmp_buf,"n") == 0)
{
//n:
printf("自定义文件名:");
scanf("%s",tmp_download_filename);
memset(tmp_buf,0,sizeof(tmp_buf));
sprintf(tmp_buf,"%s/%s",tmp_download_path,tmp_download_filename);
printf("pathname:%s\n",tmp_buf);
obj_fd = open(tmp_buf,O_RDWR|O_CREAT|O_EXCL, 777);
}
else
{
//错误输入
printf("输入错误!请重新输入!\n");
}
if (obj_fd < 0 )
{
perror("指定目录不存在,或已存在同名文件,请重新输入路径和文件名");
break;
}
else if(obj_fd >0)
{
break;
}
}
if(obj_fd > 0)
{
break;
}
}
}
else
{
printf("输入错误请重试!\n");
sleep(1);
}
if(obj_fd > 0)
{
break;
}
}
printf("开始接收数据。。。\n");
//准备接收
// obj_fd = open(param_buf,O_RDWR|O_CREAT,777);
write(cmd_sock_fd,"RX_ready",strlen("RX_ready"));
memset(tmp_buf,0,tmp_buf_len);
//开始下载
while(1)
{
if((read(cmd_sock_fd,tmp_buf,100) > 0) && (strcmp(tmp_buf,"TX_end") != 0) && (strcmp(tmp_buf, "send_err") != 0))
{
// printf("获取文件字符串:%s",tmp_buf);
write(obj_fd,tmp_buf,strlen(tmp_buf));
memset(tmp_buf,0,tmp_buf_len);
}
else if(strcmp(tmp_buf,"TX_end") == 0)
{
//接收over
printf("下载完毕!\n");
close(obj_fd);
// sprintf(tmp_buf,"rm %s",tmp_stdout_filename);
// system(tmp_buf);
return 0;
}
else if(strcmp(tmp_buf, "send_err") == 0)
{
//发方出错
close(obj_fd);
memset(tmp_buf,0,tmp_buf_len);
sprintf(tmp_buf,"rm %s",tmp_download_filename);
system(tmp_buf);
// memset(tmp_buf,0,tmp_buf_len);
// sprintf(tmp_buf,"rm %s",tmp_stdout_filename);
// system(tmp_buf);
return -1;
}
else
{
//接收出错
close(obj_fd);
memset(tmp_buf,0,tmp_buf_len);
sprintf(tmp_buf,"rm %s",tmp_download_filename);
system(tmp_buf);
// memset(tmp_buf,0,tmp_buf_len);
// sprintf(tmp_buf,"rm %s",tmp_stdout_filename);
// system(tmp_buf);
return -1;
}
}
}
memset(cmd_buf, 0, cmd_buf_len);
strcpy(cmd_buf, "ls");
sprintf(param_buf, "%s/%s -l",server_path,param_buf);
send_cmd(cmd_sock_fd,cmd_buf,param_buf);
}
//上传
int upload(int cmd_sock_fd, char *cmd_buf,int cmd_buf_len, char *param_buf)
{
//准备工作
char tmp_buf[100] = "\0";
//发送上传命令
printf("请输入要上传的文件:");
scanf("%s",param_buf);
sprintf(cmd_buf,"%s %s",cmd_buf, param_buf);//拼接命令和参数
printf("上传命令:%s\n",cmd_buf);
if( -1 == write(cmd_sock_fd, cmd_buf, strlen(cmd_buf)))
{
perror("send cmd failed...");
}
//确认是否存在同名文件
memset(cmd_buf,0, cmd_buf_len);
read(cmd_sock_fd,cmd_buf,cmd_buf_len);
printf("cmd_buf :%s\n",cmd_buf);
if(strcmp(cmd_buf, "RX_ready") == 0)
{
//不存在同名文件
int src_fd = open(param_buf,O_RDONLY);
if(src_fd < 0)
{
perror("源文件打开失败!取消传输!");
write(cmd_sock_fd,"TX_cancel",strlen("TX_cancel"));
return -1;
}
//发送“TX_ready”信号
write(cmd_sock_fd,"TX_ready",strlen("TX_ready"));
printf("服务器准备接收数据!\n");
while (1)
{
//发送数据
memset(tmp_buf,0,sizeof(tmp_buf));
int ret = read(src_fd, tmp_buf,sizeof(tmp_buf));
// printf("读取内容:ret = %d tmp_buf = %s\n",ret, tmp_buf);
if(ret <0)
{
//传输出错
perror("读取失败,取消传输!");
close(src_fd);
write(cmd_sock_fd,"send_err",strlen("send_err"));
usleep(1000);
return -1;
}else if(ret == 0)
{
//传输完成
printf("传输完成!发送TX_end信号!\n");
write(cmd_sock_fd, "TX_end", strlen("TX_end"));
close(src_fd);
usleep(1000);
return 0;
}
// printf("读取源文件:ret = %d tmp_buf = %s\n",ret , tmp_buf);
ret = write(cmd_sock_fd,tmp_buf,100);
// printf("写入字节数:%d 内容:%s\n",ret, tmp_buf);
usleep(1000);
}
}
else if(strcmp(cmd_buf, "file_err") == 0)
{
//存在同名文件
printf("存在同名文件,或写入文件失败,无法上传!\n");
return -1;
}
}
//获取文件信息
int getinfo(int cmd_sock_fd, char *cmd_buf, char *param_buf)
{
printf("请输入目标文件:");
scanf("%s",param_buf);
send_cmd(cmd_sock_fd,cmd_buf,param_buf);
sleep(6);
}
//客户端退出,发出quit信号,并释放资源
int quit(int cmd_sock_fd)
{
write(cmd_sock_fd,"quit",sizeof("quit"));
sleep(1);
close(cmd_sock_fd);
exit(0);
}
//解析并执行命令
int exct_cmd(int cmd_sock_fd, char *cmd_buf, char *param_buf)
{
if(0 == strcmp(cmd_buf, "pwd"))
{
// printf("%s\n",cmd_buf);
show_dir(cmd_sock_fd,cmd_buf, param_buf);
return 0;
}
else if(0 == strcmp(cmd_buf, "ls"))
{
show_file(cmd_sock_fd,cmd_buf, param_buf);
return 0;
}
else if(0 == strcmp(cmd_buf, "cd"))
{
// printf("%s\n",cmd_buf);
change_dir(cmd_sock_fd,cmd_buf, param_buf);
return 0;
}
else if(0 == strcmp(cmd_buf, "download"))
{
download( cmd_sock_fd, cmd_buf,100, param_buf);
return 0;
}
else if(0 == strcmp(cmd_buf, "upload"))
{
upload(cmd_sock_fd, cmd_buf,100, param_buf);
return 0;
}
else if(0 == strcmp(cmd_buf, "getinfo"))
{
getinfo(cmd_sock_fd,cmd_buf, param_buf);
return 0;
}
else if(0 == strcmp(cmd_buf, "quit"))
{
quit(cmd_sock_fd);
}
else
{
printf("输入命令有误!\n");
return -1;
}
return 0;
}