TCP 实现跨平台文件传输

╄→гoц情女王★ 提交于 2020-01-24 07:26:51

TCP 实现跨平台文件传输

实验目的

利用 TCP 完成 linux 和 windows 平台的文件传输。

实验原理

windows 与 linux 上实现 tcp 文件传输本质上是相同的,只有一些函数调用方式不一样, 这里我们仍使用上个实验的服务器端,重点学习 windows 下套接字编写。

实验步骤

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 11111 // 设置端口号
#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.客户端

#define _CRT_SECURE_NO_WARNINGS // vs 使用scanf()
#pragma comment(lib, "ws2_32.lib")  //加载 ws2_32.dll
#include <stdio.h>
#include <stdlib.h>
#include <WinSock2.h>
#define IP "10.13.146.222"  // 指定服务器ip
#define PORT 11111      // 指定端口号
#define BUFFSIZE 1024  // 缓冲区大小
#define FILE_BUFFSIZE 100  // 文件名大小
#define END_FLAG "end"
int passiveTCP()
{
	//初始化 DLL
	WSADATA wsadata;
	//WSAStartup()函数对 Winsock DLL 进行初始化 
	if (WSAStartup(MAKEWORD(2, 0), &wsadata) == SOCKET_ERROR){
		printf("Socket initialize fail!\n");
		return -1;
	}
	else{
		printf("Socket initialize success!\n");
	}
	// //创建套接字
	SOCKET sock;
	if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == SOCKET_ERROR){
		printf("Socket create fail!\n");
		return -1;
	}
	else{
		printf("Socket create success!\n");
	}
	//绑定套接字
	struct sockaddr_in ClientAddr;
	// 每个字节都用0填充
	memset(&ClientAddr, 0, sizeof(ClientAddr));
	ClientAddr.sin_family = AF_INET;
	ClientAddr.sin_port = htons(PORT);
	ClientAddr.sin_addr.s_addr = inet_addr(IP);
	//  向服务器发起请求
	system("request_.vbs");
	if (connect(sock, (LPSOCKADDR)&ClientAddr, sizeof(ClientAddr)) == SOCKET_ERROR){
		//system("seccess_no.vbs");
		printf("Connect fail!\n");
		closesocket(sock);
		return -1;
	}
	else{
		//system("seccess_.vbs");
		printf("Connect success!\n");
	}
	return sock; // 返回套接字
}
int main()
{
	SOCKET serv_fd = passiveTCP();
	if (serv_fd == -1){
		char c;
		while (serv_fd == -1){
			printf("是否重连(y/n):");
			fflush(stdin);
			scanf("%c", &c);
			if (c == 'y'){
				serv_fd = passiveTCP();
			}
			else{
				return 0; // 连接失败,没有找到服务器
			}
		}
		
	}
	char buffer[BUFFSIZE];
	char filename[FILE_BUFFSIZE];
	memset(buffer, 0, BUFFSIZE);
	memset(filename, 0, FILE_BUFFSIZE);
	//system("file_.vbs");
	/*printf("input filename:");
	scanf("%s", filename);*/
	int cnt = 0;
	char file_name_t[FILE_BUFFSIZE][FILE_BUFFSIZE];
	printf("input filename:");
	fflush(stdin); // 清空缓冲区
	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(serv_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(serv_fd, buffer, n, 0);
				//	printf("%s\n", buffer);
				// 发送完后将buffer清零
				memset(buffer, 0, sizeof(buffer));
			}
			printf("transport finish\n");
			Sleep(10);
			send(serv_fd, END_FLAG, sizeof(END_FLAG), 0); // 发送结束标志
			printf("$> %s\n\n", END_FLAG);
		}
		// 关闭文件
		fclose(fp);
		Sleep(10);
	}
	closesocket(serv_fd);
	WSACleanup();//终止对 Winsock DLL 的使用,并释放资源,以备下一次使用
	return 0;
}

实验结果

传输前 windows 端客户端根目录下有wct.txt, speak.c,Linux 端服务器根目录下没有这些文件
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

传输完成后我们发现服务器根目录下出现了 wct_副本.txt, speak_副本.c,并且内容和客户端根目录下的文件相同,传 输成功
在这里插入图片描述

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!