Не могу получать сообщения с C # TCP-сервером и C ++ TCP-клиентом - PullRequest
2 голосов
/ 14 мая 2019

У меня есть C # TCP-сервер и C ++ TCP-клиент (используется winsock2). Я пытаюсь отправить несколько сообщений, но принимается только первое ...

C # код сервера

IPAddress raw = IPAddress.Parse("127.0.0.1");
IPEndPoint ip = new IPEndPoint(raw, 7777);

Socket client = new Socket(ip.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
client.Bind(ip);
client.Listen(69);

Console.WriteLine("Waiting for connection.");
Socket conn = client.Accept();
Console.WriteLine("Got connection");

string message;
int bytesRecv;
byte[] recvBuf = new byte[100];

bytesRecv = conn.Receive(recvBuf);
Console.WriteLine("Received message, bytes: " + bytesRecv);
message = System.Text.Encoding.UTF8.GetString(recvBuf);
Console.WriteLine(message);
Array.Clear(recvBuf, 0, recvBuf.Length);

bytesRecv = conn.Receive(recvBuf);
Console.WriteLine("Received message, bytes: " + bytesRecv);
message = System.Text.Encoding.UTF8.GetString(recvBuf);
Console.WriteLine(message);
Array.Clear(recvBuf, 0, recvBuf.Length);

bytesRecv = conn.Receive(recvBuf);
Console.WriteLine("Received message, bytes: " + bytesRecv);
message = System.Text.Encoding.UTF8.GetString(recvBuf);
Console.WriteLine(message);
Array.Clear(recvBuf, 0, recvBuf.Length);

bytesRecv = conn.Receive(recvBuf);
Console.WriteLine("Received message, bytes: " + bytesRecv);
message = System.Text.Encoding.UTF8.GetString(recvBuf);
Console.WriteLine(message);
Array.Clear(recvBuf, 0, recvBuf.Length);

Console.ReadLine();

А вот код C ++

#pragma once

#include "pch.h"
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <iostream>
#include <string>
#include<winsock2.h>
#include <WS2tcpip.h>

#pragma (lib, "ws2_32.lib")

using namespace std;

int main()
{
        WSAData wsaData;
        SOCKADDR_IN addr;
        int result = WSAStartup(MAKEWORD(2, 2), &wsaData);
        cout << "startup gave: " << result << endl;

        SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
        addr.sin_family = AF_INET;
        addr.sin_port = htons(7777);
        addr.sin_addr.s_addr = inet_addr("127.0.0.1");

        result = connect(s, (SOCKADDR*)&addr, sizeof(addr));
        cout << "Connect gave: " << result << endl;

        int count = 0;
        char sendBuf[1024] = "Hello";
        int sendResult;
        sendResult = send(s, sendBuf, sizeof(sendBuf), 0);
        sendResult = send(s, sendBuf, sizeof(sendBuf), 0);
        sendResult = send(s, sendBuf, sizeof(sendBuf), 0);
        sendResult = send(s, sendBuf, sizeof(sendBuf), 0);
}

Почему только первое сообщение получено? Сервер получает все байты в одном операторе recv, поэтому нет никаких причин (на мой взгляд), чтобы это не сработало. Спасибо!

100 Байт (отправлено полной суммы) получено в каждом из операторов получения сервера.

1 Ответ

2 голосов
/ 14 мая 2019

sizeof возвращает размер объекта. В этом случае не весь этот объект фактически используется, но sizeof не волнует. Всегда возвращает размер объекта. С

char sendBuf[1024] = "Hello";

sizeof(sendBuf) всегда 1024, независимо от того, сколько символов используется. Это значит

send(s, sendBuf, sizeof(sendBuf), 0);

отправляет "Hello\0" вместе с другими 1018 неопределенными байтами (уверен, что это будут все нулевые символы благодаря инициализации char sendBuf[1024] = "Hello";, но не может найти стандартную цитату, чтобы доказать это.) В sendbuf. Этот код повторяется 4 раза для общего количества отправленных 4096 байт. Мой .Net шаткий, но

byte[] recvBuf = new byte[100];
conn.Receive(recvBuf);

получит до 100 байтов. Это Привет и набор пустых символов. Вам нужно будет прочитать 10.24 из них, чтобы пройти через все дополнительные нули и найти следующий Hello .

так первый

message = System.Text.Encoding.UTF8.GetString(recvBuf);

удается распечатать Hello , но следующие три не видят ничего, кроме 100 байтов нулевых символов.

Решение:

Отправляйте только то, что вам нужно.

char sendBuf[1024] = "Hello";
sendResult = send(s, sendBuf, strlen(sendBuf), 0); // send all characters up to the 
                                                   // first null character

или

char sendBuf[] = "Hello"; // buffer will be sized to fit string(including null)
sendResult = send(s, sendBuf, sizeof(sendBuf), 0); 

Оговорка:

TCP / IP - это потоковый протокол. Он не имеет понятия отдельных сообщений и радостно смешивает отдельные вызовы на send в один пакет или несколько пакетов, как требуется. Это означает, что при первом получении могут быть возвращены все четыре Hello s.

Кроме того, получатель никогда не знает, что он собирается получить с данным чтением сокета TCP. Сообщение, возможно, было разделено на два пакета, чтобы максимизировать пропускную способность, и один из этих пакетов мог быть перенаправлен на большие расстояния и быть недоступным, когда клиент читает сообщение.

Вам потребуется установить и использовать протокол связи, чтобы дифференцировать ваши сообщения. При отправке строк я предпочитаю отправлять размер строки в целое число известного размера и порядковый номер, а затем отправлять строку. Получатель считывает длину (и проверяет, что он получил всю длину, прежде чем продолжить) строки, а затем считывает байты длины (снова не больше, не меньше), чтобы получить строку.

...