Socket-udp-文件收发

这个处理后最大可接受的文件时128k。
server:

// server1.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include "pch.h"
#include <iostream>
#include<WinSock2.h>
#include<string>
#include<cstdio>
#include<cstdlib>
#pragma comment(lib,"ws2_32.lib")

#define MAX_BUFFER 1024

using namespace std;
static long int total = 0;
char* getFile_Name(char filepath[]);
int writeFile(char buffer[], FILE *fp);

int main()
{
	WORD sockVersion = MAKEWORD(2, 2);
	WSADATA wsadata;
	if (WSAStartup(sockVersion, &wsadata)) {
		printf("WSAStartup failed! \n");
		return 0;
	}
	SOCKET serverSocket = socket(AF_INET, SOCK_DGRAM, 0);
	
	if (serverSocket == INVALID_SOCKET) {
		printf("socket failed! \n");
		WSACleanup();
		return -1;
	}
	sockaddr_in saddr;
	int slen = sizeof(saddr);
	sockaddr_in caddr;
	int clen = sizeof(caddr);

	saddr.sin_family = AF_INET;
	saddr.sin_port = htons(8899);
	saddr.sin_addr.S_un.S_addr = INADDR_ANY;

	//绑定
	int nRet = bind(serverSocket, (SOCKADDR*)&saddr, slen);
	if (nRet == SOCKET_ERROR) {
		printf("bind failed! \n");
		return -2;
	}


	//存放接收文件的缓冲区
	char buffer[MAX_BUFFER];
	int iRcv;
	int isend;
	//buffer = (char*)malloc(sizeof(char)*MAX_BUFFER);
	memset(buffer, 0, MAX_BUFFER);


	//存放文件的路径和文件名字
	char filepath[100];
	char filename[100];
	char begin[] = "好的,我准备好了接收文件。";
	char end[] = "接收完成!";
	FILE *fp;
	memset(filename, 0, sizeof(filename));
	memset(filepath, 0, sizeof(filepath));
	/* 发送的包较大,超过接受者缓存导致丢包: 包超过mtu size数倍,几个大的udp包可能会超过接收者的缓冲,导致丢包。 这种情况可以设置socket接收缓冲。 */
	int nRecvBuf = 128 * 1024;//设置为128K
	setsockopt(serverSocket, SOL_SOCKET, SO_RCVBUF, (const char*)&nRecvBuf, sizeof(int));

	while (1) {
		
		cout << "=============================================Server==============================================" << endl;
		//接收对方的准备发信息前的声明
		iRcv = recvfrom(serverSocket, filepath , sizeof(filepath), 0, (SOCKADDR*)&caddr, &clen);
		if (iRcv == SOCKET_ERROR) {
			cout << "接收客户端信息出错!" << endl;
			closesocket(serverSocket);
			WSACleanup();
			Sleep(2000);
			return 0;
		}
		cout << "++client: " << filepath << endl;
		memset(filepath, 0, sizeof(filepath));

		//发送准备好的信息
		isend = sendto(serverSocket, begin, strlen(begin), 0, (SOCKADDR*)&caddr, clen);
		if (isend == SOCKET_ERROR) {
			cout << "发送信息出错!" << endl;
			closesocket(serverSocket);
			WSACleanup();
			Sleep(2000);
			return 0;
		}
		cout << "==server: " << begin << endl;

		//接收文件名的全路径
		iRcv = recvfrom(serverSocket, filepath, sizeof(filepath), 0, (SOCKADDR*)&caddr, &clen);
		if (iRcv == SOCKET_ERROR) {
			cout << "接收文件路径信息出错!" << endl;
			closesocket(serverSocket);
			WSACleanup();
			Sleep(2000);
			return 0;
		}
	    //处理文件全路径名,分离出文件名
		char *file_name = getFile_Name(filepath);
		strcpy_s(filename, file_name);
		memset(filepath, 0, sizeof(filepath));


		//以附加方式打开可读 / 写的文件。若文件不存在,则会建立该文件,如果文件存在,则写入的数据会被加到文件尾后,即文件原先的内容会被保留(原来的 EOF 符不保留)。
		fp = fopen(filename, "a+");
		if (fp != NULL) {
			cout << "准备接收文件······" << endl;
			cout << "文件内容为:" << endl;
			while (1) {
				//接收文件内容并计算接收的字节数
				iRcv = recvfrom(serverSocket, buffer, sizeof(buffer), 0, (SOCKADDR*)&caddr, &clen);
				if (iRcv == SOCKET_ERROR) {
					cout << "文件接收出错!" << endl;
					closesocket(serverSocket);
					WSACleanup();
					Sleep(2000);
					return 0;
				}
				
				//调用写文件函数,将接收内容写入文件,当读取对方发送的文件结束符时不在写入文件
				int f = writeFile(buffer,fp);
				memset(buffer, 0, MAX_BUFFER);
				if (f == 1)
					break;
			}
			//向客服端说明自己接收
			
			isend = sendto(serverSocket, end, strlen(end), 0, (SOCKADDR*)&caddr, clen);
			if (isend == SOCKET_ERROR) {
				cout << "发送信息出错!" << endl;
				closesocket(serverSocket);
				WSACleanup();
				Sleep(2000);
				return 0;
			}
			cout << "==server: " << end << endl;

		}
		else {
			cout << "打开文件失败!" << endl;
		}
		fclose(fp);
		fp = NULL;
	}
	closesocket(serverSocket);
	WSACleanup();
	return 0;
}




