在java网络编程中,即使客户端关闭,有没有办法让服务器端保持打开状态?

Cof*_*fee 8 java network-programming

假设我们在Java中有一个简单的Echo客户端/服务器对.据我所知,一旦插座的一侧断开,整个连接就消失了.

但是,如果我想要一个可以永远保持活着的服务器,即使客户端死亡,该怎么办呢?我希望能够恢复断开的连接.

回声服务器:

import java.net.Socket;
import java.net.ServerSocket;

public class EchoServer {

    public static void main(String[] args) throws Exception {

        // create socket
        int port = 4444;
        ServerSocket serverSocket = new ServerSocket(port);
        System.err.println("Started server on port " + port);

        // repeatedly wait for connections, and process
        while (true) {

            // a "blocking" call which waits until a connection is requested
            Socket clientSocket = serverSocket.accept();
            System.err.println("Accepted connection from client");

            // open up IO streams
            In  in  = new In (clientSocket);
            Out out = new Out(clientSocket);

            // waits for data and reads it in until connection dies
            // readLine() blocks until the server receives a new line from client
            String s;
            while ((s = in.readLine()) != null) {
                out.println(s);
            }

            // close IO streams, then socket
            System.err.println("Closing connection with client");
            out.close();
            in.close();
            clientSocket.close();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

任何提示赞赏谢谢

Dav*_*ock 11

您缺少的是"会话"的概念.想想服务器的位置,当一些位到达"线路"时.怎么办这些?使用TCP/IP,线路上已经存在一些信息,即:

  • src addr
  • src端口
  • dest addr
  • 目的港
  • 和消息有效负载本身
  • 和其他一些东西(比如序列计数器,以确保'数据包'在传输过程中没有混乱)

服务器的操作系统使用src/dest addr/port信息来判断这是否是"正在进行的对话".主要是它认为dest port(因为消息已经通过互联网和防火墙发送到机器本身)来决定是否是你的Java程序在'dest端口'上监听.但是,它使用整个src-addr/src-port/dest-addr/dest-port尝试按照发送方发送的顺序将有效负载传送到您的程序(这可能不是它们到达的顺序,因为干预互联网).请注意,单个"消息"实际上可能会拆分为多个数据包.您的操作系统的TCP/IP堆栈正在为您做一些工作.

但是,请注意,为了代表您执行此功能,操作系统必须投入一些资源来"跟踪TCP/IP会话的状态".至少,对于每组port/addr src/dest,需要有一个最后收到的'数据包'的计数器,一些缓冲空间来保存数据包直到你的程序准备好消耗它们,所以向前.

现在,TCP/IP堆栈实现者所面临的问题是"我应该在多长时间内保持这种状态"?足够10秒?20分钟?在某些时候,在没有从客户端听到一段时间后,会话必须"超时".如果序列中有更多的数据包要发送,并且客户端再次开始发送它们,那么服务器必须能够说"对不起,你发送了一些先前消息的数据包234,但因为我没有收到来自你有一段时间,我丢掉了1-233包.我们可以重新开始吗?"

所以从这个意义上讲,没有办法阻止客户端"断开连接".当然,如果客户端恢复并发送更多数据,您可以继续监听套接字.但是你和你的客户需要一种方法来"选择我们离开的地方".

在HTTP中,这是使用"会话cookie"实现的 - 会话cookie是服务器提供给客户端的长唯一字符串,客户端会根据每个新请求重新发送(无论是否在相同的TCP级会话上发生).这是一个"应用程序级会话",其生命周期比TCP会话的生命周期长.

由于您正在编写应用程序,并且听起来您对客户端和服务器之间的相互说明("协议")有一定程度的控制权,因此您可以选择更多关于如何就什么是会话,如果客户'离开'(会话超时),以及双方如何恢复并"从我们中断的地方继续"(重新建立会话)如何处理事情.你不要忘记身份验证!

正如其他人所说,这在很大程度上取决于你想要实现的目标.


小智 5

当你想创建一个服务器和多个客户端时,我们通常做的是创建一个负责服务器和其中一个客户端之间通信的线程,所以每次客户端连接到服务器时,服务器都会启动一个新的线程,会给它相应的套接字.

代码应该是这样的:

import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;

public class EchoServer {

    public static void main(String[] args) throws Exception {

        ArrayList<ClientHandler> clientHandlersList = new ArrayList<ClientHandler>();

        // create socket
        int port = 4444;
        boolean isOver = false;
        ServerSocket serverSocket = new ServerSocket(port);
        System.err.println("Started server on port " + port);

        // repeatedly wait for connections, and process
        while (!isOver) {

            // a "blocking" call which waits until a connection is requested
            Socket clientSocket = serverSocket.accept();
            System.err.println("Accepted connection from client");

            ClientHandler handler = new ClientHandler(clientSocket);
            handler.start();
            clientHandlersList.add(handler);
        }

        serverSocket.close();
    }
}
Run Code Online (Sandbox Code Playgroud)

和ClientHandler类:

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;


public class ClientHandler extends Thread {

    private Socket socket;
    private ObjectInputStream in;
    private ObjectOutputStream out;
    private boolean disconnect = false;


    public ClientHandler(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        try {
            in = new ObjectInputStream(socket.getInputStream());
            out = new ObjectOutputStream(socket.getOutputStream());
            while(!disconnect){
                Object message = readMessage();

                //DO something
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public Object readMessage(){
        try {
            Object obj = in.readObject();
            return obj;

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return null;
    }

    public void writeMessage(Object obj){
        try {
            out.writeObject(obj);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void disconnect(){
        try {
            disconnect = false;
            out.close();
            in.close();
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这是一个值得帮助的答案(+1).这是如何处理到达套接字的多个请求的公平示例.它没有*直接*解决'我们如何从中断的地方拿起'的问题,但为这样做提供了一个良好的框架.NB.而不是对`Thread`进行子类化,实现`Runnable`并执行`(new Thread(myRunnable)).start()`.NB(2)."每个请求的线程"设计通常被认为不能很好地"扩展"(即> 100个并发请求),还有其他设计.(参见NodeJS的意见,vert.x代表Java).NB3.HTTP通过防火墙,有'Cookies'. (2认同)