//WinSock的注册函数,初始化底层的Windows Sockets DLL if (WSAStartup(sockVersion, &wsaData) != 0) return 0; //创建一个socket并返回socket的标识符 SOCKET sclient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (sclient == INVALID_SOCKET) std::cout << "invalid socket!" << std::endl; return 0; sockaddr_in serAddr; serAddr.sin_family = AF_INET; serAddr.sin_port = htons(8888); serAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); if (connect(sclient, (sockaddr *)&serAddr, sizeof(serAddr)) == SOCKET_ERROR) { //连接失败 std::cout << "connect error !" << std::endl; closesocket(sclient); return 0; std::string data="abcdef_"; char* chs = new char[1000000]; for (int i = 0; i < 1000000; ++i) chs[i] = i; int n = 0; while (1) //data = "abcdef_"+std::to_string(n); //std::cin >> data; const char * sendData=chs; //sendData = data.c_str(); //string转const char* /* send()用来将数据由指定的socket传给对方主机 int send(int s, const void * msg, int len, unsigned int flags) s为已建立好连接的socket,msg指向数据内容,len则为数据长度,参数flags一般设0 成功则返回实际传送出去的字符数,失败返回-1,错误原因存于error */ //这么干肯定会干爆的吧? send(sclient, sendData, 1000000, 0);//一次发送得多,接收得少 //接收返回的数据 char recData[255]; int ret = recv(sclient, recData, 255, 0);//阻塞在此处等待接收 if (ret > 0) recData[ret] = 0x00; std::cout <<"第"<20) break; Sleep(1000); delete[] chs; closesocket(sclient); WSACleanup(); system("pause"); return 0;

服务器端程序:

#include <winsock.h> 
#include<iostream>
#pragma comment(lib,"ws2_32.lib") 
#include <Windows.h>
#include <string>
DWORD WINAPI ThreadTCP(LPVOID pParam);
int main()
	//初始化Windows Socket Application
	WORD sockVersion = MAKEWORD(2, 2);
	WSADATA wsaData;
	//WinSock的注册函数,初始化底层的Windows Sockets DLL
	if (WSAStartup(sockVersion, &wsaData) != 0)
		return 0;
	//创建套接字 
	SOCKET slisten = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (slisten == INVALID_SOCKET)
		std::cout << "create socket error!" << std::endl;
		return 0;
	//绑定IP和端口  	
	sockaddr_in sin;
	sin.sin_family = AF_INET;
	sin.sin_port = htons(8888);//指定端口,将端口号转换为网络字节顺序
	sin.sin_addr.S_un.S_addr = INADDR_ANY;
	//bind()把socket绑定到特定的网络地址上
	if (bind(slisten, (LPSOCKADDR)&sin, sizeof(sin)) == SOCKET_ERROR)
		std::cout << "bind error!" << std::endl;
		return 0;
	//开始监听
//启动指定的socket,监听到来的连接请求  
5指定了监听socket的等待连接缓冲区队列的最大长度,一般设为5	
	if (listen(slisten, 5) == SOCKET_ERROR)
		std::cout << "listen error !" << std::endl;
		return 0;
	//循环接收数据 
	SOCKET sClient;
	sockaddr_in remoteAddr;
	int nAddrlen = sizeof(remoteAddr);
	while (true)
		std::cout << "阻塞。。。。等待连接。。。" << std::endl;
		//接收一个连接请求,并新建一个socket,原来的socket返回监听状态
		sClient = accept(slisten, (SOCKADDR *)&remoteAddr, &nAddrlen);
		if (sClient == INVALID_SOCKET)
			std::cout << "accept error !" << std::endl;
			continue;
		//inet_addr()把一个标准的点分十进制的IP地址转换成长整型的地址数据
		//inet_ntoa()把长整型的IP地址数据转换成点分十进制的ASCII字符串	
		std::cout << "接受一个连接:" << inet_ntoa(remoteAddr.sin_addr)<<":"<<remoteAddr.sin_port << std::endl;
		//创建一个线程与客户端进行会话
		CreateThread(NULL, 0, ThreadTCP, (LPVOID)sClient, 0, NULL);
	closesocket(slisten);
	//winsock的注销函数,从底层的Windows Sockets DLL 中撤销注册
	WSACleanup();
	system("pause");
	return 0;