//用于分离出用户发送文件的文件名,并提供完整路径,生成文件存储路径
char* getFile_Name(char filepath[]) {
	//用于记录文件名字的长度
	int count = 0;
	//文件路径的原长度
	int len = strlen(filepath);
	//cout << len << endl;
	//从名字最后往前遍历,遍历到"\"时停止遍历,此时的count数为文件名字的总长度
	for (int i = len; i > 0; i--) {
		//没遇到“\"就说明文件名没有结束,执行count+1;
		if (filepath[i] != '\\') {
			count++;
		}
		//否则,说明文件名结束,再往前是子目录,停止遍历
		else {
			break;
		}
	}
	//cout << "count==" << count << endl;
	//文件名字在目录中的第一个位置
	int pos = len - count + 1;
	//cout << "pos=" << pos << endl;
	//文件名的位置
	int j = 0;
	//存文件名
	char name[20];
	//初始化
	memset(name, 0, sizeof(name));
	//开始将文件名存在数组中
	for (unsigned int i = pos; i < (strlen(filepath)); i++) {
		//cout << i << " ";
		name[j] = filepath[i];
		//cout<<j<<"="<<name[j]<<" ";
		j++;
	}
	//cout << "len_name" << strlen(name) << endl;
	//for (int i = 0; i < strlen(name); i++){
	//cout << name[i];
	//}
	//cout << endl;
	//存放用户输入的路径
	char dir[20];
	cout << "请输入您存放接收文件的绝对路径:(例如:D:\\test\\)" << endl;
	//输入接收文件的路径
	cin >> dir;
	// (int o = 0; o < strlen(dir); o++) {
	// cout << dir[o];
	//}
	//cout << endl;
	//接收文件存放的目录长度
	int dlen = strlen(dir);
	//接收文件的名字长度
	int nlen = strlen(name);
	//cout << "dlen"<< dlen << endl;
	//cout << "nlen" << nlen << endl;
	//存放接收文件的全路径
	char file_name[100];
	memset(file_name, 0, sizeof(file_name));
	int k = 0;
	//先把目录拷贝到接收文件全路径数组里
	for (k = 0; k < dlen; k++) {
		file_name[k] = dir[k];
	}
	//再把文件名字拷贝到接收文件全路径数组里
	for (int i = 0; i < nlen; i++) {
		file_name[k] = name[i];
		k++;
	}
	cout << "文件存储路径为:";
	for (unsigned int o = 0; o < (strlen(file_name)); o++) {
		cout << file_name[o];
	}
	cout << endl;
	return file_name;
}


int writeFile(char buffer[],FILE *fp ) {
	    int flag = 0;
		string data = "";
		data = buffer;
		//寻找结束符号
		int goal = data.rfind("success transport");
		//若找不到结束符,则将数据写入文件
		if (goal == -1) {
			fwrite(buffer, strlen(buffer), 1,fp);
			total += strlen(buffer);
			//指针当前位于文件末尾
			fseek(fp, 0, 2);
			cout << buffer;
		}
		else {
			//找到结束符,
			cout << endl;
			cout << endl;
			cout << "++client: " << buffer << endl;
			flag = 1;
		}
		return flag;
}

client:

// client1.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include "pch.h"
#include <iostream>
#include<WinSock2.h>
#include<string>
#include<stdio.h>
#include<cstdlib>
#pragma comment(lib,"ws2_32.lib")

