Java的插座和连接掉线 [英] Java Sockets and Dropped Connections

查看:95
本文介绍了Java的插座和连接掉线的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

什么是如果一个插座已被删除或不检测最合适的方式?还是一个包并实际上会发送?

我对苹果发送推送通知到iPhone通过苹果gatways(可以在GitHub上)库。客户需要打开一个套接字和发送每封邮件的二进制重新presentation;但遗憾的是苹果没有任何返回任何确认。该连接可以重复发送多封邮件也是如此。我使用的是简单的Java套接字连接。有关code是:

  Socket套接字=套接字(); //返回重用开放套接字或一个新的
。socket.getOutputStream()写(m.marshall());
socket.getOutputStream()平齐()。
logger.debug(消息\\{} \\已发送,M);

在某些情况下,如果当发送或右之前的消息的连接断开; Socket.getOutputStream()写()成功完成,虽然。我希望这是由于TCP窗口尚未用尽。

有没有一种方法,我可以肯定地告诉包是否真的在网络中有或没有?我尝试用以下两种解决方法:


  1. 有250ms的超时插入额外的 socket.getInputStream()。阅读()操作。这迫使当连接滴入失败读操作,但在其他挂起250毫秒


  2. 设置TCP发送缓冲区的大小(例如 Socket.setSendBufferSize())的消息二进制文件的大小。


这两种方法的工作,但他们显著降低服务质量;吞吐量100的消息进入/秒至约10消息/秒最多。

有什么建议?

更新:

由多个回答质疑所描述的可能性挑战。我构建了我所描述的行为的单位测试。退房单元的情况下在吉斯特273786

这两个单元测试有两个线程,服务器和客户端。而客户端没有抛出反正一个IOException发送数据的服务器关闭。下面是主要的方法:

 公共静态无效的主要(字串[] args)抛出的Throwable
    最终诠释PORT = 8005;
    最终诠释FIRST_BUF_SIZE = 5;    最终的Throwable [] =错误Throwable的新[1];
    最终旗语serverClosing =新信号量(0);
    最终旗语messageFlushed =新信号量(0);    类ServerThread继承Thread {
        公共无效的run(){
            尝试{
                ServerSocket的ssocket =新的ServerSocket(PORT);
                Socket套接字= ssocket.accept();
                InputStream的S = socket.getInputStream();
                s.read(新字节[FIRST_BUF_SIZE]);                messageFlushed.acquire();                socket.close();
                ssocket.close();
                的System.out.println(关闭套接字);                serverClosing.release();
            }赶上(Throwable的E){
                错误[0] = E;
            }
        }
    }    类ClientThread继承Thread {
        公共无效的run(){
            尝试{
                Socket套接字=新的Socket(localhost的,PORT);
                OutputStream的ST = socket.getOutputStream();
                st.write(新字节[FIRST_BUF_SIZE]);
                st.flush();                messageFlushed.release();
                serverClosing.acquire(1);                的System.out.println(写入新的数据包);                //而服务器已经发送更多的数据包
                //关闭的连接
                st.write(32);
                st.flush();
                st.close();                的System.out.println(已发送);
            }赶上(Throwable的E){
                错误[0] = E;
            }
        }
    }    线程线程1 =新ServerThread();
    线程线程2 =新ClientThread();    thread1.start();
    thread2.start();    thread1.join();
    thread2.join();    如果(误差[0]!= NULL)
        抛出错误[0];
    的System.out.println(没有任何错误运行);
}

[顺便说一句,我也有一个并发测试库,这使得安装好一点,更清晰。在检出的依据样本以及]

在运行,我得到以下的输出:

 关闭插座
写入新的数据包
写完
没有任何错误运行


解决方案

这没有多大帮助你,但在技术上你提出的解决方案都是不正确。 OutputStream.flush()和任何其他API调用你能想到的都不会做你的需要的。

唯一的便携和可靠的方法来确定是否一个数据包已被对方收到的是等待来自对等的确认。这种确认可以是一个实际响应,或优雅的套接字关闭。故事结束了 - 实在是没有别的办法了,这不是Java具体的 - 这是基本的网络编程

如果这不是一个持久连接 - 也就是说,如果你只是送东西,然后关闭连接 - 你这样做是你抓住所有的IOExceptions(其中任何指示错误)的方式,你执行正常套接字关闭

  1。 socket.shutdownOutput();
