实验环境:
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 "; 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; }