文件传输系统
项目介绍:创建一个文件传输系统,实现客户端在服务器上下载或者上传文件。 项目描述:本项目中聊天采用 TCP 的通信方式,在客户端上对服务器的指定目录可以进行ls命令,并且可以下载服务器指定目录的内容,并且支持断点续传的功能,用户也可以给服务器上传文件。
服务器端配置文件info.cnf
#ip addr
ips=127.0.0.1
#ser port
port=6000
#listen(),
lismax=5
设计一个数据结构保存服务器socket初始化数据。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define ips_max 32
struct info_data
{
char ips[ips_max];
int port;
int lismax;
};
int read_info_cnf(struct info_data * p);
#include "sock_init.h"
int read_info_cnf(struct info_data * p)
{
if ( NULL == p )
{
return -1;
}
FILE * fp = fopen("info.cnf","r");
if ( NULL == fp )
{
return -1;
}
while( 1 )
{
char buff[128] = {0};
fgets(buff,128,fp);
if ( buff[0] == '\0' )
{
break;
}
if ( buff[0] == '#' || buff[0] == '\n' )
{
continue;
}
buff[strlen(buff)-1] = '\0';
if ( strncmp(buff,"ips=",4) == 0 )
{
strcpy(p->ips,buff + 4);
continue;
}
else if ( strncmp(buff,"port=",5) == 0 )
{
p->port = atoi(buff + 5);
continue;
}
else if ( strncmp(buff,"lismax=",7) == 0 )
{
p->lismax = atoi(buff+7);
continue;
}
else
{
printf("info.cnf err:%s",buff);
}
}
fclose(fp);
return 0;
}
服务器设计如下
#include "thread.h"
#include "sock_init.h"
int create_socket(struct info_data *p);
int accept_client(int sockfd);
int main()
{
struct info_data dt;
read_info_cnf(&dt);
int sockfd = create_socket(&dt);
if ( -1 == sockfd )
{
printf("create sockfd err\n");
exit(0);
}
while( 1 )
{
int c = accept_client(sockfd);
if ( -1 == c )
{
continue;
}
start_thread(c);
}
}
int accept_client(int sockfd)
{
if ( -1 == sockfd )
{
return -1;
}
struct sockaddr_in caddr;
int len = sizeof(caddr);
int c = accept(sockfd,(struct sockaddr*)&caddr,&len);
return c;
}
int create_socket(struct info_data *p)
{
int sockfd = socket(AF_INET,SOCK_STREAM,0);
if ( -1 == sockfd )
{
return -1;
}
struct sockaddr_in saddr;
memset(&saddr,0,sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_port = htons(p->port);
saddr.sin_addr.s_addr = inet_addr(p->ips);
int res = bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
if ( -1 == res )
{
return -1;
}
if ( listen(sockfd,p->lismax) == -1 )
{
return -1;
}
return sockfd;
}
线程函数设计如下:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <sys/wait.h>
#include<fcntl.h>
#include<signal.h>
int falg=1;
void start_thread(int c);
利用lseek函数,信号的应用。因为网络的send()和recv()相当于管道的读写端,服务器再给客户端send的过程中如果客户端异常退出。服务器会接收的SIGPIPE的信号使服务器关闭,所以下面代码中修改该信号。
#include "thread.h"
#define ARG_MAX 10
void recv_file(int c, char* name)
{
if (c == -1 || name == NULL)
{
return;
}
int fd = open(name, O_CREAT | O_WRONLY, 0600);
if (fd == -1)
{
send(c, "err", 3, 0);
return;
}
send(c,"ok#",3,0);
char status[128] = { 0 };
int curr_size = 0;
int num = 0;
if (recv(c, status, 127, 0) <= 0)
{
return;
}
if (strncmp(status, "ok#", 3) != 0)
{
return;
}
int filesize = atoi(status + 3);
char data[1024] = { 0 };
while(1)
{
num = recv(c, data, 1024, 0);
write(fd, data, num);
curr_size += num;
float f = curr_size * 100.0 / filesize;
printf("%s:%.2f%%\r",name, f);
fflush(stdout);
if (curr_size >= filesize)
{
break;
}
}
send(c, "ok#", 3, 0);
printf("\n");
close(fd);
}
void fun()
{
falg=0;
}
void fun1()
{
falg=1;
}
void send_file(int c, char* name)
{
if (name == NULL)
{
send(c, "err", 3, 0);
return;
}
int fd = open(name,O_RDONLY);
if (fd == -1)
{
send(c, "err", 3, 0);
return;
}
int filesize = lseek(fd, 0, SEEK_END);
lseek(fd, 0, SEEK_SET);
char status[32] = { 0 };
sprintf(status, "ok#%d", filesize);
send(c, status, strlen(status), 0);
memset(status, 0, 32);
int num = recv(c, status, 31, 0);
if (num <= 0)
{
printf("客户端异常退出");
close(fd);
return;
}
if (strcmp(status, "err") == 0)
{
close(fd);
return;
}
int size= atoi(status + 3);
lseek(fd, size, SEEK_SET);
char data[1024];
num = 0;
fun1();
while ((num = read(fd, data, 1024)) > 0)
{
signal(SIGPIPE,fun);
if(falg)
{
send(c, data, num, 0);
}
else
{
break;
}
}
close(fd);
}
char *get_cmd(char buff[], char *myargv[])
{
if (buff == NULL || myargv == NULL)
{
return NULL;
}
int i = 0;
char *ptr = NULL;
char *s = strtok_r(buff, " ", &ptr);
while (s != NULL)
{
myargv[i++] = s;
s = strtok_r(NULL, " ", &ptr);
}
return myargv[0];
}
int run_cmd(int c, char *cmd, char *myargv[])
{
int fd[2] = {0};
if (pipe(fd) == -1)
{
printf("pipe err\n");
send(c, "er1", 3, 0);
return -1;
}
pid_t pid = fork();
if (pid == -1)
{
printf("fork err\n");
send(c, "er1", 3, 0);
return -1;
}
if (pid == 0)
{
close(fd[0]);
dup2(fd[1], 1);
dup2(fd[1], 2);
execvp(cmd, myargv);
printf("cmd not find");
exit(0);
}
close(fd[1]);
wait(NULL);
char send_buff[1024] = {"ok#"};
int num = read(fd[0], send_buff + 3, 1020);
send(c, send_buff, num + 3, 0);
close(fd[0]);
}
void *work_thread(void *arg)
{
int c = (int)arg;
while (1)
{
char buff[128] = {0};
int n = recv(c, buff, 127, 0);
if (n <= 0)
{
break;
}
char *myargv[ARG_MAX] = {0};
char *cmd = get_cmd(buff, myargv);
if (strcmp(cmd, "get") == 0)
{
send_file(c, myargv[1]);
}
else if (strcmp(cmd, "up") == 0)
{
recv_file(c,myargv[1]);
}
else
{
if ( run_cmd(c,cmd,myargv) == -1 )
{
continue;
}
}
}
close(c);
printf("client close\n");
}
void start_thread(int c)
{
pthread_t id;
pthread_create(&id, NULL, work_thread, (void *)c);
}
客户端设计如下:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include<fcntl.h>
void send_file(int c, char* name, char* s)
{
if (c == -1 || s == NULL || strlen(s) == 0 || name == NULL)
{
return;
}
int fd = open(name,O_RDONLY);
if (fd == -1)
{
return;
}
int filesize = lseek(fd, 0, SEEK_END);
lseek(fd, 0, SEEK_SET);
if (filesize==0)
{
close(fd);
return;
}
if (fd == -1)
{
return;
}
send(c, s, strlen(s), 0);
char status[128] = { 0 };
if (recv(c, status, 127, 0) <= 0)
{
printf("服务器出错\n");
return;
}
if (strncmp(status, "ok#", 3) != 0)
{
return;
}
memset(status, 0, 128);
sprintf(status, "ok#%d", filesize);
send(c, status, strlen(status), 0);
memset(status, 0, 128);
printf("上传的文件大小:%d\n",filesize);
int curr_size = 0;
int num = 0;
char data[1024] = { 0 };
while(1)
{
num = read(fd, data, 1024);
send(c, data, num, 0);
curr_size += num;
float f = curr_size * 100.0 / filesize;
printf("up%s:%.2f%%\r",name, f);
fflush(stdout);
if (curr_size >= filesize)
{
break;
}
}
printf("\n");
if (recv(c, status, 127, 0) <= 0)
{
printf("服务器出错\n");
return;
}
if (strncmp(status, "ok#", 3) == 0)
{
printf("上传成功\n");
return;
}
close(fd);
}
void recv_file(int c, char* name, char* s)
{
if (c == -1 || s == NULL || strlen(s) == 0 || name == NULL)
{
return;
}
send(c, s, strlen(s), 0);
char status[128] = { 0 };
if (recv(c, status, 127, 0) <= 0)
{
printf("服务器出错\n");
return;
}
if (strncmp(status, "ok#", 3) != 0)
{
printf("文件不存在或者没有权限\n");
return;
}
int filesize = atoi(status + 3);
printf("下载的文件大小:%d\n",filesize);
char name1[20];
strcpy(name1,name);
strcat(name, ".tmp");
int fd = open(name, O_CREAT | O_WRONLY, 0600);
int size = lseek(fd, 0, SEEK_END);
lseek(fd, size, SEEK_SET);
if (filesize==0)
{
send(c, "err", 3, 0);
close(fd);
return;
}
if (fd == -1)
{
send(c, "err", 3, 0);
return;
}
memset(status, 0, 128);
sprintf(status, "ok#%d", size);
send(c,status, strlen(status), 0);
int curr_size = 0+size;
int num = 0;
char data[1024] = { 0 };
while(1)
{
num = recv(c, data, 1024, 0);
write(fd, data, num);
curr_size += num;
float f = curr_size * 100.0 / filesize;
printf("down%s:%.2f%%\r",name, f);
fflush(stdout);
if (curr_size >= filesize)
{
break;
}
}
rename(name, name1);
printf("\n");
close(fd);
}
int connect_ser(char* ips, int port)
{
if ( ips == NULL || port <= 0 )
{
return -1;
}
int sockfd = socket(AF_INET,SOCK_STREAM,0);
if ( sockfd == -1 )
{
return -1;
}
struct sockaddr_in saddr;
memset(&saddr,0,sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_port = htons(port);
saddr.sin_addr.s_addr = inet_addr(ips);
int res = connect(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
if ( res == -1 )
{
return -1;
}
return sockfd;
}
void get_arg(int argc, char* argv[], char** s, int* p)
{
if ( argc <= 1 || argv == NULL || s == NULL || p == NULL)
{
return;
}
for( int i = 1; i < argc; i++ )
{
if ( strncmp(argv[i],"ip=",3) == 0)
{
*s = argv[i] + 3;
continue;
}
if ( strncmp(argv[i],"port=",5) == 0)
{
*p = atoi(argv[i]+5);
continue;
}
}
}
char* get_cmd(char buff[],char* myargv[])
{
if ( buff == NULL || myargv == NULL)
{
return NULL;
}
char * s = strtok(buff," ");
int i = 0;
while( s != NULL )
{
myargv[i++] = s;
s = strtok(NULL," ");
}
return myargv[0];
}
int main(int argc, char* argv[])
{
char* ips = "127.0.0.1";
int port = 6000;
get_arg(argc,argv,&ips,&port);
int sockfd = connect_ser(ips,port);
if ( sockfd == -1 )
{
printf("connect to server failed :%s ,port:%d\n",ips, port);
exit(0);
}
while( 1 )
{
char buff[128] = {0};
printf("connect: %s >> ",ips);
fflush(stdout);
fgets(buff,128,stdin);
buff[strlen(buff) - 1] = 0;
char cmd_buff[128] = {0};
strcpy(cmd_buff,buff);
char* myargv[10] = {0};
char* cmd = get_cmd(buff,myargv);
if ( strcmp(cmd,"get") == 0 )
{
recv_file(sockfd, myargv[1], cmd_buff);
}
else if ( strcmp(cmd,"up") == 0)
{
send_file(sockfd,myargv[1],cmd_buff);
}
else if ( strcmp(cmd,"end") == 0)
{
break;
}
else
{
char recv_buff[1024] = {0};
send(sockfd,cmd_buff,strlen(cmd_buff),0);
int num = recv(sockfd,recv_buff,1023,0);
if ( num <= 0 )
{
printf("ser close\n");
break;
}
if ( strncmp(recv_buff,"ok#",3) == 0)
{
printf("%s\n",recv_buff+3);
}
else
{
printf("执行命失败\n");
}
}
}
close(sockfd);
}
总结
了解到lseek命令,了解到管道的读写端的异常输出信号,熟悉到TCP编程的流程。
|