android java and sockets



我编写客户机-服务器应用程序。客户端将运行在android和服务器上的纯java。我需要从客户端到服务器,然后从服务器到客户端交换数据。我的问题是,当我从客户端向服务器发送文件时,服务器卡住了从套接字接收数据。下面是我的代码:

client send file:

    Log.i("======", "sendToServer==============");
    OutputStream output = sk.getOutputStream();     
    String pathToOurFile = directory + File.separator + file;
    FileInputStream fileInputStream = new FileInputStream(pathToOurFile);
    byte[] buffer = new byte[sk.getSendBufferSize()];
    int bytesRead = 0;
    while((bytesRead = fileInputStream.read(buffer))>0)
    {
        output.write(buffer,0,bytesRead);
    }
    fileInputStream.close();

服务器获取数据:

    System.out.println("I get data");
    File file=null;
    InputStream input = sk.getInputStream();
    file = new File("C://protocolFIle/" + "temp.xml");
    FileOutputStream out = new FileOutputStream(file);
    byte[] buffer = new byte[sk.getReceiveBufferSize()];
    int bytesReceived = 0;
    while((bytesReceived = input.read(buffer))>0) {
        out.write(buffer,0,bytesReceived);
    }
    return file;

服务器联机

while((bytesReceived = input.read(buffer))>0) {
        out.write(buffer,0,bytesReceived);
    }

在服务器端。我想当我完成发送数据时,我需要在客户端发送某种信号。我知道我可以在客户端写"output.close()",然后服务器得到文件并打破while循环,但我需要这个输出在未来。有人知道我怎么能发送这个文件,服务器不卡住?也许我需要用另一种方式发送这个文件。谢谢你的帮助。

在客户端使用close()实际上不会有太大帮助,因为TCP/IP API方法(如close())作为在应用层发送消息结束信号的手段几乎是无用的。这样做的原因是,在客户端关闭TCP流并不会立即关闭到另一个客户端的整个TCP连接。这都与TCP的底层体系结构有关。

你需要做的是实现一个应用协议级别的方法来识别消息的长度,或者其他一些消息"帧"的方法。对您来说,最简单的事情可能是在消息的开头实现一个基本的头,它可以仅仅由代表U32消息长度的四个字节组成(仅举个例子)。

关于TCP,我要再次强调的一个非常重要的事情是,TCP协议本身,特别是像close()这样的调用,不提供可靠的方式来发送通过TCP发送的应用程序消息的开始/停止信号。TCP必须被认为是一个协议,因为这正是它的本质;因此,您还必须在该流中放置一些带有长度字段或帧字符的特殊报头,或一些其他方法,以指示接收端何时从.read()调用中读取足够的信息,以便它具有完整的消息。

我最近创建了一个Android应用程序,它使用TCP套接字与服务器c#应用程序进行通信,尽管我只是通过流发送简单且相对较短的消息,但我发现实现非常基本的消息帧和消息头是绝对必要的。

发送消息长度或结束的信号应该在应用协议级别完成。

同样,TCP连接应该被两端关闭的信号也应该在应用协议级别完成。当你搜索StackOverflow时,你经常会遇到很多人在检测TCP连接是否"已关闭"时遇到问题。问题在于,这是一种本质上错误的做事方式;任何语言的TCP套接字API都无法可靠地告诉您流已关闭。

如果你看一下HTTP,这是一个使用TCP套接字的流行协议的例子,你会看到这个协议使用指示长度的标头,指示连接应关闭的标志,以及消息是否分片,等等。

回到你的特定问题,你应该做的是在你的第一个write()在Android端是构建和编写一个简单的消息头,包含一个长度字段。在服务器端,第一个read()应该期望在文件负载开始之前看到这个头。然后使用该头文件中提供的长度来确定何时对整个文件进行read()

您可以稍后扩展您的头,以包含诸如CRC值之类的内容。

最新更新