• 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
    ";
    			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;
    }


  • 相关阅读:
    Appium环境搭建
    mysql数据库在Linux下安装与配置
    各模拟器adb连接端口
    MySQL 连接出现 Authentication plugin 'caching_sha2_password' cannot be loaded
    使用pycharm创建django项目
    win10安装docker步骤
    Linux常用命令
    LoadRunner HTTP+Json 接口性能测试
    API性能测试基本性能指标及要求
    Widows自带系统监控工具——24小时监控服务器性能
  • 原文地址:https://www.cnblogs.com/chaikefusibushiji/p/7475625.html
Copyright © 2020-2023  润新知