c++ 解决TCP粘包问题

#include < iostream>
#include < Windows.h>
#include < vector>
#include < string>
#include < algorithm>
#include < time.h>
#include < exception>

#pragma comment(lib,"ws2_32.lib")
using namespace std;

WSADATA wsaData;
SOCKET hSocket;
SOCKADDR_IN servAddr;

size_t readn(SOCKET socket, void* buf, size_t count)
{
	int left = count; //剩下的字节
	char* ptr = (char*)buf;
	while (left > 0)
	{

		int readBytes = recv(socket, ptr, left, 0);
		if (readBytes < 0)//read函数小于0有两种情况:1中断 2出错
		{
			if (errno == EINTR)//读被中断
			{
				continue;
			}
			return -1;
		}
		if (readBytes == 0)//读到了EOF
		{
			// 服务器关闭
			return count - left;
		}
		left -= readBytes;
		ptr += readBytes;
	}
	return count;
}

/// 从缓冲区中读取指定长度的数据,但不清除缓冲区内容
size_t read_peek(int sockfd, char *buf, size_t len)
{
	while (1)
	{
		int ret = recv(sockfd, buf, len, MSG_PEEK);
		if (ret == 0)
		{
			std::cout << "服务器连接断开!!!" << std::endl;
			return 0;
		}
		if (ret == -1 && errno == EINTR)
		{
			//出现中断
			continue;
		}
		return ret;
	}
}

/// 按行读取数据
/// sockfd : 套接字
/// buf :应用层缓冲区,保存读取到的数据
/// maxline : 所规定的一行的长度
size_t readLine(int sockfd, void *buf, size_t maxline)
{
	int ret;
	int nRead = 0;
	int left = maxline; //剩下的字节数
	char * pbuf = (char *)buf;
	int count = 0;
	while (1)
	{
		ret = read_peek(sockfd, pbuf, left);   //读取长度为left的socket缓冲区内容
		if (ret <= 0)
		{
			return ret;
		}
		nRead = ret;
		for (int i = 0; i < nRead; i++)//看看读取出来的数据中是否有换行符\n
		{
			if (pbuf[i] == '\n')//如果有换行符
			{
				ret = readn(sockfd, pbuf, i + 1);//读取一行
				if (ret != i + 1)	//一定会读到i+1个字符,否则是读取出错
				{
					exit(EXIT_FAILURE);
				}
				return ret + count;
			}
		}
		
		//窥探的数据中并没有换行符
		//把这段没有\n的读出来
		ret = readn(sockfd, pbuf, nRead);
		if (ret != nRead)
		{
			exit(EXIT_FAILURE);
		}
		pbuf += nRead;
		left -= nRead;
		count += nRead;
	}
	return -1;
}

int main()
{
	if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
	{
		cout << "WSAStartup() error" << endl;
		return -1;
	}

	hSocket = socket(PF_INET, SOCK_STREAM, 0);
	if (hSocket == INVALID_SOCKET)
	{
		cout << "hSocket() error" << endl;
		return -1;
	}

	memset(&servAddr, 0, sizeof(servAddr));
	servAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
	servAddr.sin_family = AF_INET;
	servAddr.sin_port = htons(8888);

	if (connect(hSocket, (SOCKADDR*)& servAddr, sizeof(SOCKADDR)))
	{
		cout << "连接失败!" << endl;
		closesocket(hSocket);
		WSACleanup();

		return -1;
	}

	while (true)
	{
		int ret = readLine(hSocket, recvbuf, 1024);
		if (ret == -1)
		{
			cerr << "readline" << endl;
		}
		else if (ret == 0)
		{
			printf("service closed\n");
			break;
		}
		string ttt = recvbuf;
		cout << ttt.substr(0, ret);
		memset(recvbuf, 0, sizeof(recvbuf));
	}
	return 0;
}

发表评论

电子邮件地址不会被公开。 必填项已用*标注