Сервер перемешивает сообщения от клиента

Нашёл на ютубе пример минимального консольного чата на WinSock. Там в примере к серверу может подключаться только один клиент. Скопипастил код и скомпилировал - работает.
Но вот что странно. Если дописать код, который будет в цикле слать серверу сообщения, то на сервере они очень часто перемешиваются. То есть, несколько отправленных сообщений обрабатываются сервером как одно целое.
Код сервера:

#include <iostream>
#include <WS2tcpip.h>

#pragma comment (lib, "ws2_32.lib")

int main()
{
	WSAData wsaData;
	unsigned short winsockVersion = MAKEWORD(2, 2);
	int wsaInitCode = WSAStartup(winsockVersion, &wsaData);
	if (wsaInitCode)
	{
		std::cerr << "WSAStartup() failed! Code " << wsaInitCode << std::endl;
		return 1;
	}
	
	SOCKET serverSocket = socket(AF_INET, SOCK_STREAM, NULL);
	if (serverSocket == 0 || serverSocket == INVALID_SOCKET)
	{
		std::cerr << "Failed to create a socket!" << std::endl;
		WSACleanup();
		return 1;
	}

	const int PORT = 54000;
	sockaddr_in serverAddress;
	ZeroMemory(&serverAddress, sizeof(sockaddr_in));
	serverAddress.sin_family = AF_INET;
	serverAddress.sin_addr.S_un.S_addr = INADDR_ANY;
	serverAddress.sin_port = htons(PORT);
	if (bind(serverSocket, (sockaddr*)&serverAddress, sizeof(serverAddress)) == SOCKET_ERROR)
	{
		std::cerr << "Failed to bind a socket!" << std::endl;
		closesocket(serverSocket);
		WSACleanup();
		return 1;
	}

	if (listen(serverSocket, SOMAXCONN) == SOCKET_ERROR)
	{
		std::cerr << "Failed to listen a socket!" << std::endl;
		closesocket(serverSocket);
		WSACleanup();
		return 1;
	}

	std::cout << "Server is started on port " << PORT << std::endl;

	bool active = true;
	while (active)
	{
		sockaddr_in clientAddress;
		int clientAddressSize = sizeof(clientAddress);

		SOCKET clientSocket = accept(serverSocket, (sockaddr*)&clientAddress, &clientAddressSize);

		char host[NI_MAXHOST];
		char service[NI_MAXSERV];
		ZeroMemory(&host, sizeof(host));
		ZeroMemory(&service, sizeof(service));
		if (getnameinfo((sockaddr*)&clientAddress, clientAddressSize, host, NI_MAXHOST, service, NI_MAXSERV, 0) == 0)
		{
			std::cout << host << " connected on port " << service << std::endl;
		}
		else
		{
			inet_ntop(AF_INET, &clientAddress.sin_addr, host, NI_MAXHOST);
			std::cout << host << " connected on port " << htons(clientAddress.sin_port) << std::endl;
		}

		const int BUFFER_SIZE = 4096;
		char* buffer = (char*)_malloca(sizeof(char) * BUFFER_SIZE);
		if (buffer)
		{
			while (true)
			{
				ZeroMemory(buffer, BUFFER_SIZE);
				int bytesReceived = recv(clientSocket, buffer, BUFFER_SIZE, 0);
				if (bytesReceived == SOCKET_ERROR)
				{
					std::cerr << "recv() failed!" << std::endl;
					closesocket(clientSocket);
					break;
				}

				std::cout << "Received: " << buffer << std::endl;
				send(clientSocket, buffer, bytesReceived + 1, 0);
				if (bytesReceived == 1 && buffer[0] == 'q')
				{
					std::cout << "The QUIT command received! Quitting!" << std::endl;
					active = false;
					break;
				}
				if (bytesReceived == 1 && buffer[0] == 'z')
				{
					std::cout << "Disconnecting the client!" << std::endl;
					break;
				}
			}
			_freea(buffer);
		}
		closesocket(clientSocket);
	}
	closesocket(serverSocket);

	WSACleanup();

	return 0;
}

Код клиента:

#include <iostream>
#include <string>
#include <WS2tcpip.h>

#pragma comment (lib, "ws2_32.lib")

