【问题标题】:Java, ports, sockets, piping a connection through a programmeJava,端口,套接字,通过程序管道连接
【发布时间】:2011-09-23 11:52:21
【问题描述】:

我需要在 LAN 中不同设备上的两个程序之间建立连接。即,我的设备 A 应该连接到我的 LAN 中的设备 B:portX。问题是我无法将它们直接连接起来。我要做的是让设备 A 连接到服务器,并让该服务器连接到设备 B。在我的服务器上,我监听端口“portX”,当我获得连接时,我连接到设备 B同一个端口。然后我必须通过服务器将数据从 A 传送到 B,但由于某种原因,设备 B 在从 A 接收数据(命令)时没有做它应该做的事情。

我该怎么做?

这是我一直在尝试的方法:

public class Main {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = null;
        try {
            serverSocket = new ServerSocket(8000);
        } catch (IOException e) {
            System.err.println("Could not listen on port: 8000.");
            System.exit(1);
        }
        Socket clientSocket = null;
        try {
            clientSocket = serverSocket.accept();
            System.err.println("connection accepted");
        } catch (IOException e) {
            System.err.println("Accept failed.");
            System.exit(1);
        }
        Socket remoteSocket = null;
        try {
            remoteSocket = new Socket("192.168.1.74", 8000);
        } catch (Exception e) {
            System.out.println("Failed to connect to device B");
        }
        PrintWriter remoteOut = new PrintWriter(remoteSocket.getOutputStream(),
                true);
        BufferedReader remoteIn = new BufferedReader(new InputStreamReader(
                remoteSocket.getInputStream()));
        PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
        BufferedReader in = new BufferedReader(new InputStreamReader(
                clientSocket.getInputStream()));
        String inputLine;
        System.out.println("Hi, we are before the while");
        int inputChar = 0;
        while ((inputChar = in.read()) >= 0) {
            remoteOut.println(inputChar);
            System.out.println(inputChar);
        }
        System.out.println("We are after the while");
        out.close();
        in.close();
        remoteIn.close();
        remoteOut.close();
        clientSocket.close();
        serverSocket.close();
        remoteSocket.close();
    }
}

提前致谢, 蒂莫菲

【问题讨论】:

  • 您是否尝试过让“服务器”直接为“B”运行代码以确认问题出在管道中?如果不是,那么这方面只会使问题变得模糊,您最好将其从您的问题中删除。
  • 另外,你说B“没有做它应该做的事”。这意味着什么? 做什么 B做什么?
  • 等等...你为什么要让相同的代码充当客户端、服务器和代理?拥有三个不同的程序,这只是令人困惑的事情。
  • 问题是A和B中的代码不是我的,我也没有源代码。我知道这听起来像什么,但一切都是严格合法的;) B 应该采取一些措施来响应通过该套接字发送的内容,例如,如果 A 说“闪烁绿灯”,B 应该闪烁其绿灯。现在它根本没有做任何事情。同样,正如我所说,当 A 直接连接到 B 时,B “服从”。 “A”实际上是一个程序,它在“服务器”或我网络中的任何其他计算机上运行时都可以工作。我想做的和“中间人”的事情很像,只是我自己做的网
  • Mark Peters 的“等等...为什么”——它们三个不同的程序。 A 和 B 不是我的,我只是想写服务器部分。

标签: java sockets port


【解决方案1】:

我创建了一个使用 NIO 通道的版本。这种方法的好处是您可以使用单个线程来管理来自多个来源的内容。我不需要知道两个服务之间的协议是什么,因为我们只是在复制字节。如果您只想使用普通的旧套接字,则需要使用互斥体和 2 个线程在两个套接字之间读取/写入数据(套接字不是线程安全的)。

注意:处理错误情况可能有比仅仅删除连接并创建新连接更好的方法。

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Set;

/**
 * Socket Gateway for SO Question 7528528
 * User: jhawk28
 * Date: 9/26/11
 * Time: 9:03 PM
 * <p/>
 * http://stackoverflow.com/questions/7528528/java-ports-sockets-piping-a-connection-through-a-programme
 */
