java java中永久和持久的Socket连接

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/29685705/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 15:43:57  来源:igfitidea点击:

Permanent and persistent Socket connection in java

javasockets

提问by bvdb

I've created a client-server connection, something like a chat system. Previously I was using a whileloop on the client side, and it was waiting to read a message from the console every time (of course server has a whileloop as well to serve forever). But now, I'm trying to first create a connection at the beginning of the session, and then occasionally send a message during the session, so to maintain a permanent and persistent connection.

我创建了一个客户端-服务器连接,类似于聊天系统。以前我while在客户端使用一个循环,它每次都在等待从控制台读取消息(当然服务器也有一个while循环可以永远服务)。但是现在,我尝试在会话开始时先创建连接,然后在会话期间偶尔发送消息,以便保持永久和持久的连接。

Currently, without the whileloop, the client closes the connection and I don't know how to find a workaround.

目前,没有while循环,客户端关闭连接,我不知道如何找到解决方法。

Here is the client code:

这是客户端代码:

import java.net.*;
import java.io.*;
public class ControlClientTest {
    private Socket socket = null;
//  private BufferedReader console = null;  
    private DataOutputStream streamOut = null;

    public static void main(String args[]) throws InterruptedException {
        ControlClientTest client = null;

        String IP="127.0.0.1";
        client = new ControlClientTest(IP, 5555);
    }

    public ControlClientTest(String serverName, int serverPort) throws InterruptedException {
        System.out.println("Establishing connection. Please wait ...");
        try {
            socket = new Socket(serverName, serverPort);
            System.out.println("Connected: " + socket);
            start();
        } catch (UnknownHostException uhe) {
            System.out.println("Host unknown: " + uhe.getMessage());
        } catch (IOException ioe) {
            System.out.println("Unexpected exception: " + ioe.getMessage());
        }

        String line = "";
//      while (!line.equals(".bye")) {
            try {
                Thread.sleep(1000);
                //TODO get data from input
//              line = console.readLine();
                line="1";
                if(line.equals("1"))
                    line="1,123";
                streamOut.writeUTF(line);
                streamOut.flush();
            } catch (IOException ioe) {
                System.out.println("Sending error: " + ioe.getMessage());
            }
//      }
    }

    public void start() throws IOException {
//      console = new BufferedReader(new InputStreamReader(System.in));
        streamOut = new DataOutputStream(socket.getOutputStream());
    }

}

And here is the Server code:

这是服务器代码:

import java.awt.*;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

public class ControlServer {
    private Socket socket = null;
    private ServerSocket server = null;
    private DataInputStream streamIn = null;

    public static void main(String args[]) {

        ControlServer server = null;
        server = new ControlServer(5555);
    }

    public ControlServer(int port) {
        try {
            System.out
            .println("Binding to port " + port + ", please wait  ...");
            server = new ServerSocket(port);
            System.out.println("Server started: " + server);
            System.out.println("Waiting for a client ...");
            socket = server.accept();
            System.out.println("Client accepted: " + socket);
            open();
            boolean done = false;
            while (!done) {
                try {
                    String line = streamIn.readUTF();
                    // TODO get the data and do something
                    System.out.println(line);

                    done = line.equals(".bye");
                } catch (IOException ioe) {
                    done = true;
                }
            }
            close();
        } catch (IOException ioe) {
            System.out.println(ioe);
        }

    }


    public void open() throws IOException {
        streamIn = new DataInputStream(new BufferedInputStream(
                socket.getInputStream()));
    }
public void close() throws IOException {
    if (socket != null)
        socket.close();
    if (streamIn != null)
        streamIn.close();
}

}

回答by bvdb

I would like to summarize some good practices regarding the stability of TCP/IP connections which I apply on a daily basis.

我想总结一些关于我每天应用的 TCP/IP 连接稳定性的良好实践。

Good practice 1 : Built-in Keep-Alive

良好做法 1:内置 Keep-Alive

socket.setKeepAlive(true);

It automatically sends a signal after a period of inactivity and checks for a reply. The keep-alive interval is operating system dependent though, and has some shortcomings. But all by all, it could improve the stability of your connection.

它会在一段时间不活动后自动发送信号并检查回复。保持活动间隔虽然依赖于操作系统,但有一些缺点。但总而言之,它可以提高连接的稳定性。

Good practice 2 : SoTimeout

良好做法 2:SoTimeout

Whenver you perform a read(or readUTFin your case), your thread will actually block forever. In my experience this is bad practice for the following reasons: It's difficult to close your application. Just calling socket.close()is dirty.

每当您执行read(或readUTF在您的情况下)时,您的线程实际上将永远阻塞。根据我的经验,这是不好的做法,原因如下: 关闭应用程序很困难。只是打电话socket.close()是肮脏的。

A clean solution, is a simple read time-out (e.g. 200ms). You can do this with the setSoTimeoutmethod. When the read()method timeouts it will throw a SocketTimeoutException. (which is a subclass of IOException).

一个干净的解决方案是简单的读取超时(例如 200 毫秒)。你可以用这个setSoTimeout方法来做到这一点。当read()方法超时时,它会抛出一个SocketTimeoutException. (它是 的子类IOException)。

socket.setSoTimeout(timeoutInterval);

Here is an example to implement the loop. Please note the shutdowncondition. Just set it to true, and your thread will die peacefully.

下面是一个实现循环的例子。请注意shutdown条件。只需将其设置为 true,您的线程就会平静地消亡。

while (!shutdown)
{
  try
  {
    // some method that calls your read and parses the message.
    code = readData();
    if (code == null) continue; 
  }
  catch (SocketTimeoutException ste)
  {
    // A SocketTimeoutExc. is a simple read timeout, just ignore it.
    // other IOExceptions will not be stopped here.
  }
}

