【问题标题】:how to correctly close SocketChannel java in JAVA?如何在 JAVA 中正确关闭 SocketChannel java?
【发布时间】:2013-07-17 08:28:24
【问题描述】:

我正在用 Java 开发多线程非阻塞 tcp 服务器和客户端。我在服务器意识到客户端套接字已关闭时遇到问题。对他来说,一切都是开放的。这是我的代码的代码和输出:

服务器:

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package androidnonblockingnewesttest;

import globalvariables.*;
import interface_package.*;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.net.InetSocketAddress;
import java.nio.channels.Channels;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;

/**
 *
 * @author wsserver
 */
public class AndroidNonBlockingNewestTest {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        //Prevzemi staticen interface
        GlobalVariables.sinterface = new ServerInterface();
        GlobalVariables.sinterface.show();

        //POSTAVI SERVER
        //INFINITE LOOP
        //NE MOZAM POSLE OVA DA POVIKUVAM NISTO BIDEJKI NEMA DA STIGNE DO NEGO PORADI while(true)
        int port = GlobalVariables.portNo;

        ServerSocketChannel serverSocket = null;
        try {
            serverSocket = ServerSocketChannel.open();
            serverSocket.socket().bind(new InetSocketAddress(port));
            serverSocket.configureBlocking(false);
            System.out.println("Server has started listening on port " + port);
            GlobalVariables.sinterface.setServerStatus("Server has started listening on port " + port);
            GlobalVariables.sinterface.setReceivedText("Server has started listening on port " + port);
        } catch (IOException e) {
            System.out.println("Error: Cannot listen on port " + port + " : " + e);
            GlobalVariables.sinterface.setServerStatus("Error: Cannot listen on port " + port + " : " + e);
            GlobalVariables.sinterface.setReceivedText("Error: Cannot listen on port " + port + " : " + e);
            System.exit(1);
        }
        while (true) // infinite loop - loops once for each client
        {
            SocketChannel clientSocket = null;
            try {
                clientSocket = serverSocket.accept(); //waits here (forever) until a client connects

                if (clientSocket == null) {
                    // No connections came .
                } else {
                    clientSocket.configureBlocking(false);
                    // You got a connection. Do something
                    System.out.println("Server has just accepted socket connection from a client");
                    GlobalVariables.sinterface.setServerStatus("Server has just accepted socket connection from a client");
                    GlobalVariables.sinterface.setReceivedText("Server has just accepted socket connection from a client");

                    // Create the Handle Connection object - our new thread object - only create it
                    ThreadedHandleConnection con = new ThreadedHandleConnection(clientSocket);

                    if (con == null) //If it failed send and error message
                    {
                        try {
                            ObjectOutputStream os = new ObjectOutputStream(Channels.newOutputStream(clientSocket));
                            os.writeObject("error: Cannot open socket thread");
                            GlobalVariables.sinterface.setReceivedText("error: Cannot open socket thread");
                            os.flush();
                            os.close();
                        } catch (Exception ex) //failed to even send an error message
                        {
                            System.out.println("Cannot send error back to client: " + ex);
                            GlobalVariables.sinterface.setServerStatus("Cannot send error back to client: " + ex);
                            GlobalVariables.sinterface.setReceivedText("Cannot send error back to client: " + ex);
                        }
                    } else {
                        con.start();
                    } // otherwise we have not failed to create the HandleConnection object
                    // start this thread now
                }
            } catch (IOException e) {
                System.out.println("Accept failed: " + e);
                GlobalVariables.sinterface.setServerStatus("Accept failed: " + e);
                GlobalVariables.sinterface.setReceivedText("Accept failed: " + e);
                break;
            }
        }

        try // do not get here at the moment 
        {
            System.out.println("Closing server socket.");
            GlobalVariables.sinterface.setServerStatus("Closing server socket.");
            GlobalVariables.sinterface.setReceivedText("Closing server socket.");
            serverSocket.close();
        } catch (IOException e) {
            System.err.println("Could not close server socket. " + e.getMessage());
            GlobalVariables.sinterface.setServerStatus("Could not close server socket. " + e.getMessage());
            GlobalVariables.sinterface.setReceivedText("Could not close server socket. " + e.getMessage());
        }
    }
}

