@Elnurhan

Почему строка разбивается при передаче по сокету?

Есть задача написать приложение (клиент-сервер) для передачи бинарных файлов. Написал следующую реализацию:

Client
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iostream>
#include <cstring>

#define IP "127.0.0.1"
#define PORT 27015
#define BUFSIZE 4096

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


void sendFile(SOCKET& client_socket, char* buf, char* filepath);


int main()
{
	WSADATA d;
	int result;
	result = WSAStartup(MAKEWORD(2,2), &d);
	if (result != 0)
	{
		std::cout << "Error WSAStartup: " << result << "\n";
		return 1;
	}

	SOCKET clientSocket = INVALID_SOCKET;
	clientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (clientSocket == INVALID_SOCKET)
	{
		std::cout << "Error socket(): " << WSAGetLastError() << "\n";
		WSACleanup();
		return 1;
	}

	sockaddr_in clientService;
	clientService.sin_family = AF_INET;
	clientService.sin_addr.s_addr = inet_addr(IP);
	clientService.sin_port = htons(PORT);

	result = connect(
				clientSocket,
				reinterpret_cast<SOCKADDR*>(&clientService),
				sizeof(clientService)
			);
	if (result != 0)
	{
		std::cout << "Connection error: " << WSAGetLastError() << "\n";
		WSACleanup();
		return 1;
	}

	const int N = 100;
	char buf[BUFSIZE], filepath[N];
	int r;
	do
	{
		std::cin >> filepath;
		sendFile(clientSocket, buf, filepath);

		if (r > 0)
			std::cout << "Receive: " << buf << "\n";
		else if (r == 0)
			std::cout << "Connection lost\n";
		else
			std::cout << "Recv error: " << WSAGetLastError() << "\n";
	} while (r > 0);

	closesocket(clientSocket);
	WSACleanup();
	return 0;
}

void sendFile(SOCKET& clientSocket, char* buf, char* filepath)
{
	FILE *f;
	f = fopen(filepath, "rb");

	int readed;

	if (f != 0)
	{
		while((readed = fread(buf, 1, sizeof(buf), f)) != 0)
		{
			send(clientSocket, (char*)buf, readed, 0);
		}
	}
}

Server:
#define _WIN32_WINNT 0x501

#include <winsock2.h>
#include <ws2tcpip.h>
#include <iostream>

#define DEFAULT_PORT "27015"
#define DEFAULT_BUFLEN 4096

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

int main()
{
    int iResult;
    WSAData d;

    // Данные для сокета
    iResult = WSAStartup(MAKEWORD(2,2), &d);
    if (iResult != 0)
    {
        std::cout << "Error at WSAStartup: " << iResult;
        return 1;
    }

    struct addrinfo *result = nullptr, hints;
    ZeroMemory(&hints, sizeof(hints));
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;
    hints.ai_flags = AI_PASSIVE;

    iResult = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result);
    if (iResult != 0)
    {
        std::cout << "getaddrinfo error: " << iResult;
        WSACleanup();
        return 1;
    }

    // Creating socket
    SOCKET listenSocket = INVALID_SOCKET;
    listenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
    if (listenSocket == INVALID_SOCKET)
    {
        std::cout << "Error at socket(): " << WSAGetLastError();
        freeaddrinfo(result);
        WSACleanup();
        return 1;
    }

    iResult = bind(listenSocket, result->ai_addr, result->ai_addrlen);
    if (iResult == SOCKET_ERROR)
    {
        std::cout << "Error at bind(): " << WSAGetLastError();
        freeaddrinfo(result);
        WSACleanup();
        return 1;
    }

    freeaddrinfo(result);

    if (listen(listenSocket, SOMAXCONN) == SOCKET_ERROR)
    {
        std::cout << "Listen error: " << WSAGetLastError();
        closesocket(listenSocket);
        WSACleanup();
        return 1;
    }

    SOCKET ClientSocket = INVALID_SOCKET;

    ClientSocket = accept(listenSocket, NULL, NULL);
    if (ClientSocket == INVALID_SOCKET)
    {
        std::cout << "Accept failed with error: " << WSAGetLastError();
        closesocket(listenSocket);
        WSACleanup();
        return 1;
    }

    // data trade
    char recvbuf[DEFAULT_BUFLEN];
    int iSendResult;
    int recvbuflen = DEFAULT_BUFLEN;
    
    do
    {
        iResult = recv(ClientSocket, recvbuf, sizeof(recvbuflen), 0);
        FILE *f = fopen("output.txt", "wb");
		fwrite(recvbuf, 1, iResult, f);
        if (iResult > 0)
        {
            std::cout << "Recv: " << recvbuf << "\n";

            iSendResult = send(ClientSocket, recvbuf, recvbuflen, 0);
            if (iSendResult == SOCKET_ERROR)
            {
                std::cout << "Send failed with error: " << WSAGetLastError();
                closesocket(ClientSocket);
                WSACleanup();
                return 1;
            }
        }
        else if (iResult == 0)
            std::cout << "Connection closed...\n";
        else
        {
            std::cout << "Recv error: " <<WSAGetLastError();
            closesocket(ClientSocket);
            return 1;
        }
    } while(iResult > 0);
}

Client должен ввести путь до файла, который нужно передать. В этой же директории у меня имеется файл Hello.txt, в котором содержится "Hello, world". При передаче данного файла, сервер получает данные следующим образом:

Recv: Hell

Recv: o, w

Recv: orld

И записывает orld в файл. С чем это связано?
  • Вопрос задан
  • 111 просмотров
Решения вопроса 1
gbg
@gbg Куратор тега C++
Любые ответы на любые вопросы
Очередной "я не читал мануалы, памагити" вопрос.

Читайте Снейдер, Й. "Эффективное программирование TCP/IP".

Кратко - функции send() и recv() не гарантируют, что будет записано или прочитано столько байт, сколько вы в них передали. Нужно анализировать возвращенное этими функциями значение - если оно 0 или < 0 - что-то поломалось и нужно анализировать errorno, в противном случае, вот столько байт и передалось.
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 1
maaGames
@maaGames
Погроммирую программы
В цикле чтения сокета файл каждый раз переоткрывается на запись, удаляя ранее записанное. Открой файл перед циклом и закрой после выхода из цикла.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы