基于TCP的文件服务器

  1. 服务端
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#include <errno.h>

#define IP "192.168.8.101"    //服务器 IP
#define PORT 8888            //端口号

#define MAX 515

#define ERROR(msg) do{\
	fprintf(stderr, "__%d__", __LINE__);\
	perror(msg);\
}while(0)

typedef void (*sighandler_t)(int);

void handler(int sig)
{
	while(waitpid(-1, NULL, WNOHANG) > 0);
}

//处理客户端信息
void msg_deal(int cfd, char* buf, ssize_t* res);
//接收来自客户端信息
char* msg_recv(int cfd,char* buf, ssize_t* res);
//给客户端发送信息
char* msg_send(int cfd,char* buf, ssize_t* res);
//下载
int down(int cfd, char* buf, ssize_t* res);
//上传
int up(int cfd, char* buf, ssize_t* res);
//目录信息
int dir(int cfd, char* buf, ssize_t* res);


int main(int argc, char *argv[])
{
	int sfd, cfd;
	ssize_t res;
	struct sockaddr_in server_addr, client_addr;
	socklen_t client_addr_size;
	pid_t pid;

	/*if(SIG_ERR == signal(SIGCHLD,handler))
	{
		ERROR("signal");
		return -1;
	}*/
	signal(SIGCHLD, SIG_IGN);
	//1.创建流式套接字
	sfd = socket(AF_INET, SOCK_STREAM, 0);
	if (sfd < 0)
	{
		ERROR("socket");
		return -1;
	}
	memset(&server_addr, 0, sizeof(struct sockaddr_in));
	memset(&client_addr, 0, sizeof(struct sockaddr_in));
	
	//填充地址信息结构体
	server_addr.sin_family = AF_INET;
	server_addr.sin_port = htons(PORT);   //端口的网络字节序
	server_addr.sin_addr.s_addr = inet_addr(IP);  //IP的网络字节序
	
	//允许端口快速被重用
	int reuse = 1;
	if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse ,sizeof(reuse)) < 0)
	{
		ERROR("setsockopt");
		return -1;
	}

	//2.将地址信息绑定到套接字上
	if (bind(sfd, (struct sockaddr *) &server_addr,
				sizeof(struct sockaddr_in)) < 0)
	{
		ERROR("bind");
		return -1;
	}

	//3.将sfd设置为被动监听状态
	if (listen(sfd, 128) < 0)
	{
		ERROR("listen");
		return -1;
	}
	printf("服务器正在监听....\n");

	client_addr_size = sizeof(struct sockaddr_in);
	
	while(1)
	{
		//4.从队列头获取一个客户的信息
		cfd = accept(sfd, (struct sockaddr *) &client_addr,
				&client_addr_size);
		if (cfd < 0)
		{
			ERROR("accept");
			return -1;
		}
		printf("[%s:%d] 连接成功\n", inet_ntoa(client_addr.sin_addr), 
			ntohs(client_addr.sin_port));
		//printf("cfd = %d\n",cfd);
		pid = fork();
		if(pid > 0)
		{
			printf("cfd = %d\n",cfd);
			close(cfd);
		}
		else if(0 == pid)
		{
			printf("child_cfd = %d\n",cfd);
			char *buf = (char*)malloc(sizeof(char)*MAX);
			if(NULL == buf)
			{
				ERROR("malloc");
				return -1;
			}
			ssize_t res = 0;
			if(close(sfd) < 0)
			{
				ERROR("close");
				return -1;
			}
			while(1)
			{
				//接收来自客户端信息
				buf = msg_recv(cfd, buf, &res);
				//处理信息内容
				msg_deal(cfd, buf, &res);
			}
			free(buf);
		}
		else
		{
			ERROR("fork");
			return -1;
		}

	}

	if(close(sfd) < 0)
	{
		ERROR("close");
		return -1;
	}
	return 0;
}

char* msg_recv(int cfd, char* buf, ssize_t* res)
{
	bzero(buf,sizeof(char)*MAX);
	//5.接收来自客户端信息
	*res = recv(cfd, buf, sizeof(char)*MAX, 0);
	if(*res < 0)
	{
		ERROR("recv");
		return NULL;
	}

	if(0 == *res)
	{
		printf("客户端已关闭\n");
		close(cfd);
		free(buf);
		exit(0);
	}

	return buf;
}

char* msg_send(int cfd, char* buf,ssize_t* res)
{
	if(send(cfd, buf, *res, 0) < 0)
	{
		ERROR("send");
		return NULL;
	}
	return buf;
}