连接处理程序:

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package androidnonblockingnewesttest;

import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;

/**
 *
 * @author wsserver
 */
public class ThreadedHandleConnection extends Thread {

    private SocketChannel clientSocket;
    Charset charset = Charset.forName("ISO-8859-1");
    CharsetEncoder encoder = charset.newEncoder();
    CharsetDecoder decoder = charset.newDecoder();
    ByteBuffer buffer = ByteBuffer.allocate(1024);

    // The constructor for the connecton handler
    public ThreadedHandleConnection(SocketChannel clientSocket) {
        try {
            this.clientSocket = clientSocket;
            clientSocket.configureBlocking(false);
        } catch (IOException ex) {
            System.out.println("SocketChannel blocking exception: " + ex.getMessage());
        }
    }

    // The main thread execution method 
    @Override
    public void run() {
        while (!clientSocket.socket().isClosed()) {

            try {

                System.out.println("Socket status:clientSocket.isConnected():" + clientSocket.isConnected());
                System.out.println("Socket status:clientSocket.finishConnect():" + clientSocket.finishConnect());
                System.out.println("Socket status:clientSocket.isOpen():" + clientSocket.isOpen());
                System.out.println("Socket status:clientSocket.socket().isClosed():" + clientSocket.socket().isClosed());
                System.out.println("Socket status:clientSocket.socket().isClosed():" + clientSocket.socket().isConnected());


                int bytesread = clientSocket.read(buffer);
                if (!(bytesread > 0)) {
                    System.out.println("Nothing to read");
                } else {
                    buffer.flip();

                    String request = decoder.decode(buffer).toString();

                    System.out.println("Request:" + request);
                    buffer.clear();
                }
                Thread.sleep(3000);
            } catch (IOException ex) {
                System.out.println("Socket run exception " + ex.getMessage());
            } catch (Exception ex) {
                System.out.println("Exception " + ex.getMessage());
            }
        }
    }
}

客户:

package androidnonblockingnewesttest;

import globalvariables.GlobalVariables;
import java.io.*;
import java.net.*;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
/**
 *
 * @author wsserver
 */
public class ThreadedTCPClient {

    private SocketChannel socket = null;
    String serverIP = GlobalVariables.serverIP;
    int port = GlobalVariables.portNo;

    Charset charset = Charset.forName("ISO-8859-1");
    CharsetEncoder encoder = charset.newEncoder();
    CharsetDecoder decoder = charset.newDecoder();
    ByteBuffer buffer = ByteBuffer.allocate(1024);

    // the constructor expects the IP address of the server
    public ThreadedTCPClient() {
        if (!connectToServer()) {
            System.out.println("Cannot open socket connection...");
        }
    }

    private boolean connectToServer() {
        try // open a new socket to port:  and create streams
        {
            this.socket = SocketChannel.open();
            this.socket.configureBlocking(false);
            this.socket.connect(new InetSocketAddress("localhost", port));
            while(!this.socket.finishConnect()){

            }
            System.out.print("Connected to Server\n");

        } catch (Exception ex) {
            System.out.print("Failed to Connect to Server\n" + ex.toString());
            System.out.println(ex.toString());
            return false;
        }
        return true;
    }

    public void sendTest() {
        try {
            buffer = encoder.encode(CharBuffer.wrap("Hellow from client"));
            socket.write(buffer);
            buffer.clear();
        } catch (IOException ex) {
            System.out.println("Write exception: "+ex.getMessage());
        }
    }

    public void closeConnection(){
        try{
            this.socket.socket().close();
            this.socket.close();
            System.out.println("Close");
        }catch(Exception ex){
            System.out.print("Failed to close connection to Server\n" + ex.toString());
            System.out.println(ex.toString());
        }
    }