2.等待inputStream.read()返回-1,表示该邻居也已经停产其插座

What's the most appropriate way to detect if a socket has been dropped or not? Or whether a packet did actually get sent?

I have a library for sending Apple Push Notifications to iPhones through the Apple gatways (available on GitHub). Clients need to open a socket and send a binary representation of each message; but unfortunately Apple doesn't return any acknowledgement whatsoever. The connection can be reused to send multiple messages as well. I'm using the simple Java Socket connections. The relevant code is:

Socket socket = socket();   // returns an reused open socket, or a new one
socket.getOutputStream().write(m.marshall());
socket.getOutputStream().flush();
logger.debug("Message \"{}\" sent", m);

In some cases, if a connection is dropped while a message is sent or right before; Socket.getOutputStream().write() finishes successfully though. I expect it's due to the TCP window isn't exhausted yet.

Is there a way that I can tell for sure whether a packet actually got in the network or not? I experimented with the following two solutions:

  1. Insert an additional socket.getInputStream().read() operation with a 250ms timeout. This forces a read operation that fails when the connection was dropped, but hangs otherwise for 250ms.

  2. set the TCP sending buffer size (e.g. Socket.setSendBufferSize()) to the message binary size.

Both of the methods work, but they significantly degrade the quality of the service; throughput goes from a 100 messages/second to about 10 messages/second at most.

Any suggestions?

UPDATE:

Challenged by multiple answers questioning the possibility of the described. I constructed "unit" tests of the behavior I'm describing. Check out the unit cases at Gist 273786.

Both unit tests have two threads, a server and a client. The server closes while the client is sending data without an IOException thrown anyway. Here is the main method:

public static void main(String[] args) throws Throwable {
    final int PORT = 8005;
    final int FIRST_BUF_SIZE = 5;

    final Throwable[] errors = new Throwable[1];
    final Semaphore serverClosing = new Semaphore(0);
    final Semaphore messageFlushed = new Semaphore(0);

    class ServerThread extends Thread {
        public void run() {
            try {
                ServerSocket ssocket = new ServerSocket(PORT);
                Socket socket = ssocket.accept();
                InputStream s = socket.getInputStream();
                s.read(new byte[FIRST_BUF_SIZE]);

                messageFlushed.acquire();

                socket.close();
                ssocket.close();
                System.out.println("Closed socket");

                serverClosing.release();
            } catch (Throwable e) {
                errors[0] = e;
            }
        }
    }

    class ClientThread extends Thread {
        public void run() {
            try {
                Socket socket = new Socket("localhost", PORT);
                OutputStream st = socket.getOutputStream();
                st.write(new byte[FIRST_BUF_SIZE]);
                st.flush();

                messageFlushed.release();
                serverClosing.acquire(1);

                System.out.println("writing new packets");

                // sending more packets while server already
                // closed connection
                st.write(32);
                st.flush();
                st.close();

                System.out.println("Sent");
            } catch (Throwable e) {
                errors[0] = e;
            }
        }
    }

    Thread thread1 = new ServerThread();
    Thread thread2 = new ClientThread();

    thread1.start();
    thread2.start();

    thread1.join();
    thread2.join();

    if (errors[0] != null)
        throw errors[0];
    System.out.println("Run without any errors");
}

[Incidentally, I also have a concurrency testing library, that makes the setup a bit better and clearer. Checkout the sample at gist as well].

When run I get the following output:

Closed socket
writing new packets
Finished writing
Run without any errors

解决方案

This not be of much help to you, but technically both of your proposed solutions are incorrect. OutputStream.flush() and whatever else API calls you can think of are not going to do what you need.

The only portable and reliable way to determine if a packet has been received by the peer is to wait for a confirmation from the peer. This confirmation can either be an actual response, or a graceful socket shutdown. End of story - there really is no other way, and this not Java specific - it is fundamental network programming.

If this is not a persistent connection - that is, if you just send something and then close the connection - the way you do it is you catch all IOExceptions (any of them indicate an error) and you perform a graceful socket shutdown:

1. socket.shutdownOutput();
2. wait for inputStream.read() to return -1, indicating the peer has also shutdown its socket

这篇关于Java的插座和连接掉线的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