DWORD WINAPI ThreadTCP(LPVOID pParam)
	SOCKET sClient = (SOCKET)pParam;
	char revData[255];
	std::string data = "hello_";
	int n = 0;
	while (1)
		data = "hello_"+std::to_string(n);
		//接收数据  
		//从一个已经与对方建立连接的socket接收数据	
		//快速发送与慢接收-->导致接收端满
		int ret = recv(sClient, revData, 255, 0);//阻塞于此进行接收
		if (ret > 0)
			revData[ret] = 0x00;
			std::cout << "第" << n++ << "回:" << revData << std::endl;
			//printf(revData);
		//发送数据  	
		const char * sendData = data.c_str();
		向一个已经与对方建立连接的socket发送数据	
		int nb=send(sClient, sendData, strlen(sendData), 0);
		if (nb != strlen(sendData))
			break;
		//Sleep(1000);
	//断开连接
	std::cout << "断开连接" << std::endl;
	//关闭socket,释放相应的资源
	closesocket(sClient);
	return 0;

 wireshark抓包的显示:

在第23个数据包的时候,服务器端8888就告诉客户端5147自己不行了,然后就断开了

下面来分析具体过程:

首先前3个数据包是在建立TCP会话

然后在第4个数据包,客户端5147发送了65539字节的数据(包含44个字节的帧头,和65495字节数据),

但是程序中要求发送的是1000000个字节:send(sclient, sendData, 1000000, 0);注意数据包4的Data(65495bytes)

然后8888确认了接收【数据包5】

紧接着5147继续发送数据【数据包6】--【数据包16】,注意其数据的序号Seq从1->65496->130991->196486->....->720446

依次相差每次发送的数据量65495;

在第17个数据包,8888确认了接收,其确认号Ack为785941(即8888已经接收到785940字节,接下来应该接收785941字节了)

接下来5147继续发送数据【数据包18】--【数据包20】 ,数据序号Seq从785941->851436->916931,即982426之前的数据都已经发送了;离1000000数据还剩下一些,在第21个数据包继续发送

然后8888确认了接收【数据包22】其确认号Ack=1000001(我已经接收到1000000字节了,你该发送1000001啦!),

不过,控制台已经关闭了,程序崩了。。。

第23个数据包和第22个数据包中间隔了近5秒,8888已经关闭了会话

找到服务器控制台崩溃的原因了,应该是在定义char revData[255]; 这里的问题,应该改成比255大的数,比如256;

原因在于后面干了这么一件事情:revData[ret] = 0x00;

要防止索引数组溢出嘛!

》》》》》》》》》》》》》》》 》》》》》》》》》

客户端程序:

#include <winsock.h> 
#include<iostream>
#pragma comment(lib,"ws2_32.lib") 
#include <Windows.h>
#include<string>
int main()
	//初始化Windows Socket Application
	WORD sockVersion = MAKEWORD(2, 2);
	WSADATA wsaData;
	//WinSock的注册函数,初始化底层的Windows Sockets DLL
	if (WSAStartup(sockVersion, &wsaData) != 0)
		return 0;
	//创建一个socket并返回socket的标识符
	SOCKET sclient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (sclient == INVALID_SOCKET)
		std::cout << "invalid socket!" << std::endl;
		return 0;
	sockaddr_in serAddr;
	serAddr.sin_family = AF_INET;
	serAddr.sin_port = htons(8888);
	serAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
	if (connect(sclient, (sockaddr *)&serAddr, sizeof(serAddr)) == SOCKET_ERROR)
	{  			//连接失败 	
		std::cout << "connect error !" << std::endl;
		closesocket(sclient);
		return 0;
	std::string data="abcdef_";
	char* chs = new char[1000000];
	for (int i = 0; i < 1000000; ++i)
		chs[i] = i;
	const char * sendData = chs;
	char recData[255];
	int n = 0;
	while (1)
		//data = "abcdef_"+std::to_string(n);
		//std::cin >> data;
		//sendData = data.c_str();
		//string转const char* 		
		/*		send()用来将数据由指定的socket传给对方主机
		int send(int s, const void * msg, int len, unsigned int flags)
		s为已建立好连接的socket,msg指向数据内容,len则为数据长度,参数flags一般设0
		成功则返回实际传送出去的字符数,失败返回-1,错误原因存于error		*/
		send(sclient, sendData, 1000000, 0);//一次发送多,接收少
		//接收返回的数据
		//int ret = recv(sclient, recData, 255, 0);//阻塞在此处等待接收
		//if (ret > 0)
		//	recData[ret] = 0x00;
		//	std::cout <<"第"<<n<<"回:"<< recData << std::endl;
		if (n++>10)
			break;
		Sleep(1000);
	delete[] chs;
	closesocket(sclient);
	WSACleanup();
	system("pause");
	return 0;

服务器端程序:

#include <winsock.h> 
#include<iostream>
#pragma comment(lib,"ws2_32.lib") 
#include <Windows.h>
#include <string>
DWORD WINAPI ThreadTCP(LPVOID pParam);
int main()
	//初始化Windows Socket Application
	WORD sockVersion = MAKEWORD(2, 2);
	WSADATA wsaData;
	//WinSock的注册函数,初始化底层的Windows Sockets DLL
	if (WSAStartup(sockVersion, &wsaData) != 0)
		return 0;
	//创建套接字 
	SOCKET slisten = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (slisten == INVALID_SOCKET)
		std::cout << "create socket error!" << std::endl;
		return 0;
	//绑定IP和端口  	
	sockaddr_in sin;
	sin.sin_family = AF_INET;
	sin.sin_port = htons(8888);//指定端口,将端口号转换为网络字节顺序
	sin.sin_addr.S_un.S_addr = INADDR_ANY;
	//bind()把socket绑定到特定的网络地址上
	if (bind(slisten, (LPSOCKADDR)&sin, sizeof(sin)) == SOCKET_ERROR)
		std::cout << "bind error!" << std::endl;
		return 0;
	//开始监听
//启动指定的socket,监听到来的连接请求  
5指定了监听socket的等待连接缓冲区队列的最大长度,一般设为5	
	if (listen(slisten, 5) == SOCKET_ERROR)
		std::cout << "listen error !" << std::endl;
		return 0;
	//循环接收数据 
	SOCKET sClient;
	sockaddr_in remoteAddr;
	int nAddrlen = sizeof(remoteAddr);
	while (true)
		std::cout << "阻塞。。。。等待连接。。。" << std::endl;
		//接收一个连接请求,并新建一个socket,原来的socket返回监听状态
		sClient = accept(slisten, (SOCKADDR *)&remoteAddr, &nAddrlen);
		if (sClient == INVALID_SOCKET)
			std::cout << "accept error !" << std::endl;
			continue;
		//inet_addr()把一个标准的点分十进制的IP地址转换成长整型的地址数据
		//inet_ntoa()把长整型的IP地址数据转换成点分十进制的ASCII字符串	
		std::cout << "接受一个连接:" << inet_ntoa(remoteAddr.sin_addr)<<":"<<remoteAddr.sin_port << std::endl;
		//创建一个线程与客户端进行会话
		CreateThread(NULL, 0, ThreadTCP, (LPVOID)sClient, 0, NULL);
	closesocket(slisten);
	//winsock的注销函数,从底层的Windows Sockets DLL 中撤销注册
	WSACleanup();
	system("pause");
	return 0;
DWORD WINAPI ThreadTCP(LPVOID pParam)
	SOCKET sClient = (SOCKET)pParam;
	//char* revData=new char[100000];
	char revData[1000];
	std::string data = "hello_";
	int n = 0;
	while (1)
		data = "hello_"+std::to_string(n);
		//接收数据  
		//从一个已经与对方建立连接的socket接收数据	
		//快速发送与慢接收-->导致接收端满
		int ret = recv(sClient, revData, 100, 0);//阻塞于此进行接收
		if (ret > 0)
			revData[ret] = 0x00;
			std::cout << "第" << n++ << "回:" << revData << std::endl;
			//printf(revData);
		//发送数据  	
		//const char * sendData = data.c_str();
		//向一个已经与对方建立连接的socket发送数据	
		//int nb=send(sClient, sendData, strlen(sendData), 0);
		//if (nb != strlen(sendData))
		//	break;
		if (n++ > 100)
			break;
		Sleep(1000);
	//断开连接
	std::cout << "断开连接" << std::endl;
	//关闭socket,释放相应的资源
	closesocket(sClient);
	return 0;

下面进行详细分析:

前3个数据包建立TCP连接,然后客户端程序发送send(1000000)个字节

第4个数据包 客户端6324发送发送65495个字节,8888返回一个确认,然后第6个数据包到第16个数据包,每次发送65495个字节,第17个数据包是8888的确认,第18个数据包到第21个数据包,完成了1000000个字节的发送

此时客户端程序Sleep(1000)

而服务器程序则recv(100)个字节,并显示100个字节的内容到控制台,然后它也Sleep(1000),所以其接收缓存区还得有(1000000-100)字节待取出

客户端程序Sleep完毕,再次send(1000000)个字节

从第23个数据包直到第38个数据包,6324发送完1000000个字节

服务器端从Sleep醒来,接收recv(100)个字节后,接收缓存区有(2000000-200)个字节,它又Sleep(1000)

客户端程序Sleep醒来,继续发送1000000个字节,从第40个数据包到第50个数据包,每个数据包65495个字节

第51个数据包8888返回一个确认告知:它已经接收到27720445字节,准备接收27720446字节,但是Win=30208,

第52个数据包告知:[TCP Window Full]

第53个数据包8888告知:[TCP ZeroWindow] Win=0了

第54个数据包6324发送了一个零窗口探测? [TCP ZeroWIndowProbe]其中仅仅包含1个字节的数据

通信在第68个数据包结束了,8888->6324[RST,ACK]

下面给出一个更好的实验样例:

客户端程序:

#include <winsock.h> 
#include<iostream>
#pragma comment(lib,"ws2_32.lib") 
#include <Windows.h>
#include<string>
int main()
	//初始化Windows Socket Application
	WORD sockVersion = MAKEWORD(2, 2);
	WSADATA wsaData;
	//WinSock的注册函数,初始化底层的Windows Sockets DLL
	if (WSAStartup(sockVersion, &wsaData) != 0)
		return 0;
	//创建一个socket并返回socket的标识符
	SOCKET sclient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (sclient == INVALID_SOCKET)
		std::cout << "invalid socket!" << std::endl;
		return 0;
	sockaddr_in serAddr;
	serAddr.sin_family = AF_INET;
	serAddr.sin_port = htons(8888);
	serAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
	if (connect(sclient, (sockaddr *)&serAddr, sizeof(serAddr)) == SOCKET_ERROR)
	{  			//连接失败 	
		std::cout << "connect error !" << std::endl;
		closesocket(sclient);
		return 0;
	std::string data="abcdef_";
	char* chs = new char[1000000];
	for (int i = 0; i < 1000000; ++i)
		chs[i] = i;
	const char * sendData = chs;
	char recData[255];
	int n = 0;
	while (1)
		//data = "abcdef_"+std::to_string(n);
		//std::cin >> data;
		//sendData = data.c_str();
		//string转const char* 		
		/*		send()用来将数据由指定的socket传给对方主机
		int send(int s, const void * msg, int len, unsigned int flags)
		s为已建立好连接的socket,msg指向数据内容,len则为数据长度,参数flags一般设0
		成功则返回实际传送出去的字符数,失败返回-1,错误原因存于error		*/
		std::cout << "第" << n << "个发送" << std::endl;
		int sn=send(sclient, sendData, 1000000, 0);//一次发送多,接收少
		if (sn <= 0)
			std::cout << "数据发送失败" << std::endl;
		//接收返回的数据
		//int ret = recv(sclient, recData, 255, 0);//阻塞在此处等待接收
		//if (ret > 0)
		//	recData[ret] = 0x00;
		//	std::cout <<"第"<<n<<"回:"<< recData << std::endl;
		if (n++>20)
			break;
		Sleep(500);
	delete[] chs;
	closesocket(sclient);
	WSACleanup();
	system("pause");
	return 0;

 服务器端程序:

#include <winsock.h> 
#include<iostream>
#pragma comment(lib,"ws2_32.lib") 
#include <Windows.h>
#include <string>
//采用select实现多个连接同时处理
int main()
	//初始化Windows Socket Application
	WORD sockVersion = MAKEWORD(2, 2);
	WSADATA wsaData;
	//WinSock的注册函数,初始化底层的Windows Sockets DLL
	if (WSAStartup(sockVersion, &wsaData) != 0)
		return 0;
	//创建服务器端监听套接字 
	SOCKET slisten = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (slisten == INVALID_SOCKET)
		std::cout << "create socket error!" << std::endl;
		return 0;
	//绑定IP和端口  	
	sockaddr_in sin;
	sin.sin_family = AF_INET;
	sin.sin_port = htons(8888);//指定端口,将端口号转换为网络字节顺序
	sin.sin_addr.S_un.S_addr = INADDR_ANY;
	//bind()把socket绑定到特定的网络地址上
	if (bind(slisten, (LPSOCKADDR)&sin, sizeof(sin)) == SOCKET_ERROR)
		std::cout << "bind error!" << std::endl;
		return 0;
	//开始监听
	//启动指定的socket,监听到来的连接请求  
	//5指定了监听socket的等待连接缓冲区队列的最大长度,一般设为5	
	if (listen(slisten, 5) == SOCKET_ERROR)
		std::cout << "listen error !" << std::endl;
		return 0;
	std::cout << "服务器开启成功" << std::endl;
	FD_SET  read_set;//创建文件描述集合
	FD_ZERO(&read_set);//清空文件描述集合
	FD_SET(slisten, &read_set);//将监听套接字放入文件描述集合
	while (true)
		FD_SET settmp;
		FD_ZERO(&settmp);
		settmp = read_set;
		//检查文件描述集合中的socket状态是否就绪
		int total = select(0, &settmp, nullptr, nullptr, nullptr);
		if (total == SOCKET_ERROR)
			continue;
		for (int i = 0; i < settmp.fd_count; i++)
			SOCKET ss = settmp.fd_array[i];
			if (ss == slisten)//监听套接字有就绪
				循环接收数据 
				SOCKET sClient;
				sockaddr_in remoteAddr;
				int nAddrlen = sizeof(remoteAddr);
				//接收一个连接请求,并新建一个socket,原来的socket返回监听状态
				sClient = accept(slisten, (SOCKADDR *)&remoteAddr, &nAddrlen);
				if (sClient == INVALID_SOCKET)
					std::cout << "accept error !" << std::endl;
					continue;
				//inet_addr()把一个标准的点分十进制的IP地址转换成长整型的地址数据
				//inet_ntoa()把长整型的IP地址数据转换成点分十进制的ASCII字符串	
				std::cout << "接受一个连接:" << inet_ntoa(remoteAddr.sin_addr) << ":" << remoteAddr.sin_port << std::endl;
				FD_SET(sClient, &read_set);
				//客户端连接的socket
				char msg[10000];
				int ret=recv(ss, msg, 10000, 0);//接收数据
				if (ret == SOCKET_ERROR || ret == 0)
					closesocket(ss);
					FD_CLR(ss, &read_set);
					std::cout << "结束一个连接" << std::endl;
					std::cout << "接收到数据" << std::endl;
		Sleep(100);
	closesocket(slisten);
	//winsock的注销函数,从底层的Windows Sockets DLL 中撤销注册
	WSACleanup();
	system("pause");
	return 0;

提要:客户端每次发送100w个字节向服务器,然后暂停500ms,继续;

服务器端每次取出1w个字节,然后暂停100ms,显然服务器端是接收不及的;

当服务器端的接收缓存被充满之后,客户端的send()被阻塞,不再继续往客户端的发送缓存写入;

由此观察到的控制台显示情况是:客户端在显示了第0个发送、第1个发送、第2个发送、第3个发送后,就暂停了;而服务器端的控制台是飞快的显示接收到数据,其中每隔100ms稍微暂停一下,

过一会儿,客户端控制台则又显示第4个发送、第5个发送,然后就继续暂停(程序阻塞在send()位置);

整个发送和接收的过程持续时间在这里最终取决于服务器端接收的时间--客户端总共发送21*100w个字节;而服务器端接收的速率为1w字节/100ms,总的接收时间为(21*100w)/(10w/s)=210s时间,加上程序Sleep的一些时间,大概在230s左右

根据wireshark抓包的情况,也能与上面的分析匹配:

如果客户端发送数据的速度远远大于服务器接收数据的速度,那么--&gt;客户端程序:#include &lt;winsock.h&gt; #include&lt;iostream&gt;#pragma comment(lib,"ws2_32.lib") #include &lt;Windows.h&gt;#include&lt;string&gt;int main(){ //初始化Windows Socket Application WORD sockVersion = MAK.
TCP协议被称作一种端对端协议。这是因为它为两台计算机之间的连接起了重要作用:当一台计算机需要与另一台远程计算机连接时,TCP协议会让它们建立一个连接、发送接收数据以及终止连接。 TCP是提供面向连接的,可靠的字节流服务。面向连接是TCP在正式进行通讯之前首先通过一些握手机制确认双方通讯意向并建立一条认为可以传输的概念通道。字节流服务是TCP传输的最小单位为字节,认为字节是有意义的单位。并且TCP不会在此字节流中插入记录符号。发送端不对字节数据做任何解释,均由接收端的应用完成解释。
本文实例讲述了Go语言服务器开发之简易TCP客户端与服务端实现方法。分享给大家供大家参考。具体实现方法如下: Go语言具备强大的服务器开发支持,这里示范了最基础的服务器开发:通过TCP协议实现客户端服务器的通讯。 一 服务端,为每个客户端新开一个goroutine 代码如下:func ServerBase() {      fmt.Println(“Starting the server…”)      //create listener      listener, err := net.Listen(“tcp”, “192.168.1.27:50000”)      if er
本文实例讲述了Android实现TCP客户端接收数据的方法。分享给大家供大家参考,具体如下: 配合log4net使用,用来接收调试信息。因此,此客户端只管通过TCP接收字符串数据,然后显示在界面上。 接收TCP数据 try { Socket s = new Socket(192.168.1.5, 8240); InputStream inputStream = s.getInputStream(); DataInputStream input = new DataInputStream(inputStream); byte[] b = new byte[10000];
访问的数据类型不是期望的数据类型而产生崩溃。比如,我期望服务端返回string类型,但是后台给我返回的是NSNumber类型,那么我操作时候用到的是string的方法。结果因为找不到对应的方法而崩溃。这类情况是比较常见的,后端传回了空数据客户端没有做对应的判断继续执行下去了,这样就产生了crash。或者自己本地的某个数据为空数据而去使用了。 解决办法:1、服务端都加入默认值,不返回空内容或无key。或者是在客户端进行非空判断。2、对容易出错的地方提前进行类型判断。 点击事件方法处理不当 这类情况比较常见,
int numsRecvSoFar = 0;//目前收到的数据 int numsRemainingToRecv = dateSize;//剩余要接收数据 printf("enter MySocketRecv0\n"); while (1){ int bytes
2.主从之间数据吞吐量过大,数据同步操作频率过高 服务器重启时,redis中没有数据,而此时如果大量请求访问,redis不能给数据,将大量直接访问数据库,造成服务器宕机 解决方案: