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,并且内容和客户端根目录下的文件相同,传 输成功
来源:CSDN
作者:_Hello Spring
链接:https://blog.csdn.net/wct3344142/article/details/103803827