Java TCP Client-send заблокирован? - PullRequest
1 голос
/ 29 апреля 2011

Я пишу TCP-клиент Java, который общается с C-сервером.Я должен чередовать отправку и получение между двумя.Вот мой код.

  1. Сервер отправляет клиенту (java) длину двоичного msg (len)
  2. Клиент отправляет строку "ok"
  3. Сервер отправляет двоичный файл, а клиент выделяет байтовый массив байтов len для его получения.
  4. Он снова отправляет обратно "ok".

шаг 1. работает.Я получаю значение "лен".Однако Клиент получает «отправка заблокирована», и сервер ожидает получения данных.

Может кто-нибудь взглянуть.

В блоке try я определил:

            Socket echoSocket = new Socket("192.168.178.20",2400);
            OutputStream os = echoSocket.getOutputStream();       
            InputStream ins = echoSocket.getInputStream();
            BufferedReader br = new BufferedReader(new InputStreamReader(ins));

            String fromPU = null;


            if( (fromPU = br.readLine()) !=  null){
            System.out.println("Pu returns  as="+fromPU);  

            len = Integer.parseInt(fromPU.trim());
            System.out.println("value of len from PU="+len);

            byte[] str = "Ok\n".getBytes();
            os.write(str, 0, str.length);
            os.flush();

            byte[] buffer = new byte[len];
            int bytes;
            StringBuilder curMsg = new StringBuilder();
            bytes =ins.read(buffer);
            System.out.println("bytes="+bytes); 
            curMsg.append(new String(buffer, 0, bytes));            
            System.out.println("ciphertext="+curMsg); 
                    os.write(str, 0, str.length);
            os.flush();
            }

ОБНОВЛЕНО:

Вот мой код.На данный момент нет блокировок recv или send с обеих сторон.Однако, как с Buffered Reader, так и с DataInput Stream, я не могу отправить сообщение msg.На стороне сервера я получаю большое количество байтов вместо 2 байтов для ok.

            Socket echoSocket = new Socket("192.168.178.20",2400);
            OutputStream os = echoSocket.getOutputStream();   
            InputStream ins = echoSocket.getInputStream();
            BufferedReader br = new BufferedReader(new InputStreamReader(ins));
            DataInputStream dis = new DataInputStream(ins);
            DataOutputStream dos = new DataOutputStream(os);
            if( (fromPU = dis.readLine()) !=  null){
            //if( (fromPU = br.readLine()) !=  null){
            System.out.println("PU Server returns length as="+fromPU);      
            len = Integer.parseInt(fromPU.trim());
            byte[] str = "Ok".getBytes();
            System.out.println("str.length="+str.length);
            dos.writeInt(str.length);
            if (str.length > 0) {
                    dos.write(str, 0, str.length);
                 System.out.println("sent ok");
            }
            byte[] buffer = new byte[len];
            int bytes;
            StringBuilder curMsg = new StringBuilder();
            bytes =ins.read(buffer);
            System.out.println("bytes="+bytes); 
                curMsg.append(new String(buffer, 0, bytes));            
                System.out.println("binarytext="+curMsg); 

            dos.writeInt(str.length);
            if (str.length > 0) {
                    dos.write(str, 0, str.length);
                 System.out.println("sent ok");
            }

Ответы [ 2 ]

3 голосов
/ 29 апреля 2011

Использование BufferedReader вокруг потока, а затем попытка чтения двоичных данных из потока - плохая идея.Я не удивлюсь, если сервер фактически отправил все данные за один раз, а BufferedReader прочитал двоичные данные, а также строку, которую они возвратили.

Вы контролируете протокол?Если это так, я предлагаю вам изменить его так, чтобы он передавал длину данных как двоичный (например, фиксированные 4 байта), чтобы вам не приходилось разбираться, как переключаться между текстовым и двоичным (что по сутиболь).

Если вы не можете сделать это, вам, вероятно, нужно просто читать байт за раз, чтобы начать, пока не увидите байт, представляющий \n, а затем преобразовать то, что вычитать в текст, анализировать его, а затем читать остальное как кусок.Это немного неэффективно (чтение байта за раз вместо чтения буфера за раз), но я думаю, что объем данных, читаемых в этот момент, довольно мал.

0 голосов
/ 29 апреля 2011

Несколько мыслей:

        len = Integer.parseInt(fromPU.trim());

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

        BufferedReader br = new BufferedReader(new InputStreamReader(ins));
        /* ... */
        bytes =ins.read(buffer);

Может быть ваш BufferedReader засосал слишком много данных? (Сервер ждет для Ok, прежде чем продолжить?) Вы уверены, что вам разрешено читать из базового объекта InputStreamReader после присоединения объекта BufferedReader?

Обратите внимание, что TCP может свободно доставлять ваши данные в виде десятибайтовых блоков в течение следующих двух недель :) - потому что инкапсуляция, различное оборудование и т. Д. Очень затрудняет определение размера пакетов, которые в конечном итоге будут использоваться между два пира, большинство приложений, которые ищут определенный объем данных, вместо этого будут заполнять свои буферы, используя код, похожий на этот (украдено из Advanced Programming в Unix Environment, отличная книга; жаль, что код на C и ваш код на Java, но принцип тот же):

ssize_t             /* Read "n" bytes from a descriptor  */
readn(int fd, void *ptr, size_t n)
{
    size_t      nleft;
    ssize_t     nread;

    nleft = n;
    while (nleft > 0) {
        if ((nread = read(fd, ptr, nleft)) < 0) {
            if (nleft == n)
                return(-1); /* error, return -1 */
            else
                break;      /* error, return amount read so far */
        } else if (nread == 0) {
            break;          /* EOF */
        }
        nleft -= nread;
        ptr   += nread;
    }
    return(n - nleft);      /* return >= 0 */
}

Смысл в том, что для заполнения буфера может потребоваться один, десять или сто вызовов read(), а ваш код должен быть устойчивым к небольшим изменениям в сетевых возможностях.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...