почему Android получает только 2896 байт на c # tcp сокете?

#android #sockets #tcp

#Android #сокеты #tcp

Вопрос:

c # Использует tcp сокет для отправки сообщения на Android:

 string data = "my message....";
byte[] msg = Encoding.UTF8.GetBytes(data);
//for example msg Length is 5210 bytes
client.socket.SendBufferSize = 500000;
socket.Send(msg, msg.Length, SocketFlags.None);
  

Android получает сообщение со стороны сервера c #:

     socket = new Socket(ServerIP, ServerPort);
    socket.setReceiveBufferSize(500000);
    isReceive = true;
    receiveThread = new ReceiveThread(socket);
    receiveThread.start();

private class ReceiveThread extends Thread{
private InputStream inStream = null;
ReceiveThread(Socket socket){
inStream = socket.getInputStream();
}
@Override
public void run(){
while(isReceive){
byte[] buffer = new byte[99999];
try {
//only receive 2896 bytes?
int size = inStream.read(buffer);
} catch (IOException e) {
unConnSocket();
}
}
}
}
  

почему размер получает только 2896 байт?

Ответ №1:

Ваш код Android не имеет возможности узнать, сколько байт отправляет код C #. inStream.read() считывает только те байты, которые в данный момент доступны в сокете. Вы должны заставить код C # отправлять длину строки перед отправкой строковых данных, чтобы код Android знал, сколько байтов ожидать, например:

 string data = "my message....";
byte[] dataBytes = Encoding.UTF8.GetBytes(data);
int dataLen = IPAddress.HostToNetworkOrder(dataBytes.Length);
byte[] dataLenBytes = BitConverter.GetBytes(dataLen);
socket.Send(dataLenBytes);
socket.Send(dataBytes);
  

 private class ReceiveThread extends Thread
{
    private DataInputStream inStream = null;

    ReceiveThread(Socket socket)
    {
        inStream = new DataInputStream(socket.getInputStream());
    }

    @Override
    public void run()
    {
        while (isReceive)
        {
            try
            {
                String s;
                int size = inStream.readInt();
                if (size > 0)
                {
                    byte[] buffer = new byte[size];
                    inStream.readFully(buffer); 
                    s = new String(buffer, "UTF-8");
                }
                else
                    s = "";

                // use s as needed ...
            }
            catch (IOException e)
            {
                unConnSocket();
            }
        }
    }
}
  

Ответ №2:

Потому что TCP — это протокол потока байтов, и он не обязан доставлять вам более одного байта за раз.

Вы должны выполнить цикл.

Я цитирую из Linux man recv (2):

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

Комментарии:

1. На самом деле для TCP это протокол на основе сегментного потока, дополнительно ограниченный MTU…. Стек получает Ethernet, а затем анализирует IP в MTU точно так же, как UDP… Стек анализирует сегменты, и по завершении приложение имеет право получать данные от сегмента до целого сегмента или все, что доступно в буфере приложения. Если в TCP доступно несколько сегментов, то вы можете и обычно будете получать все данные во всех восстановленных сегментах, в отличие от UDP, где вы можете получать только один восстановленный сегмент.

2. @Jay Это неверсально определено как протокол байтового потока. Под ним есть сегменты, но они не указаны для соблюдения ни функциями отправки, ни функциями получения, и это объяснение поведения, которое наблюдает OP. В вашем комментарии нет ничего, что делает недействительным этот ответ.

3. Тогда как бы вы получили только 1 байт, если segment имеет более 1 байта, и вы вызвали recv с количеством, большим 1? Должен был быть сегмент или повторная передача, который соответствует 1 байту, чтобы прикладной уровень получил 1 байт… Я знаю, потому что я часто использую пакеты размером 0, чтобы указывать различные вещи отправителям и получателям устаревших протоколов, где они не могут получить новые данные протокола. Вы также могли бы использовать данные вне полосы, но семантика такая же, поскольку заголовок TCP все еще передается. Как это не делает недействительным ваш ответ?

4. @Jay Я не говорил, что вы получите только один байт, если сегмент содержит более одного байта. Не вкладывайте слов в мои уста. Я имею в виду контракт recv (2) , в котором говорится именно то, что я изложил выше. Вы не можете отправлять (и, следовательно, не получать) сегменты нулевой длины в TCP, поэтому я не вижу значимости, не говоря уже о том, как это может сделать недействительным мой ответ. Вы будете тщетно искать инструкцию в текстовых книгах. «TCP — это сегментно-ориентированный протокол», и вы везде найдете утверждение «TCP — это байт-ориентированный протокол». Это не спорное утверждение.

5. @ EJP, `не обязан доставлять вам более одного байта за раз` он не обязан предоставлять вам что-либо большее, чем вы запрашиваете с помощью receive… то, что вы получаете, — это совершенно другая история. (Вы также можете получить get -1 ) Наконец, ДА, вы можете отправлять сегменты TCP размером 0. В конечном итоге получатель видит сегмент длиной 0 в зависимости от используемого стека и того, как он это обрабатывает. Вы также можете использовать данные TCP вне полосы, но это отдельный поток данных. Так что, насчет цитаты.. может быть, вам следует прекратить читать о программировании и начать собственно программировать…