2012-03-12 3 views
0

Все,Не удается прочитать ответ от сервера Java в IOS клиента с помощью NSInputStream

Это мой первый раз размещения здесь - Я искал в течение нескольких часов в течение последних нескольких дней. Это не первое клиентское/серверное приложение, которое я сделал, и я совершенно не понимаю, что происходит.

У меня есть сервер Java (и он умеет правильно читать запрос от моего клиента iOS - он даже генерирует ответ и, кажется, отправляет его правильно, хотя данные не доступны для чтения на iOS-клиенте) :

public void run() { 
    BufferedReader in; 

    try { 
     in = new BufferedReader(new InputStreamReader(socket.getInputStream())); 

     OutputStream out_stream = this.socket.getOutputStream(); 
     StringBuilder request = new StringBuilder(); 
     String request_buffer; 

     while ((request_buffer = in.readLine()) != null) { 
      request.append(request_buffer); 
     } 
     out_stream.write(processRequest(request.toString()).getBytes()); 
     out_stream.close(); 

     socket.close(); 
    } catch (IOException e) { 
     e.printStackTrace(); 
    } 
} 

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

DEFAULT_HOST = '' 
DEFAULT_PORT = 2012 
RECEIVE_BUFFER_SIZE = 4096 

if __name__ == "__main__": 
    import sys, socket 

    port = DEFAULT_PORT 
    host = DEFAULT_HOST 
    if len(sys.argv) > 2: 
     host = sys.argv[1] 
     del sys.argv[1] 

    if len(sys.argv) == 2: 
      request = sys.argv[1] 
      print "Requesting: %s" % request 
      s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
      s.connect((host, port)) 
      s.send(request) 
      s.shutdown(socket.SHUT_WR) 

      response = "" 
      message = True 
      while message: 
       message = s.recv(RECEIVE_BUFFER_SIZE) 
       response += message 

    print "Response: %s" % response 

Перед отправкой клиенту IOS, я проверил его со следующим сервером Python (и клиент IOS может читать/писать, как и ожидалось .. это также работает с тестовым клиентом Python):

import os, sys 

DEFAULT_HOST = '' 
DEFAULT_PORT = 4150 

# Simple test server 
DEFAULT_SIZE = 4096 
import socket 

class Server: 

    def __init__(self, host, port, root, protocol, debug=True): 
     self.debug = debug 
     self.host = host 
     self.port = port 
     self.root = root 
     self.protocol = protocol 

    def __call__(self): 
     s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
     s.bind((self.host, self.port)) 
     s.listen(5) 

     while True: 
      try: 
       c = s.accept() 
       print "Connection: %s" % str(c) 

       request = c[0].recv(DEFAULT_SIZE) 
       print "Request: %s" % request 

       try: 
        response = "test" 
        if self.debug: 
         print "Response: %s" % response 

       except Exception as ex: 
        print "Error generating response: %s" % ex 

       if response: 
        c[0].send(response) 

       else: 
        c[0].send("3rr0rZ") 

       c[0].close() 
       print 


      except Exception as ex: 
       print ex 


if __name__ == "__main__": 
    host = DEFAULT_HOST 
    port = DEFAULT_PORT 

    args = sys.argv 

    # choose a port 
    if len(args) > 1 and args[1] == "-p": 
     if len(args) < 3: 
      raise Exception("Missing Argument for %s" % "-p") 
      port = int(args[2]) 
      del args[1:3] 
     else: 
      port = DEFAULT_PORT 

    # check if root specified 
    if len(args) > 1: 
     root = os.path.realpath(args[1]) 
     del args[1] 
    else: 
     root = os.getcwd() 

    print "Using:" 
    print "\tHost: %s" % host 
    print "\tPort: %s" % port 
    print "\tRoot: %s" % root 
    print 
    server = Server(host, port, root) 
    server() 

Очевидно, что это упрощенный сервер - проблема не в том, как генерируются запросы. Для немного больше фона, запросы и ответы - это строки JSON, хотя это не совсем актуально. Как упоминалось ранее, клиент Python может успешно запрашивать и получать ответ от серверов Java и Python. Клиент iOS может успешно отправлять запросы как на Python, так и на Java-серверы, но он может только читать ответ с сервера Python. Вот соответствующая часть клиента IOS:

- (NSData *)sendMessage:(NSData *)request 
{ 
    // Create low-level read/write stream objects 
    CFReadStreamRef readStream = nil; 
    CFWriteStreamRef writeStream = nil; 

    // Create high-level stream objects 
    NSInputStream *inputStream = nil; 
    NSOutputStream *outputStream = nil; 

    // Connect the read/write streams to a socket 
    CFStreamCreatePairWithSocketToHost(nil, (__bridge CFStringRef) self.address, self.port, &readStream, &writeStream); 

    // Create input/output streams for the raw read/write streams 
    if (readStream && writeStream) { 
     CFReadStreamSetProperty(readStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue); 
     CFWriteStreamSetProperty(writeStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue); 

     inputStream = (__bridge_transfer NSInputStream *)readStream; 
     [inputStream open]; 

     outputStream = (__bridge_transfer NSOutputStream *)writeStream; 
     [outputStream open]; 
    } 

    NSLog(@"Sending message to server: %@", request); 
    [outputStream write:[request bytes] maxLength:[request length]]; 
    [outputStream close]; 

    int size; 
    int buffer_size = 1024; 
    uint8_t buffer[buffer_size]; 
    NSMutableData *response = [NSMutableData dataWithLength:0]; 
    while (![inputStream hasBytesAvailable]); 

    NSLog(@"About to read"); 
    while ([inputStream streamStatus] == NSStreamStatusOpen) 
    {   
     if ([inputStream hasBytesAvailable] && (size = [inputStream read:buffer maxLength:buffer_size]) > 0) 
     { 
      NSLog(@"Reading response data"); 
      [response appendData:[NSData dataWithBytes:buffer length:size]]; 
     } 
    } 

    NSLog(@"\tResponse:%@", response); 
    return response; 
} 

При чтении с сервера Java, клиент IOS никогда не попадает мимо линии, которая гласит:

while (![inputStream hasBytesAvailable]); 

Я прочитал всю документацию, форум сообщения, вопросы и т. д., которые я мог найти для различных поисковых запросов, но ничего не помогло; Я надеюсь, что кто-то здесь может пролить свет на этот вопрос! Я опубликовал несколько упрощенную/сплющенную версию кода, который я использую, но, опять же, этого должно быть достаточно для установления контекста. Я буду счастливо выслать больше кода, если это необходимо, и я ценю любую помощь или понимание, что вы можете поделиться.

Я целенаправленно не использую NSStreamDelegate, и я не могу представить, что это проблема. Если бы я был, я бы предположил, что проблема просто превратится в NSStreamEventHasBytesAvailable, который никогда не произойдет.

+0

Мне любопытно, если вы отправляете и получаете строки JSON, почему вы решили написать сервер сокетов для обработки запросов вместо использования HTTP. – davidethell

+0

Попробуйте out_stream.flush() вместо out_stream.close(). socket.close() должен автоматически закрывать как входные, так и выходные потоки. – brettw

+0

@ davidethell Это только часть спецификации - она ​​должна быть настраиваемым сервером, обменивающимся через TCP. – jshort

ответ

0

Ваш сервер python и сервер Java не являются эквивалентными. В python вы читаете так:

request = c[0].recv(DEFAULT_SIZE) 

который считывает до 4096 байт в блоке. В то время как в Java вы используете:

while ((request_buffer = in.readLine()) != null) 

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

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

 
Length (2-bytes) | Data (length bytes) 
---------------------------------------------------------- 
0x000C   | Hello World! 

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

+0

Вы правы насчет неэквивалентности ... еще один кусок в головоломке, если быть уверенным. Мне больше всего интересна ваша заметка о закрытии сокета в Objective C. Я очень * новичок в этом языке, поэтому я должен проверить и убедиться, что я просто не наивный по поводу имени этого вызов; Я столкнулся с подобным в python, где закрытие одного конца сокета немного неочевидно. Я * очень * предпочитаю не прибегать к отправке размера в сообщениях запроса/ответа, хотя я понимаю, что это обычный и одобренный способ делать что-то. Я отчитаюсь о результатах. Спасибо за советы! – jshort

+0

еще одна вещь для добавления - на сервер java, хотя этот вызов может блокироваться, он проходит через этот цикл, генерирует ответ и, кажется, отправляет его. – jshort

+0

После гораздо большего чтения и исследований я не дальше. Я изменил сервер Python, чтобы больше походить на версию Java; похоже, что закрытие сокета в iOS-клиенте не ведет себя как «нормальный» сокет, закрывается. вызов recv() сервера Python должен блокироваться до тех пор, пока не будут получены данные или сокет отправки, это никогда не произойдет, когда iOS клиент закрывает сокет! По-видимому, что-то странное происходит, и [outputStream close] не создает событие закрытия сокета для сервера (Python или Java). Я даже пытался закрыть базовый CFWriteStream .. ничего. – jshort

2

Попробуйте мой код, он работает для меня.

NSInputStream *inputStream; 
    NSOutputStream *outputStream; 
    -(void) init{ 
     NSURL *website = [NSURL URLWithString:@"http://YOUR HOST"]; 

     CFReadStreamRef readStream; 
     CFWriteStreamRef writeStream; 
     CFStreamCreatePairWithSocketToHost(NULL,CFBridgingRetain([website host]),9876, &readStream, &writeStream); 

     inputStream = (__bridge_transfer NSInputStream *)readStream; 
     outputStream = (__bridge_transfer NSOutputStream *)writeStream; 
     [inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; 
     [outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; 
     [outputStream open]; 
     [inputStream open]; 

     CFReadStreamSetProperty(readStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue); 
     CFWriteStreamSetProperty(writeStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue); 
    } 

- (void) sendMessage { 

    // it is important to add "\n" to the end of the line 
    NSString *response = @"Say hello to Ukraine\n"; 
    NSData *data = [[NSData alloc] initWithData:[response dataUsingEncoding:NSUTF8StringEncoding]]; 
    int sent = [outputStream write:[data bytes] maxLength:[data length]]; 
    NSLog(@"bytes sent: %d",sent); 

    do{ 
     uint8_t buffer[1024]; 
     int bytes = [inputStream read:buffer maxLength:sizeof(buffer)]; 
     NSString *output = [[NSString alloc] initWithBytes:buffer length:bytes encoding:NSUTF8StringEncoding]; 
     NSLog(@"%@",output); 
    } while ([inputStream hasBytesAvailable]); 
} 

    public class ServerTest { 
     public static void main(String[] args) { 
      Thread thr = new Thread(new SocketThread()); 
      thr.start(); 
     } 
    } 

    class SocketThread implements Runnable { 

     @Override 
     public void run() { 
      try { 
       ServerSocket server = new ServerSocket(9876); 
       while (true) { 
        new SocketConnection(server.accept()).start(); 
       } 
      } catch (IOException e) { 
       e.printStackTrace(); 
      } 
     } 
    } 

    class SocketConnection extends Thread { 
     InputStream input; 
     PrintWriter output; 
     Socket socket; 

     public SocketConnection(Socket socket) { 
      super("Thread 1"); 
      this.socket = socket; 
      try { 
       input = socket.getInputStream(); 
       output = new PrintWriter(new OutputStreamWriter(
         socket.getOutputStream())); 
      } catch (IOException e) { 
       e.printStackTrace(); 
      } 
     } 

     @Override 
     public void run() { 
      try { 
       byte array[] = new byte[1024]; 
       while (true) { 
        do { 
         int readed = input.read(array); 
         System.out.println("readed == " + readed + " " 
           + new String(array).trim()); 
         String sendString = new String(
           "Hello Ukraine!".getBytes(), 
           Charset.forName("UTF-8")); 
         output.write(sendString); 
         output.flush(); 
        } while (input.available() != 0); 
       } 

     } catch (IOException e) { 
      e.printStackTrace(); 
     } 
    } 
}