public class Gateway
{
  public static void main(String[] args) throws IOException
  {
    // Set up Server Socket and bind to the port 8000
    ServerSocketChannel server = ServerSocketChannel.open();
    SocketAddress endpoint = new InetSocketAddress(8000);
    server.socket().bind(endpoint);

    server.configureBlocking(false);

    // Set up selector so we can run with a single thread but multiplex between 2 channels
    Selector selector = Selector.open();
    server.register(selector, SelectionKey.OP_ACCEPT);


    ByteBuffer buffer = ByteBuffer.allocate(1024);

    while (true)
    {
      // block until data comes in
      selector.select();

      Set<SelectionKey> keys = selector.selectedKeys();

      for (SelectionKey key : keys)
      {
        if (!key.isValid())
        {
          // not valid or writable so skip
          continue;
        }

        if (key.isAcceptable())
        {
          // Accept socket channel for client connection
          ServerSocketChannel channel = (ServerSocketChannel) key.channel();
          SocketChannel accept = channel.accept();
          setupConnection(selector, accept);
        }
        else if (key.isReadable())
        {
          try
          {
            // Read into the buffer from the socket and then write the buffer into the attached socket.
            SocketChannel recv = (SocketChannel) key.channel();
            SocketChannel send = (SocketChannel) key.attachment();
            recv.read(buffer);
            buffer.flip();
            send.write(buffer);
            buffer.rewind();
          } catch (IOException e)
          {
            e.printStackTrace();

            // Close sockets
            if (key.channel() != null)
              key.channel().close();
            if (key.attachment() != null)
              ((SocketChannel) key.attachment()).close();
          }
        }
      }

      // Clear keys for next select
      keys.clear();
    }
  }

  public static void setupConnection(Selector selector, SocketChannel client) throws IOException
  {
    // Connect to the remote server
    SocketAddress address = new InetSocketAddress("192.168.1.74", 8000);
    SocketChannel remote = SocketChannel.open(address);

    // Make sockets non-blocking (should be better performance)
    client.configureBlocking(false);
    remote.configureBlocking(false);

    client.register(selector, SelectionKey.OP_READ, remote);
    remote.register(selector, SelectionKey.OP_READ, client);
  }
}

【讨论】:

  • 谢谢。不幸的是,现在不能尝试,被分配了另一个任务......我会在最近的将来看看它。
【解决方案2】:

您的问题是您使用 PrintWriter 作为转发机制。您读入一个字符,然后写出字符+换行符。尝试将其切换为remoteOut.print(inputChar);

更好的解决方案是读取字符然后写出字符(您可以使用 BufferedWriter)。 commons-io 已经在 IOUtils 中提供了可以执行此类操作的复制方法

【讨论】:

  • 我会更进一步说完全忘记处理字符(/Readers/Writers),除非对它们有一些要求。只需使用InputStreamOutputStream 将数据作为任意二进制数据转发,就像IOUtils 方法所做的那样。
  • @MarkPeters 同意,对 Reader/Writers 没有太大价值,因为已经有一个 Stream。
  • 感谢您的回复。但它没有用。我尝试使用 out.print 而不是 out.write,但没有帮助。关键是我不仅需要在服务器内的两个设备之间创建连接,我还必须能够读取它们相互发送的数据并能够对其进行过滤,发送到多个设备 (B) 等等。似乎我只能从其中一个设备中读取,因为 .read() 是一种阻塞方法。更糟糕的是,我并不完全了解这些设备的通信协议。
  • 我将尝试通过在输入流上调用 .available() 方法来找出缓冲区长度。
猜你喜欢
  • 1970-01-01
  • 2015-05-26
  • 1970-01-01
  • 2021-10-08
  • 1970-01-01
  • 2021-10-13
  • 1970-01-01
  • 2018-01-13
  • 2011-12-10
相关资源
最近更新 更多