Good practice 3 : Tcp No-Delay

良好做法 3:Tcp 无延迟

Use the following setting when you are often interfacing small commands that need to be handled quickly.

当您经常连接需要快速处理的小命令时,请使用以下设置。

try
{
  socket.setTcpNoDelay(true);
}
catch (SocketException e)
{
}

Good practice 4 : A heartbeat

好习惯 4:心跳

Actually there are a lot of side scenario's that are not covered yet.

实际上,还有很多侧面场景尚未涵盖。

One of them for example are server applications that are designed to only communicate with 1 client at a time. Sometimes they accept connections and even accept messages, but never reply to them.

例如,其中之一是设计为一次仅与 1 个客户端通信的服务器应用程序。有时他们接受联系,甚至接受消息,但从不回复他们。

Another one: sometimes when you lose your connection it actually can take a long time before your OS notices this. Possibly due to the shortcomings described in good practice 3, but also in more complex network situations (e.g. using RS232-To-Ethernet converters, VMware servers, etc) this happens often.

另一个:有时当您失去连接时,实际上可能需要很长时间才能让您的操作系统注意到这一点。可能是由于良好实践 3 中描述的缺点,而且在更复杂的网络情况下(例如使用 RS232 到以太网转换器、VMware 服务器等),这种情况经常发生。

The solution here is to create a thread that sends a message every x seconds and then waits for a reply. (e.g. every 15 seconds). For this you need to create a second thread that just sends a message every 15 seconds. Secondly, you need to expand the code of good practice 2a little bit.

这里的解决方案是创建一个线程,每 x 秒发送一条消息,然后等待回复。(例如每 15 秒)。为此,您需要创建第二个线程,每 15 秒发送一条消息。其次,您需要稍微扩展良好实践 2的代码。

  try
  {
    code = readData();

    if (code == null) continue; 
    lastRead = System.currentTimeMillis();

    // whenever you receive the heart beat reply, just ignore it.
    if (MSG_HEARTBEAT.equals(code)) continue;

    // todo: handle other messages
  }
  catch (SocketTimeoutException ste)
  {
    // in a typical situation the soTimeout is about 200ms
    // the heartbeat interval is usually a couple of seconds.
    // and the heartbeat timeout interval a couple of seconds more.
    if ((heartbeatTimeoutInterval > 0) &&
        ((System.currentTimeMillis() - lastRead) > heartbeatTimeoutInterval))
    {
      // no reply to heartbeat received.
      // end the loop and perform a reconnect.
      break;
    }
  }

You need to decide if your client or server should send the message. That decision is not so important. But e.g. if your client sends the message, then your client will need an additional thread to send the message. Your server should send a reply when it receives the message. When your client receives the answer, it should just continue(i.e. see code above). And both parties should check: "how long has it been?" in a very similar way.

您需要决定您的客户端还是服务器应该发送消息。这个决定并不那么重要。但是例如,如果您的客户端发送消息,那么您的客户端将需要一个额外的线程来发送消息。您的服务器应该在收到消息时发送回复。当您的客户收到答案时,它应该只是continue(即见上面的代码)。并且双方都要核对:“已经多久了?” 以非常相似的方式。

回答by Sanj

Make the client socket connection wrapped around a thread. Use a blocking queueto wait for messages. There should only be a single sender queue throughout your application, so use a singleton pattern.

使客户端套接字连接环绕一个线程。使用阻塞队列来等待消息。整个应用程序中应该只有一个发送者队列,因此请使用单例模式。

e.g.

例如

QueueSingleton queue = QueueSingleton.getSenderQueue();
Message message = queue.take() // blocks thread
send(message); //send message to server

When you need to send a message to the server, you can use the blocking queue to send the message.

当需要向服务器发送消息时,可以使用阻塞队列来发送消息。

QueueSingleton queue = QueueSingleton.getSenderQueue();
queue.put(message)

The client thread will wake up and process the message.

客户端线程将唤醒并处理消息。

For maintaining the connection, use a timer task. This is special type of thread that calls a run method repetitively at specified periods. You can use this to post a message, a ping message, every so often.

为了保持连接,请使用计时器任务。这是一种特殊类型的线程,它在指定的时间段内重复调用 run 方法。您可以使用它来发布一条消息,一条 ping 消息,每隔一段时间。

For processing the received message, you could have another thread, waiting for messages on another blocking queue (receiver queue). The client thread will put the received message on this queue.

为了处理接收到的消息,您可以有另一个线程,等待另一个阻塞队列(接收器队列)上的消息。客户端线程会将接收到的消息放到这个队列中。

回答by NinePlanFailed

You could wrap a thread around the connection and have it periodically send a status to keep the line open, say every 30 seconds or whatever. Then, when it actually has data to send it would reset the keep alive to be 30 seconds after the last transmission. The status could be helpful to see if the client is still alive anyway, so at least it can be a useful ping.

您可以在连接周围包裹一个线程,并让它定期发送一个状态以保持线路打开,例如每 30 秒或其他时间。然后,当它实际有数据要发送时,它会将保持活动状态重置为最后一次传输后的 30 秒。该状态可能有助于查看客户端是否仍然活着,因此至少它可以是一个有用的 ping。

Also, you should change your server code, you appear to only handle one connection at the moment. You should loop and when a socket connection comes in spawn a thread to handle the client request and go back to listening. I may be reading to much into what may just be your test code, though.

此外,您应该更改服务器代码,您目前似乎只处理一个连接。您应该循环并在套接字连接进入时产生一个线程来处理客户端请求并返回侦听。不过,我可能正在深入了解可能只是您的测试代码的内容。