send和recv (大文件传输)
实验环境:
Windows
实验目的:
使用socket传输大文件(19.4M)
实验过程:
第一阶段
1.Server使用send(s,buffer,dataLen,0)将文件装入buffer一次发送出去
结果:成功
2.Client端使用char ch[20*1024*1024] = {0}开出20M栈空间以接数据
结果:栈溢出
应对方法:
使用堆分配char *ch = new char[20*1024*1024]
3.Client接收数据int ret = recv(s,ch,20*1024*1024,0)
结果:recv只接了27K数据,Server中send显示发送数据为19.4M
4.Client采用循环接收,不断的recv
结果:文件传输成功
第二阶段
5.本机开Server,Client跑在其它电脑
结果:Client接收失败
6.在Client的循环接收中加入输出,打印差多少字节
结果:循环输出42
分析原因:
Server在send mfc.zip之前先send了8字节文件长度,Client在recv mfc.zip前先使用50字节长的缓冲区tmp[50] recv了长度,推断:差 的42字节被tmp收走
验证:
添加输出,在recv长度处打印此次接收的长度
结果:
输出50,Server send发送长度设置正确
7.将tmp的后42字节加入buffer中并修改代码
结果:成功
特殊现象概述:
发送方:send两次,第一次send 8字节,第二次send 19.4M
接收方:8字节在前、19.4M在后,两部分合二为一、一起到达
实验结论:
1.系统发送缓冲区一定很大,即send(s,buffer,dataLen,0)19.4M一次发送没有问题。
2.系统接收缓冲区有限制,即recv(s,buffer,bufferLen,0)不管buffer多大一次只能几十到上百K,只有循环接收才能解决。
3.小数据发送会产生粘包。
附加内容:
1.send函数
int send(SOCKET s, const char *buffer, int len, int flag)
数据从buffer拷贝到transport buffer,然后进行发送。len大小为多少便发送多少的数据。
2.程序栈和堆的大小
linux系统下默认栈大小是10M,windows系统下默认栈大小是1M.
windows下用vs2010编译C++程序时,编译属性中可以重新设定栈大小.
堆的话,理论上内存有多大,就可以建多大.
但32位的程序在64位系统上运行的时候,一个进程的堆大小应该是不可以超过4G的.
栈和线程相关, 默认1MB预留, 初次递交8KB, 自动增长, 具体使用要看线程调用栈了. 所以如果进程中有N个线程. 默认情况下, 有N*1MB的栈预留 空间, 和小于这个数字的实际使用.
堆和Heap管理有关, 默认存在系统堆和CRT堆. 具体大小取决于程序本身对内存的分配和使用, 可以调用HeapSize看实际使用大小.
另外还有虚拟内存, 独立于对堆外, 直接通过VirtualAlloc预留或分配. 也属于进程动态分配的内存.
实验代码:
Server
#include <WinSock2.h>
#include <Windows.h>
#include <iostream>
#include <string>
#include <fstream>
#pragma comment(lib, "Ws2_32.lib")
int main()
{
WSADATA ws;
if(WSAStartup(MAKEWORD(2, 2), &ws) != 0)
return false;
SOCKET s = socket(AF_INET, SOCK_STREAM, 0);
if(INVALID_SOCKET == s)
return false;
SOCKADDR_IN addr;
addr.sin_family = AF_INET;
addr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
addr.sin_port = htons(4444);
int ret = bind(s, (sockaddr*)&addr, sizeof(addr));
if(SOCKET_ERROR == ret)
{
std::cout<<"bind failed : "<<GetLastError()<<std::endl;
return SOCKET_ERROR;
}
ret = listen(s, 10);
if(SOCKET_ERROR == s)
{
std::cout<<"listen fail : "<<GetLastError()<<std::endl;
return SOCKET_ERROR;
}
while(true)
{
SOCKADDR_IN clientAddr;
int addrLen = sizeof(clientAddr);
SOCKET cs = accept(s, (sockaddr*)&clientAddr, &addrLen);
if(INVALID_SOCKET == cs)
{
std::cout<<"accept failed : "<<GetLastError()<<std::endl;
continue;
}
std::cout<<"one connection enter into..."<<std::endl;
{
char buf[1024] = {0};
ret = recv(cs, buf, 1024, 0);
if(SOCKET_ERROR == ret)
{
std::cout<<"recv error : "<<GetLastError()<<std::endl;
continue;
}
std::cout<<buf<<std::endl;
}
{
char buf[] = "hello baby\r\n";
ret = send(cs, buf, strlen(buf), 0);
if(SOCKET_ERROR == ret)
{
std::cout<<"send error : "<<GetLastError()<<std::endl;
return -1;
}
}
{
std::ifstream is("C:\\Documents and Settings\\Administrator\\桌面\\mfc.zip", std::ios::binary);
is.seekg(0, is.end);
int length = is.tellg();
is.seekg(0, is.beg);
char *ch = new char[length+10];
std::string str = std::to_string((long long)length);
memset(ch, 0, 10);
memcpy(ch, str.c_str(), str.length());
is.read(ch+10, length);
ret = send(cs, ch, length+10, 0);
if(SOCKET_ERROR == ret)
{
std::cout<<"send large data error : "<<GetLastError()<<std::endl;
return -1;
}
delete []ch;
is.close();
}
Sleep(10000);
closesocket(cs);
}
return 0;
}Client
#include <WinSock2.h>
#include <Windows.h>
#include <iostream>
#include <fstream>
#include <string>
#include <process.h>
#pragma comment(lib, "Ws2_32.lib")
int main()
{
WSADATA ws;
if(WSAStartup(MAKEWORD(2, 2), &ws) != 0)
return false;
SOCKET s = socket(AF_INET, SOCK_STREAM, 0);
if(INVALID_SOCKET == s)
return false;
sockaddr_in addr = {0};
addr.sin_addr.S_un.S_addr = inet_addr("192.168.4.18");
addr.sin_family = AF_INET;
addr.sin_port = htons(4444);
int ret = connect(s, (sockaddr*)&addr, sizeof(addr));
if(SOCKET_ERROR == ret)
{
std::cout<<"Connect Server Failed : "<<GetLastError()<<std::endl;
return -1;
}
std::string str = "xiaogushihaoren";
ret = send(s, str.c_str(), str.length(), 0);
if(SOCKET_ERROR == ret)
{
std::cout<<"Send Data Failed : "<<GetLastError()<<std::endl;
return -1;
}
char ch[100] = {0};
ret = recv(s, ch, 100, 0);
if(0 == ret)
{
std::cout<<"Peer Close The Connection"<<std::endl;
return -1;
}
else if(SOCKET_ERROR == ret)
{
std::cout<<"Recv Data Failed : "<<GetLastError()<<std::endl;
return -1;
}
std::cout<<ch<<std::endl;
{
char tmp[10] = {0};
ret = recv(s, tmp, 10, 0);
int len = std::stoi(tmp);
char *chmax = new char[len];
int cursor = 0;
while(cursor != len)
{
char ch[100*1024] = {0};
ret = recv(s, ch, 100*1024, 0);
if(SOCKET_ERROR == ret)
{
std::cout<<"Recv Data Failed : "<<GetLastError()<<std::endl;
return -1;
}
if(0 == ret)
break;
memcpy(chmax+cursor, ch, ret);
cursor += ret;
}
std::ofstream os("C:\\mfc1.zip", std::ios::binary);
os.write(chmax, len);
os.close();
}
std::cin.get();
return 0;
}
浙公网安备 33010602011771号