利用 TCP 完成文件传输的设计

利用 TCP 完成文件传输的设计

实验目的

输入文件路径,利用 TCP 实现客户文件向服务器的传输,并实现对 TCP 的基本封装

实验原理

1.服务器

(1)建立 socket
(2)确定服务器 scokaddr_in 结构体
(3)点分十进制 IP 转换
(4)使用 bind 绑定套接字
(5)使用 listen 监听
(6)使用 accept 接受连接请求
(7)accept 返回新的套接字描述符
(8)使用 recv 接收传来的数据(文件路径)
(9)打开文件,这里需要文件名
(10)从该字符串获取文件名
(11)使用 recv 接收文件内容
(12)判断 recv 函数返回的状态
(13)将接收到的内容放入缓冲区
(14)将缓冲区内容写入文件
(15)关闭文件
(16)关闭 socket

# 2.客户端

(1)建立 socket
(2)确定服务器 scokaddr_in 结构体
(3)点分十进制 IP 转换
(4)使用 connect 连接
(5)打开文件
(6)准备缓冲区
(7)缓冲区初始化置空
(8)将文件内容读入缓冲区
(9)使用 send 将缓冲区内容发送到服务器
(10)文件内容发送完成
(11)关闭文件
(12)关闭 socket

实验步骤

1.服务器

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>

#define PORT 1111 // 设置端口号
#define LISTENQ 10 // 设置监听队列长度
#define BUFFSIZE 1024 // 设置缓冲区大小
#define FILE_BUFFSIZE 100 // 设置文件缓冲区大小
#define END_FLAG "end" // 结束标记

int passiveTCP() {//封装 tcp 的建立
	// 创建套接字,使用流数据格式
	int serv_fd = socket(AF_INET, SOCK_STREAM, 0); 
	// 定义一个地址结构体变量
	struct sockaddr_in serv_addr;
	// 清零地址
	memset(&serv_addr, 0, sizeof(serv_addr));
	// 给地址结构体设置值
	serv_addr.sin_family = AF_INET; // Ipv4
	serv_addr.sin_port = htons(PORT); // 指定端口号
	serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); // 指定任意ip
	bind(serv_fd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)); // 套接字绑定地址结构
	listen(serv_fd, LISTENQ); // 设置为监听模式
	return serv_fd; // 返回套接字
}
int main() {
	int serv_fd = passiveTCP(); // 创建套接字并绑定端口和ip
	char filename[FILE_BUFFSIZE];
	char buffer[BUFFSIZE];
	int cnt = 0;

	while (1) {
		//将文件接收到服务器根目录
		int clie_fd;
		// 定义一个新的地址结构变量
		struct sockaddr_in clie_addr;
		// 清零地址结构
		memset(&clie_addr, 0, sizeof(clie_addr));

		int len = sizeof(clie_addr);
		// 处理连接请求, 返回一个新的套接字,用户客户端通信
		printf("初始化成功\n");
		if ((clie_fd = accept(serv_fd, (struct sockaddr *)&clie_addr, &len)) < 0) { 
			printf("error\n");
		}
		else {
			char buff_t[BUFFSIZE] = { 0 }; //做中间临时保存
			cnt ++;
			printf("===connect success %d===\n", cnt);
			// 处理考虑有多个文件的情况
			while(1){
				memset(filename, 0, FILE_BUFFSIZE);
				memset(buffer, 0, BUFFSIZE);
				recv(clie_fd, filename, sizeof(filename), 0);//接收文件名
				
				if( strcmp(filename, END_FLAG) == 0 || strcmp(filename, "") == 0){
					break; // 文件全部发送完毕
				}
			
				printf("#> %s\n", filename);
				// 对文件名进行处理
				int t = (int)(strchr(filename, '.') - filename); // 计算'.'的下标
				// 判断文件有无后缀
				if( t < 0 )
				{
					strcat(filename, "_副本");
					printf("#> save filename: [%s]\n", filename);
				}
				else{
					// 将后缀保存
					strcpy(buff_t, filename + t);
					printf("#> %s\n", buff_t);
					strcpy(filename + t, "_副本");
					printf("#> %s\n", filename);
					strcat(filename, buff_t);
					printf("#> save filename: [%s]\n", filename);
				}
				FILE *fp = fopen(filename, "w");//创建文件
				printf("transport start\n");
				int n;
				// 接收文件内容,并写入打开的文件中,直到文件结尾
				while ((n = recv(clie_fd, buffer, BUFFSIZE, 0)) > 0) { 
					//printf("%d: %s\n", strlen(buffer), buffer);
					if( strcmp(buffer, END_FLAG) == 0){
						break;
					}
					fwrite(buffer, sizeof(char), n, fp);
					// buffer 清零
					memset(buffer, 0, BUFFSIZE);
				}
				printf("transport finish\n");
				printf("_______________________\n");
				// 关闭文件
				fclose(fp);
			}
			// 关闭连接新建套接字
			close(clie_fd);
			printf("===connect close %d===\n", cnt);
		}
	}
	// 关闭服务器套接字
	close(serv_fd); 
	
	return 0;
}

2.客户端

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>