void msg_deal(int cfd, char* buf,ssize_t* res)
{

	if('L' == *(buf+1))
	{
		printf("这是一个查看目录中的文件名请求\n");
		*res = MAX;
		dir(cfd, buf, res);
	}
	else if('R' == *(buf+1))
	{
		printf("这是一个下载请求\n");
		down(cfd, buf, res);
	}
	else if('W' == *(buf+1))
	{
		printf("这是一个上传请求\n");
		up(cfd, buf, res);
	}
	else if('Q' == *(buf+1))
	{
		printf("这是一个客户端退出\n");
	}
	else
	{
		printf("这是一个错误请求\n");
	}

}

int down(int cfd, char* buf,ssize_t* res)
{
	//打开文件
	int fd = open(buf+2, O_RDONLY);	
	if(fd < 0)
	{
		ERROR("open");
		char buf[20] = "file is not exit!";
		msg_send(cfd, buf, res);
		msg_recv(cfd, buf, res);
		goto END;
	}
	else
	{
		while(1)
		{
			//读取文件
			if((*res = read(fd, buf, sizeof(char)*MAX)) < 0)
			{
				ERROR("read");
				return -1;
			}
			msg_send(cfd, buf, res);
			if(*res<515)
			{
				printf("发送完毕\n");
				break;
			}
			msg_recv(cfd, buf, res);
			//组织协议

		}
	}
	if(close(fd) < 0)
	{
		ERROR("close");
		return -1;
	}
END: printf("文件不存在\n");
}

int up(int cfd, char* buf,ssize_t* res)
{
	//打开文件
	int fd;
	fd = open(buf+2, O_WRONLY|O_CREAT|O_TRUNC, 0664);
	if(fd < 0)
	{
		ERROR("open");
	}
	while(1)
	{
		msg_recv(cfd, buf, res);
		if(strcmp(buf,"file is not exit!") == 0)
		{
			goto END;
		}
		//写文件
		if((*res = write(fd, buf,*res)) < 0)
		{
			ERROR("write");
			return -1;
		}
		if(*res < 515)
		{
			printf("上传完毕\n");
			break;
		}
		//组织协议

		msg_send(cfd, buf, res);
	}
	if(close(fd) < 0)
	{
		ERROR("close");
		return -1;
	}
	if(0)
	{
END: 	printf("上传的文件不存在\n");
	}
}

int dir(int cfd, char* buf,ssize_t* res)
{
	struct dirent* dirinfo;
	//打开目录
	DIR* fd;
	if(NULL == (fd = opendir("./")))
	{
		ERROR("opendir");
		return -1;
	}
	while(1)
	{
		bzero(buf, sizeof(char)*MAX);
		//读取目录
		if(NULL == (dirinfo = readdir(fd)))
		{
			if(0 == errno)
			{
				printf("目录读取完毕\n");
				break;
			}
			else
			{
				ERROR("readdir");
				break;
			}
		}
		//发送目录信息
		if('.' == dirinfo->d_name[0])
		{
			continue;
		}
		strcpy(buf+2,dirinfo->d_name);
		*res = strlen(buf+2)+2;
		msg_send(cfd, buf, res);
		msg_recv(cfd, buf, res);
	}
	strcpy((buf+2),"filename is over");
	*res = strlen(buf+2)+2;
	msg_send(cfd, buf, res);
	msg_recv(cfd, buf, res);
	//关闭目录
	if(closedir(fd) < 0)
	{
		ERROR("close");
		return -1;
	}
}

2.客户端

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#include <errno.h>

#define SERVER_IP "192.168.8.101" //服务器 IP
#define SERVER_PORT 8888             //端口号

#define MAX 515

#define ERROR(msg) do{\
	fprintf(stderr, "__%d__", __LINE__);\
	perror(msg);\
}while(0)

//打印菜单
void menu(int cfd, char* buf);
//接收来自客户端信息
char* msg_recv(int cfd,char* buf, ssize_t* res);
//给客户端发送信息
char* msg_send(int cfd,char* buf, ssize_t* res);
//下载
int down(int cfd, char* buf, ssize_t* res);
//上传
int up(int cfd, char* buf, ssize_t* res);