#define MAX_BUFFER 120
using namespace std;
int main()
{
	long filesize(FILE*stream);
	WORD sockVersion = MAKEWORD(2, 2);
	WSADATA wsadata;
	if (WSAStartup(sockVersion, &wsadata)) {
		cout << "WSAStartup failed !" << endl;
		return 0;
	}



	SOCKET client;

	client = socket(AF_INET, SOCK_DGRAM, 0);
	if (client == INVALID_SOCKET) {
		cout << "socket failed" << endl;
		return 0;
	}

	sockaddr_in caddr, saddr;
	int slen = sizeof(saddr);
	int clen = sizeof(caddr);
	saddr.sin_family = AF_INET;
	saddr.sin_port = htons(8899);
	saddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");

	FILE *fp;     //声明一个文件类型的指针
	int isend;    //文件发送成功状态
	int ircv;
	char buffer[MAX_BUFFER]; //文件的缓冲区
	char filepath[200];
	char begin[]="我要准备给你发邮件了。";                            //
	memset(buffer, 0, sizeof(buffer));
	memset(filepath, 0, sizeof(filepath));

	while (1) {

		cout << "++++++++++++++++++++++++++++++++++++Client++++++++++++++++++++++++++++++++++++++++++++" << endl;
		//给服务器发送文件前的呼叫
		isend = sendto(client, begin, strlen(begin), 0, (SOCKADDR*)&saddr, slen);
		cout << "++client: " << begin << endl;
		
		//接收服务器端准备信息
		ircv = recvfrom(client, filepath, sizeof(filepath), 0, (SOCKADDR*)&saddr, &slen);
		if (ircv==SOCKET_ERROR) {
			cout << " 服务器关闭状态,无法访问!" << endl;
			closesocket(client);
			WSACleanup();
			cout << " 2s后将退出此控制台!" << endl;
			Sleep(2000);
			return 0;
		}
		cout << "==server: " << filepath << endl;
		memset(filepath, 0, sizeof(filepath));


		cout << "++client: 请输入所要传送文件的路径:形如(D:\\1.txt" << endl;
		scanf("%s", filepath);

		fp = fopen(filepath, "r");
		if (fp == NULL) {
			cout << " 文件不存在!" << endl;
			return 0;
		}
		else {
			cout << " 文件已经打开,等待传输······" << endl;
			Sleep(10);
		}

		isend = sendto(client, filepath, strlen(filepath), 0, (SOCKADDR*)&saddr, slen);
		memset(filepath, 0, sizeof(filepath));
		if (isend == SOCKET_ERROR) {
			cout << "文件名字发送失败!" << endl;
			return 0;
		}
		else {
			cout << " ······" << endl;
		}
		//开始读取文件内容进行发送
		while (!feof(fp)) {
			memset(buffer, 0, sizeof(buffer));
			fread(buffer, 1, MAX_BUFFER, fp);
			isend = sendto(client, buffer, sizeof(buffer), 0, (SOCKADDR*)&saddr, slen);
			if (isend == SOCKET_ERROR) {
				cout << " 文件传输失败!" << endl;
				closesocket(client);
				WSACleanup();
				cout << " 2s后将退出此控制台!" << endl;
				Sleep(2000);
				return 0;
			}
		}
		//告诉服务器传送此文件结束。
		isend = sendto(client, "success transport", strlen("success transport"), 0, (SOCKADDR*)&saddr, slen);
		if (isend == SOCKET_ERROR) {
			cout << " 结束信息传输失败!" << endl;
			closesocket(client);
			WSACleanup();
			cout << " 2s后将退出此控制台!" << endl;
			Sleep(2000);
			return 0;
		}
		cout << "++client: success transport "<< endl;

		//接收服务器接收文件的确认信息
		ircv = recvfrom(client, filepath, sizeof(filepath), 0, (SOCKADDR*)&saddr, &slen);
		if (ircv == SOCKET_ERROR) {
			cout << " 接收服务器确认信息失败!" << endl;
			closesocket(client);
			WSACleanup();
			cout << " 2s后将退出此控制台!" << endl;
			Sleep(2000);
			return 0;
		}
		cout << "==server: " << filepath << endl;
		memset(buffer, 0, sizeof(buffer));

		cout << endl;
		fclose(fp);
		fp = NULL;
	}


		/* while (1) { //获取文件剩余长度 long int length=filesize(fp); cout << "当前文件未读:" << length << endl; //文件剩余长度大于缓冲区大小,进行分块地区 if (length > sizeof(buffer)) { memset(buffer, 0, sizeof(buffer)); fread(buffer, 1, MAX_BUFFER, fp); isend = sendto(client, buffer, strlen(buffer), 0, (SOCKADDR*)&saddr, slen); if (isend == SOCKET_ERROR) { cout << "文件传输失败!" << endl; closesocket(client); WSACleanup(); return 0; } } else { //进行最后一次文件的读取 memset(buffer, 0, sizeof(buffer)); fread(buffer, 1, MAX_BUFFER, fp); isend = sendto(client, buffer, strlen(buffer), 0, (SOCKADDR*)&saddr, slen); if (isend == SOCKET_ERROR) { cout << "文件传输失败!" << endl; closesocket(client); WSACleanup(); return 0; } //告诉服务器传送此文件结束。 isend = sendto(client, "success transport", strlen("success transport"), 0, (SOCKADDR*)&saddr, slen); cout << "文件读取完毕!" << endl; break; } } fclose(fp); fp = NULL; }*/

	closesocket(client);
	WSACleanup();
	return 0;
}

long filesize(FILE*stream) {
	long curpos, length;
	curpos = ftell(stream);           //获取当前读写位置偏离文件头部的字节数。
	fseek(stream, 0L, SEEK_END);      //重新定位到文件的末尾位置
	length = ftell(stream);           //获取当前读写位置(当前指针在末尾)偏离文件头部的字节数,即文件总长度
	fseek(stream, curpos, SEEK_SET);  //从文件头部正向偏离curpos个位置,即回到原来的位置
	return length-curpos;             //获取文件剩余未读长度
}

在这里插入图片描述

在这里插入图片描述 问题: 最大的问题就是乱码问题,在接收文件的过程中总是会出现乱码问题,查了很多,乱七八糟的,由于经验少,想不到问题出现在哪里,最后发现是缓冲区问题。出现了数据的丢包等问题应该。