Не удается прочитать ответ с Java-сервера в клиенте iOS с помощью NSInputStream - PullRequest
0 голосов
/ 12 марта 2012

All

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

У меня есть сервер 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-функция вызывается в результате порождения экземпляра класса, членом которого она является, и инициализируется результатом метода accept () объекта 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, который никогда не случится.

Ответы [ 2 ]

2 голосов
/ 08 октября 2013

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

    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();
        }
    }
}
0 голосов
/ 12 марта 2012

Ваш Python-сервер и Java-сервер не эквивалентны.В Python вы читаете так:

request = c[0].recv(DEFAULT_SIZE)

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

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

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

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

...