【问题标题】:Can a process connect to a socket that itself created?进程可以连接到自己创建的套接字吗?
【发布时间】:2011-11-15 04:17:40
【问题描述】:

编辑:下面的代码没有抛出异常,但没有输出并挂起。它应该输出“测试消息”。在 main() 中,我们启动了一个线程,该线程给定了一个监听随机端口的服务器套接字。主线程尝试在同一个随机端口上与 ServerSocket 连接并通信,但显然失败了。为什么?

public class IntraProcSockTest {
private static int port;

private class Listener extends Thread {
    public Listener() {
    }

    public void run() {
        try {
            ServerSocket serverSocket = new ServerSocket(0);
            port = serverSocket.getLocalPort();
            Socket socket = serverSocket.accept();

            BufferedReader in;
            String fromClient;

            in = new BufferedReader(new InputStreamReader(
                    socket.getInputStream()));

            while ((fromClient = in.readLine()) != null) {
                System.out.println("From client: " + fromClient);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

public IntraProcSockTest() {
    new Listener().start();
}

public static void main(String[] args) {
    new IntraProcSockTest();

    try {
        Thread.sleep(5000);
        Socket socket = new Socket("localhost", port);
        PrintWriter socketOut = new PrintWriter(socket.getOutputStream());

        socketOut.println("Test message");
    } catch (UnknownHostException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

【问题讨论】:

  • 另见example
  • 已解决:我必须刷新套接字:忘记使用自动刷新

标签: java


【解决方案1】:

进程可以连接到自己创建的套接字,没有问题。 向我们展示引发异常的代码和/或有关异常的更多详细信息。

首先,注意不要为客户端套接字(连接到正在监听的另一个端口)指定一个本地端口。让操作系统选择一个随机端口。请记住,任何套接字都由四个元素(remote host, local host, remote port, local port)标识,如果将服务器套接字和客户端套接字绑定在同一个本地端口上,则设为4498,两个套接字定义如下:(localhost, localhost, 4498, 4498) 这不起作用。我怀疑这可能是您的问题。
为避免此类问题,客户端套接字通常绑定到操作系统选择的随机端口。向我们展示您的代码,特别是创建客户端套接字并连接到服务器套接字的部分。

关于 IPC,使用套接字作为进程间甚至进程内通信技术并不总是不好。显然,性能更差,并且您可能会失去一些代码可读性,但您的软件将很容易移植到网络(分布式)应用程序中。这取决于您的计划,它不像 IPC 套接字 == 不好。

【讨论】:

  • 啊第二段确实是我的问题。因此,将客户端和服务器套接字绑定到本地主机上的同一端口对我不起作用。
  • 当然可以,但这绝对不是问题,因为您可以让操作系统将套接字绑定到它可以找到的任何空闲本地端口。请务必指定远程端口,否则您将无法连接。本地和远程端口之间的区别总是有点令人困惑。对于服务器套接字,本地端口很重要,对于客户端套接字,只关心远程端口。
  • 关于随机端口的问题:当服务器选择一个随机端口时,客户端如何知道连接到它的socket是哪个端口?
  • 好的,实际上没有服务器套接字绑定到随机的本地端口。对于服务器套接字,本地端口很重要,对于客户端套接字只关心远程端口,本地端口是随机的。
  • 作为一个例外,在 IPC 套接字中,您也经常为服务器使用随机端口。但我不想让你感到困惑:如果你修复你的代码,我发现它更有用。 :)
【解决方案2】:

你可以在一个线程中创建一个 Socket 连接。

ServerSocket ss = new ServerSocket(0); // open a random free port.
Socket c = new Socket(ss.getInetAddress(), ss.getLocalPort());
Socket s = ss.accept();
ss.close();

final byte[] bytes = "Hello World!".getBytes();
final OutputStream out = c.getOutputStream();
out.write(bytes.length);
out.write(bytes);


final DataInputStream in = new DataInputStream(s.getInputStream());
int len = in.read();
final byte[] b = new byte[len];
in.readFully(b);
System.out.println(new String(b));

c.close();
s.close();

如果您想要的只是进程中的 IPC,那么套接字并不是最快或最简单的方法。尝试使用管道 (NIO) 或管道输入/输出流 (IO)。它更快更简单。

Pipe pipe = Pipe.open();
SinkChannel sink = pipe.sink();
SourceChannel source = pipe.source();

PipedOutputStream output = new PipedOutputStream();
PipedInputStream input = new PipedOutputStream(output);  

顺便说一句:您可以在同一个线程中连接客户端和服务器 Socket,但是

使用 Exchanger 的速度提高了 10 倍,而使用环形缓冲区的速度再次提高了。

如果您想要方便,使用 ExecutorService 是将工作委托给后台线程池的最佳方式。这仍然可以每秒执行数百万个任务。

【讨论】:

  • +1 你是对的,即使我发现使用阻塞队列更易读和更直接。然而,他问如何用套接字来做这件事,缺乏一些关于套接字如何工作的知识:) 这是一种学习东西的方法,就像在 S.O 中一样......
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-12-02
  • 1970-01-01
  • 1970-01-01
  • 2012-03-03
  • 1970-01-01
  • 2017-08-13
  • 1970-01-01
相关资源
最近更新 更多