int main()
{
	WSAData wsaData;
	unsigned short winsockVersion = MAKEWORD(2, 2);
	int wsaInitCode = WSAStartup(winsockVersion, &wsaData);
	if (wsaInitCode)
	{
		std::cerr << "WSAStartup() failed! Code " << wsaInitCode << std::endl;
		return 1;
	}

	const int PORT = 54000;
	const char* IP = (char*)"*.*.*.*";

	sockaddr_in serverAddress;
	ZeroMemory(&serverAddress, sizeof(sockaddr_in));
	serverAddress.sin_family = AF_INET;
	in_addr ipAddr;
	if (inet_pton(AF_INET, IP, &ipAddr) <= 0)
	{
		std::cerr << "Failed to parse an IP address!" << std::endl;
		WSACleanup();
		return 1;
	}
	serverAddress.sin_addr = ipAddr;
	serverAddress.sin_port = htons(PORT);

	SOCKET clientSocket = socket(AF_INET, SOCK_STREAM, NULL);
	if (clientSocket == 0 || clientSocket == INVALID_SOCKET)
	{
		std::cerr << "Failed to create a socket!" << std::endl;
		WSACleanup();
		return 1;
	}

	if (connect(clientSocket, (sockaddr*)&serverAddress, sizeof(sockaddr)) == SOCKET_ERROR)
	{
		std::cerr << "Connection failed!" << std::endl;
		closesocket(clientSocket);
		WSACleanup();
		return 1;
	}

	while (true)
	{
		std::string msg = "Fuck you!\0";
		int len = msg.length();
		if (send(clientSocket, msg.c_str(), len, 0) == SOCKET_ERROR)
		{
			break;
		}

		Sleep(1);
	}
	send(clientSocket, "z", 1, 0);
	Sleep(1);
	closesocket(clientSocket);
	WSACleanup();

	return 0;
}

Если идти по циклу отправки дебаггером, то видно, что на строчке send он не задерживается. То есть, функция send выполняется и выполнение сразу идёт дальше. При этом, в консоли сервера видно, что сообщение он ещё не получил (или не успел обработать). Хотя, чего там обрабатывать? 9 символов + служебный трафик.
Должна приходить вот такая строка:

Received: Fuck you!

Но очень часто приходит такое:

MY_HOST connected on port 52519
Received: Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!F}чe!╧
Received: uck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!Fuck you!
Received: Fuck you!
Received: Fuck you!
Received: Fuck you!
Received: Fuck you!
Received: Fuck you!
Received: Fuck you!
Received: Fuck you!
Received: Fuck you!

Это момент, когда вижуалка только что загрузила клиент в дебаггере и там на фоне ещё всякие средства диагностики грузятся и т.п.
А когда всё уже загружено, то слепляет меньше. Но всё-равно неприятно. Увеличение задержки между отправками не помогает. При этом не важно, где находится сервер - на localhost или в соседнем здании.
Когда-то давно уже была такая же проблема с сокетами TServerSocket / TClientSocket на Delphi. Там я каждую секунду передавал клиентам информацию о свободном месте на жёстких дисках. И сообщения тоже довольно часто смешивались.

Хотя, наврал. Локальный намного быстрее соображает. Но всё-равно что-то не то :thinking: Когда водишь мышкой по кнопкам на панели задач - в консоли сервера перемешиваются сообщения :dizzy_face: Это же ненормально! Процессор только на ~30% загружен! :man_facepalming:
И ещё сообщения перемешиваются, когда наводишь мышью куда-нибудь и появляется всплывающая подсказка :grin: :dizzy_face: :man_facepalming: :+1: :man_with_turban:

Наверное, так и должно быть :thinking: Сервер же не знает, что и сколько ему отправил клиент. Он просто получает всё что было передано. По-этому, первым сообщением надо передавать какую-нибудь структуру, содержащую тип и размер сообщения. А сразу следом передавать само сообщение. Видимо, так и работает :man_shrugging:
Но тогда не понятно, что будет если клиент начнёт слать серверу сообщения в многопотоке? :thinking: Например, передавать несколько файлов одновременно.

Так это можно сделать как отдельные соединения/клиенты.

Отдельные клиенты - ещё как-то понятно. А отдельные соединения это как? :thinking:

Это ж то же самое.

Просто несколько сокетов создать.