int main(int argc, char *argv[])
{
	int cfd;
	struct sockaddr_in server_addr;
	socklen_t server_addr_size;

	//1.创建流式套接字
	cfd = socket(AF_INET, SOCK_STREAM, 0);
	if (cfd < 0)
	{
		ERROR("socket");
		return -1;
	}
	memset(&server_addr, 0, sizeof(struct sockaddr_in));

	//填充服务器地址信息结构体
	server_addr.sin_family = AF_INET;
	server_addr.sin_port = htons(SERVER_PORT);   //端口的网络字节序
	server_addr.sin_addr.s_addr = inet_addr(SERVER_IP);  //IP的网络字节序

	//3.连接服务器
	server_addr_size = sizeof(struct sockaddr_in);
	if(connect(cfd, (struct sockaddr*)&server_addr, server_addr_size) < 0)
	{
		ERROR("connect");
		return -1;
	}
	printf("服务器连接成功\n");
	char* buf = (char*)malloc(sizeof(char)*MAX);
	ssize_t res = 0;
	while(1)
	{
		bzero(buf, sizeof(char)*MAX);
		//打印菜单
		menu(cfd, buf);
		//请求
		res = 515;

		msg_send(cfd, buf, &res);
		//下载
		if('R' == *(buf+1))
		{
			down(cfd, buf, &res);
		}
		else if('W' == *(buf+1))
		{
			up(cfd, buf, &res);
		}
		else if('L' == *(buf+1))
		{
			while(1)
			{
				msg_recv(cfd, buf,&res);
				if(0 == strcmp(buf+2,"filename is over"))
				{
					printf("目录信息打印完毕!\n");
					msg_send(cfd, buf, &res);
					break;
				}
				puts(buf+2);
				msg_send(cfd, buf, &res);
			}
		}
		else if('Q' == *(buf+1))
		{
			goto END;
		}
		else
		{
			printf("输入错误\n");
		}
	}

END:
	close(cfd);
	return 0;
}

//打印菜单
void menu(int cfd,char* buf)
{
	printf("=========TCP文件传输功能选项=========\n");
	printf("            1.列出文件目录           \n");
	printf("            2.下载文件               \n");
	printf("            3.上传文件               \n");
	printf("            4.退出                   \n");
	printf("=====================================\n");
	printf("请输入选择的功能:");
	char c = getchar();
	while(getchar() != '\n');
	*buf = '1';
	if('1' == c)
	{
		*(buf+1) = 'L';
		printf("文件目录如下:\n");
	}
	else if('2' == c)
	{
		*(buf+1) = 'R';
		printf("请输入需要下载的文件名:");
		fscanf(stdin,"%s",buf+2);
		getchar();
	}
	else if('3' == c)
	{
		*(buf+1) = 'W';
		printf("请输入需要上传的文件名:");
		fscanf(stdin,"%s",buf+2);
		getchar();
	}
	else if('4' == c)
	{
		*(buf+1) = 'Q';
		printf("正在退出......\n");
	}
	else
	{
		printf("输入错误!!!!\n");
	}
}

//接收来自服务端信息
char* msg_recv(int cfd, char* buf, ssize_t* res)
{
	bzero(buf,sizeof(char)*MAX);
	//5.接收来自客户端信息
	*res = recv(cfd, buf, sizeof(char)*MAX, 0);
	if(*res < 0)
	{
		ERROR("recv");
		return NULL;
	}

	return buf;
}
//给服务发送信息
char* msg_send(int cfd, char* buf,ssize_t* res)
{
	if(send(cfd, buf, *res, 0) < 0)
	{
		ERROR("send");
		return NULL;
	}
	return buf;
}
//上传文件
int up(int cfd, char* buf,ssize_t* res)
{
	//打开文件
	int fd = open(buf+2, O_RDONLY);
	printf("%d\n",fd);
	if(fd < 0)
	{
		ERROR("open");
		buf = "file is not exit!";
		*res = 20;
		msg_send(cfd, buf, res);
		goto CLOSE;
	}
	while(1)
	{
		//读取文件
		if((*res = read(fd, buf, sizeof(char)*MAX)) < 0)
		{
			ERROR("read");
			return -1;
		}
		msg_send(cfd, buf, res);
		if(*res<515)
		{
			printf("发送完毕\n");
			break;
		}
		msg_recv(cfd, buf, res);
		//组织协议

	}

CLOSE:	if(close(fd) < 0)
		{
			ERROR("close");
		}
}
//下载文件
int down(int cfd, char* buf,ssize_t* res)
{
	int flag = 1;
	int fd;
	char str[20] = ""; 
	strcpy(str,buf+2);
	//打开文件
	while(1)
	{
		msg_recv(cfd, buf, res);
		if(strcmp(buf,"file is not exit!") == 0)
		{
			printf("%s\n",buf);
			msg_send(cfd, buf, res);
			break;
		}
		if(flag)
		{
			fd = open(str, O_WRONLY|O_CREAT|O_TRUNC, 0664);
			printf("%d\n",fd);
			if(fd < 0)
			{
				ERROR("open");
				return -1;
			}
			flag = 0;
			*res = MAX;
		}
		//写文件
		if(write(fd, buf,*res) < 0)
		{
			ERROR("write");
			return -1;
		}
		if(*res < 515)
		{
			printf("下载完毕\n");
			break;
		}
		//组织协议

		msg_send(cfd, buf, res);
	}
	if(flag == 0)
	{
		if(close(fd) < 0)
		{
			ERROR("close");
			return -1;
		}
	}
}

3.运行结果
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值