#define PORT 1111 // 设置端口号
#define IP "127.0.0.1"  // 设置ip
#define BUFFSIZE 1024  // 设置缓冲区大小
#define FILE_BUFFSIZE 100
#define END_FLAG "end"

int main(int argc, char *argv[]) {
	// 创建套接字,采用 流格式套接字 SOCK_STREAM
	int clie_fd = socket(AF_INET, SOCK_STREAM, 0);
	struct sockaddr_in clie_addr; 
	// 地址结构清零
	memset(&clie_addr, 0, sizeof(clie_fd));

	// 地址结构赋值
	clie_addr.sin_family = AF_INET; 
	clie_addr.sin_port = htons(PORT);
	clie_addr.sin_addr.s_addr = inet_addr(IP);// 指定ip
	//inet_pton(AF_INET, IP, &(addr.sin_addr.s_addr)); 
	//
	char filename[FILE_BUFFSIZE];
	char buffer[BUFFSIZE];

	// 将 filename、buffer 清零
	memset(filename, 0, sizeof(filename));
	memset(buffer, 0, sizeof(buffer));
	printf("=====================\n");
	if (connect(clie_fd, (struct sockaddr*)&clie_addr, sizeof(clie_addr)) < 0) { 
		printf("error\n");
	}
	else {
		int cnt = 0;
		char file_name_t[FILE_BUFFSIZE][FILE_BUFFSIZE];
		printf("===connect success===\n");
		if( argc > 1 ){// 表示参数来自程序运行的参数
			int i = 0;
			for( i = 1;i < argc; i ++)
			{
				strcpy(file_name_t[cnt ++], argv[i]);
			}
		}
		//输入要传送的文件名,文件确保在根目录下
		else{
			printf("input filename:"); 
			fgets(filename, FILE_BUFFSIZE, stdin); // 输入遇到回车结束
			// 将字符串最后的'\n'去掉
			filename[strlen(filename) - 1] = '\0';
			// 分割字符串
			char *p = strtok(filename, " ");
			while(p){
				//printf("&> %s\n", p);
				strcpy(file_name_t[cnt ++], p);
				p = strtok(NULL, " ");
			}
		}
		for(int i = 0; i < cnt; i ++){
			if( strcmp(file_name_t[i], "") == 0 ){
				continue;
			}
			FILE* fp = fopen(file_name_t[i], "r"); // 以读的方式打开文件 
			if (fp == NULL) {
				printf("%s :file not exist\n", file_name_t[i]);
				printf("$> %s\n\n", END_FLAG);
				continue;
			}
			else {
				//先传送文件名,后传送文件内容 
				printf("transport start\n");
				send(clie_fd, file_name_t[i], sizeof(file_name_t[i]), 0);
				printf("$> %s\n", file_name_t[i]);
				int n;
				// 读取文件的内容并发送,直到文件发送内容发送完毕
				while ( (n = fread(buffer, sizeof(char), BUFFSIZE, fp)) > 0 ) {
					send(clie_fd, buffer, n, 0);
				//	printf("%s\n", buffer);
					// 发送完后将buffer清零
					memset(buffer, 0, sizeof(buffer));
				}
				printf("transport finish\n");
				sleep(0.5);
				send(clie_fd, END_FLAG, sizeof(END_FLAG), 0); // 发送结束标志
				printf("$> %s\n\n", END_FLAG);
			}
			// 关闭文件
			fclose(fp);
			sleep(0.5);
		}
		send(clie_fd, END_FLAG, sizeof(END_FLAG), 0); // 发送结束标志
		printf("$> %s\n", END_FLAG);
	}
	printf("=====================\n");
	// 关闭套接字
	close(clie_fd); 

	return 0;
}

实验结果:

传输前客户端根目录下保存着我们
传输的文件 wct.txt
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
好的,您的问题是关于基于 TCP 的局域网内高性能文件传输系统设计与实现。我会尽力回答您的问题。 首先,基于 TCP 的局域网内高性能文件传输系统需要考虑以下几个方面: 1. 数据传输效率:文件传输速度需要尽可能快,系统需要支持同时传输多个文件。 2. 数据安全性:确保传输文件不被篡改或泄露。 3. 可扩展性:支持多用户同时传输文件,支持多个客户端同时连接服务器。 4. 用户友好性:提供简单易用的操作界面,让用户可以方便地上传和下载文件。 下面是一个简单的基于 TCP 的局域网内高性能文件传输系统的设计方案: 1. 系统架构 系统分为客户端和服务器端两个部分,客户端用于上传和下载文件,服务器端用于接收和发送文件。 2. 数据传输 采用 TCP 协议进行文件传输,客户端和服务器端之间通过套接字建立连接,实现可靠传输和高效传输。 3. 数据安全 文件传输过程中,可以使用 SSL/TLS 进行加密,确保数据传输的安全性。 4. 可扩展性 服务器端可以支持多个客户端同时连接,实现多用户同时上传和下载文件。 5. 用户界面 客户端提供简单易用的操作界面,让用户可以方便地上传和下载文件。 以上是一个简单的基于 TCP 的局域网内高性能文件传输系统的设计方案,具体实现还需要考虑很多细节问题,例如文件的分块传输、数据校验、断点续传等等。希望这些信息能够帮助您。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_Hello Spring

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值