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; //获取文件剩余未读长度
}
问题:
最大的问题就是乱码问题,在接收文件的过程中总是会出现乱码问题,查了很多,乱七八糟的,由于经验少,想不到问题出现在哪里,最后发现是缓冲区问题。出现了数据的丢包等问题应该。