0

全て、

ここに投稿するのは初めてです。ここ数日で数時間検索しました。これは私が作成した最初のクライアント/サーバー アプリケーションではありません。何が問題なのか完全に困惑しています。

私は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 の accept() メソッドの結果で初期化されます。ここではすべて問題なく動作しているようです。次の 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 に変化することはないと思います。

4

2 に答える 2

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();
        }
    }
}
于 2013-10-08T09:56:57.843 に答える
0

Python サーバーと Java サーバーは同等ではありません。Python では、次のように読みます。

request = c[0].recv(DEFAULT_SIZE)

ブロックで最大4096バイトを読み取っています。Javaでは次を使用しています:

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

in.readLine() は、行末になるまでブロックするか、ファイルの終わりになるまでブロックします。クライアントがソケットをシャットダウンするまで、これはスタックする可能性があります。クライアントが出力ストリームを正しくシャットダウンしていますか? 私は Objective C に精通していませんが、出力ストリームを閉じることは、ソケットの書き込み側をシャットダウンすることと同じではないかもしれません。

あなたがワイヤーの両側を担当している場合、クライアントに最初に長さヘッダー (2 バイト) を書き込んでから、ちょうどその量のデータを書き込んでもらいます。次に、サーバーは 2 バイトを読み取り、残りのデータの長さを計算し、正確にその長さを読み取ることができます。

長さ (2 バイト) | データ (長さバイト)
-------------------------------------------------- --------
0x000C | 「こんにちは世界」

常に長さの後にデータを送信することで、ソケットを簡単にシャットダウンせずに複数のメッセージを送信することもできます。

于 2012-03-12T13:17:58.097 に答える