0

我知道标题很糟糕,但让我解释一下我的情况。好的,我正在制作一个服务器,它坐在一边并与我的 Minecraft 客户端进行通信。我可以让一个客户端连接和传输数据,第四个。当我尝试与第二个客户端连接时,出现错误:

java.net.SocketException: Connection reset by peer: socket write error
at java.net.SocketOutputStream.socketWrite0(Native Method)
at java.net.SocketOutputStream.socketWrite(Unknown Source)
at java.net.SocketOutputStream.write(Unknown Source)
at java.io.ObjectOutputStream$BlockDataOutputStream.drain(Unknown Source)
at java.io.ObjectOutputStream$BlockDataOutputStream.setBlockDataMode(Unknown Source)
at java.io.ObjectOutputStream.<init>(Unknown Source)
at ConnectionHandler.setupStreams(ConnectionHandler.java:71)
at ConnectionHandler.HandleChat(ConnectionHandler.java:30)
at ConnectionHandler.<init>(ConnectionHandler.java:23)
at Server.waitForConnection(Server.java:118)
at Server.RunAll(Server.java:65)
at Server.run(Server.java:50)
at ServerTest.main(ServerTest.java:9)

因此,结果代码如下:

setupStreams:第 71 行

public static void setupStreams() throws IOException
{
    for(int i = 1; i <= Server.ConnectionArray.size(); i++)
    {
        Socket TempSocket = (Socket) Server.ConnectionArray.get(i - 1);
        output = new ObjectOutputStream(TempSocket.getOutputStream());//Line 71
        output.flush();
        input = new ObjectInputStream(TempSocket.getInputStream());
        whileChatting();
    }
}

现在我觉得最好解释一下所有这些代码是如何设置的:

它从一个名为 ServerTest.Java 的类开始:

public class ServerTest
{
    public static void main(String[] args)
    {
        Server Minecraft = new Server();
        Minecraft.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        Minecraft.run();
    }
}

我称这个对象为“我的世界”。

现在它引用的类也实现了 Runnable 类并以 run() 方法开始。这是那个方法:

@Override
public void run()
{  
    RunAll();
}

public void RunAll()
{
    ConnectionHandler.showMessage("Waiting for client connections...");
    try
    {

        server = new ServerSocket(1337, 10);
        while(true)
        {
            try 
            {
                waitForConnection();
            }
            catch (IOException e)
            {
                e.printStackTrace();
            }
        }
    }
    catch(IOException Exception)
    {
        Exception.printStackTrace();
    }
    finally
    {
       ConnectionHandler.closeCrap();
    }
}

现在从这里开始等待连接,我认为它会反复循环,但我不知道。这是该方法的代码:

public void waitForConnection() throws IOException
{
   connection = server.accept();
   ConnectionArray.add(connection);
   ConnectionHandler connections = new ConnectionHandler(connection);
   connection = null; 
}

现在我认为这个方法的作用是接受来自客户端的传入连接,然后将该连接添加到我的套接字数组中,然后将套接字交给处理程序类,然后将原始套接字设置回 Null 以等待另一个传入连接.

现在这将我们带到 ConnectionHandler 类。在这里,它需要一个套接字 X 并将其设置为全局套接字,如下所示:

static Socket Socket;
public PrintWriter Out;
String Message = "";
public static ObjectOutputStream output;
public static ObjectInputStream input;
public static int Players = 0;

public ConnectionHandler(Socket X)
{
    this.Socket = X;
    HandleChat();
}

该类还实现了 Runnable 类。但正如您所看到的,它将全局套接字设置为 X 然后运行 ​​HandleChat(); 这是方法:

public static void HandleChat()
{
    try
    {
        setupStreams();
    }
    catch (IOException e)
    {
        e.printStackTrace();
    }

}

如您所见,这一切都在运行 setupStreams(); 方法。这也是该方法:

public static void setupStreams() throws IOException
{
    for(int i = 1; i <= Server.ConnectionArray.size(); i++)
    {
        Socket TempSocket = (Socket) Server.ConnectionArray.get(i - 1);
        output = new ObjectOutputStream(TempSocket.getOutputStream());
        output.flush();
        input = new ObjectInputStream(TempSocket.getInputStream());
        whileChatting();
    }
}

现在我“思考”这个方法创建一个名为 TempSocket 的新套接字,并使其 = 数组中的最新连接,然后将输出和输入流设置到该套接字,从而为该单个客户端生成一个套接字,然后运行 ​​whileChatting(); 这是生成的代码:

public static void whileChatting() throws IOException
{
    String message = "";
    do
     {
         try
         {
             message = (String) input.readObject();
             showMessage("\n" + message);
             sendMessage("", message); 
         }
         catch(ClassNotFoundException classNotFoundException)
         {
             ConnectionHandler.showMessage("\n idk wtf that user sent!");
         }
      }while(!message.equals("/stop"));
}

再次与“思考”在这里。我认为读取对象被设置为字符串消息;然后被发送到客户端并显示在控制台屏幕上。(Swing) 下面是方法 (Send) 和 (Show) 的代码:

public static void sendMessage(String message, String returnedMessage)
{
    try
    {
    if(!message.isEmpty())
    {
        output.writeObject("\247c[Server]\247d " + message);
    }
    else
    {
        output.writeObject(returnedMessage);
    }
        output.flush();
        showMessage("\n[Server] " + message);
    }
    catch(IOException ioException)
    {
        Server.chatWindow.append("\n ERROR: DUDE I CANT SEND THAT MESSAGE");
    }
 }

static void showMessage(final String text)
   {
      SwingUtilities.invokeLater
      (
         new Runnable()
         {
            public void run()
            {
                Server.chatWindow.append(text);
            }
         }
      );
   }

现在你可以在这里看到,当我想在客户端显示一条消息时;我希望它看起来像:[Server] 文本。此外,您会注意到它还 sys 返回了消息。我将客户端设置为仅发送到服务器,然后在屏幕上显示返回的内容,所以可以说我输入“Hay”它在发送时不会在我的屏幕上显示任何内容,但是一旦服务器收到它,它会将确切的文本发送回给我每个人。这就是 sendMessage(); 方法。现在的显示方法比较简单。它在接收时显示文本。

简而言之,这就是代码。我宁愿给你留下太多的信息,也不愿让你坐在这里想知道发生了什么。我将再一次更深入地解释我的情况。我正在制作一个与我的 Minecraft 客户端通信的服务器。一个客户端连接正常,但是当另一个客户端尝试连接它时出错并且当第一个客户端断开连接时,服务器将不允许客户端重新连接。我认为这可能与连接数组有关,并且套接字在断开连接时没有从数组中删除,但我不知道。现在最后,我的问题是如何解决这些错误?提前感谢我对这个问题的任何回复。

4

3 回答 3

0

似乎在您调用 setupStreams 的每个用户连接上都会循环所有连接。

这意味着如果用户 1 连接您为用户打开输出套接字,第二个用户连接您再次循环遍历所有连接并再次为已经打开的用户 1 打开输出蒸汽。你需要 setupStreams 你不能为用户打开输出 stearm 两次\我也认为你在这里有错误

   for(int i = 1; i <= Server.ConnectionArray.size(); i++)

您从 1 开始而不是从 0 开始。并与 <= 而非 < 进行比较。我不知道为什么,ConnectionArray 的类型是什么。

于 2013-04-17T04:49:13.510 回答
0
public void waitForConnection() throws IOException
{
   connection = server.accept();
   ConnectionArray.add(connection);
   ConnectionHandler connections = new ConnectionHandler(connection);
   connection = null; 
}

connection未找到,因此这不是实际代码,或者您将此临时变量存储在成员或全局中。在任何一种情况下,增加临时变量的范围以跨越不同的函数都是不好的,多线程就像杂耍电锯一样。顺便说一句:将连接处理程序存储在列表中比创建它们并将其套接字存储在列表中更自然。这是一种责任不明确的情况,导致代码不清晰和错误。

虽然上述案例可能仍然无害,但它仍然显示出糟糕和危险的风格。你继续使用这种风格(至少它是一致的!)“......它需要一个套接字 X 并将其设置为全局套接字,如图所示......”:

static Socket Socket;

public ConnectionHandler(Socket X)
{
    this.Socket = X;
}

制作Socket Socket成静态的,在这个类的所有实例之间共享,这显然不是你想要的。您现在应该知道为什么全局变量被认为是不好的以及为什么类静态变量只是伪装的全局变量。

摘要:决定谁访问哪些部分。如果它只是一个对象,则将部分设为私有成员,以便编译器确保它实际上只是一个对象。尽量不要共享状态。如果多个对象和线程访问一个对象,请计划好它们如何不互相干扰。

于 2013-04-17T05:43:38.710 回答
0

我建议使用某种 API,因为这只是(抱歉)混乱的代码。 是我使用的那个。它为您做所有事情,它创建线程,打开套接字并且它是基于事件的!因此,您不必遍历所有套接字来读取所有数据,当有可用时您会收到通知!

于 2015-01-04T13:27:00.563 回答