    public static void main(String args[]) {

        ThreadedTCPClient theApp = new ThreadedTCPClient();
        try {
            Thread.sleep(10000);
            theApp.sendTest();
            theApp.closeConnection();
        } catch (Exception ex) {
            System.out.println(ex.toString());
        }

    }
}

这是我的输出。 客户端输出:

Connected to Server
Close

来自服务器的输出:

Server has started listening on port 10001
Server has just accepted socket connection from a client
Socket status:clientSocket.isConnected():true
Socket status:clientSocket.finishConnect():true
Socket status:clientSocket.isOpen():true
Socket status:clientSocket.socket().isClosed():false
Socket status:clientSocket.socket().isClosed():true
Nothing to read
Socket status:clientSocket.isConnected():true
Socket status:clientSocket.finishConnect():true
Socket status:clientSocket.isOpen():true
Socket status:clientSocket.socket().isClosed():false
Socket status:clientSocket.socket().isClosed():true
Nothing to read
...
Socket status:clientSocket.isConnected():true
Socket status:clientSocket.finishConnect():true
Socket status:clientSocket.isOpen():true
Socket status:clientSocket.socket().isClosed():false
Socket status:clientSocket.socket().isClosed():true
Request:Hellow from client
Socket status:clientSocket.isConnected():true
Socket status:clientSocket.finishConnect():true
Socket status:clientSocket.isOpen():true
Socket status:clientSocket.socket().isClosed():false
Socket status:clientSocket.socket().isClosed():true
Nothing to read
...

从客户端关闭连接后,所有 SocketChannel 和 SocketChannel.socket() 状态都不会改变。请帮忙。

【问题讨论】:

  • 不确定java,但在其他环境中,读取返回0字节读取是远程对等方已关闭连接的信号,因此'Nothing to read'表示客户端关闭。
  • 另外,所有的 sleep() 调用是怎么回事?
  • 我也赞成 EJP,主要是因为“你为什么要设置非阻塞?”,我没有注意到,(主要是因为它在每个客户端一个线程的服务器中是如此出乎意料) .

标签: java multithreading tcp nio nonblocking


【解决方案1】:

你的代码在很多方面都是错误的。

  1. 如果您使用非阻塞 I/O,您当然也应该使用 Selector,而不是在 accept()finishConnect() 等中疯狂旋转。

  2. isClosed() 不会告诉您对等方是否已关闭。它告诉你是否已经关闭。如果对等点已关闭,read() 将返回 -1。同样,isConnected() 只告诉你 是否曾经连接过套接字。你做到了,所以重复调用它是没有意义的。

  3. 在非阻塞模式下连接的正确方法是 (a) 调用 connect(), (b) 选择 OP_CONNECT, (c) 当它触发调用 finishConnect() 时,以及 (d) 如果和仅当返回 true 时,才取消对OP_CONNECT.的兴趣

  4. 如果您在非阻塞模式下收到任何IOException,则必须关闭通道。

  5. “多线程非阻塞服务器”在术语上几乎是自相矛盾的。如果你有线程,阻塞。如果您有非阻塞模式,请选择。

【讨论】:

  • +1 是的 - 我没有注意到非阻塞套接字。每个客户端启动一个线程,然后使用非阻塞的,奇怪的:((
  • 感谢您的回复,我尝试使用选择器,但失败了。今天晚些时候,我发现一个 xSocket 库女巫帮了我很多。它非常易于使用。下载:xsocket.org 教程:xsocket.sourceforge.net/core/tutorial/V2/TutorialCore.htm
  • @AdrianES 在我看来,带有线程的java.net.Socket 是你能得到的最简单的方法。我认为您根本不需要使用任何第三方的东西。我只是觉得你从错误的地方开始。除非您计划建立数千个连接,否则您不需要 NIO。
猜你喜欢
  • 2018-03-07
  • 1970-01-01
  • 2020-09-07
  • 1970-01-01
  • 1970-01-01
  • 2015-04-23
  • 2021-07-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多