使用线程实现多客户端的服务器

4
我遇到了一些问题。现在我看了这个教程:http://docs.oracle.com/javase/tutorial/networking/sockets/clientServer.html。这个教程提供了一个服务器,多个客户端可以连接到该服务器,当他们连接到服务器时,会被告知参与knock knock游戏。现在我理解如何传输数据等等,但线程是如何工作的呢?
我正在制作一个网络乒乓球游戏,其中服务器将保存位置并将其传递给客户端。我已经连接了一个客户端到服务器,并且球的位置传递给了客户端,它工作得很好,有点抖动,但我相信使用.sleep的线程将会有所帮助。但不管怎样,我的问题是,如何使我的客户端成为线程?以及如何存储它们?
例如,这里是Knock Knock服务器MultiThread类。
package knockKnockServer;

import java.net.*;
import java.io.*;

public class KKMultiServerThread extends Thread {
private Socket socket = null;

public KKMultiServerThread(Socket socket) {
super("KKMultiServerThread");
this.socket = socket;
}

public void run() {

try {
    PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
    BufferedReader in = new BufferedReader(
                new InputStreamReader(
                socket.getInputStream()));

    String inputLine, outputLine;
    KnockKnockProtocol kkp = new KnockKnockProtocol();
    outputLine = kkp.processInput(null);
    out.println(outputLine);

    while ((inputLine = in.readLine()) != null) {
    outputLine = kkp.processInput(inputLine);
    out.println(outputLine);
    if (outputLine.equals("Bye"))
        break;
    }
    out.close();
    in.close();
    socket.close();

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

在服务器上,我们有

package knockKnockServer;

import java.net.*;
import java.io.*;

public class MultiKKServer {
public static void main(String[] args) throws IOException {
    ServerSocket serverSocket = null;
    boolean listening = true;

    try {
        serverSocket = new ServerSocket(4444);
    } catch (IOException e) {
        System.err.println("Could not listen on port: 4444.");
        System.exit(-1);
    }

    while (listening)
    new KKMultiServerThread(serverSocket.accept()).start();

    serverSocket.close();
}
}

现在看服务器,它将在每个连接上创建一个新的KKMultiServerThread,但是如何存储它们呢?我可以制作一个KKMultiServerThread数组吗? 我试图制作一个KKMultiServerThread数组, 当我尝试这行代码时:
multi[0] = new KKMultiServerThread(serverSocket.accept()).start();

我遇到了这个错误:"无法将void转换为Thread",如果有人能解决我的问题就好了。
画布
更新
我现在有了自己的线程类。
package Pong;

import java.net.*;
import java.io.*;

public class PongPlayerThread extends Thread 
{
private Socket socket = null;
private String pongData = "";

public PongPlayerThread(Socket socket, int id) 
{
    super("PongPlayerThread");
    this.socket = socket;
}

public void passData(String data)
{
    pongData = data;
}

public void run()
{
    try
    {
        PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
        while(true)
        {
            out.println(pongData);
        }
    }
    catch (IOException e) 
    {
        e.printStackTrace();
    }
}

}

pongData是一个字符串,包含了所有信息。假如我在我的乒乓球服务器顶部声明一个player1:

private static PongPlayerThread player1;

当它正在侦听时,执行此行。

    while(listen)
    {
        PongPlayerThread player1 = new PongPlayerThread(serverSocket.accept(), 0).start();
    }

它给我这个错误:“无法将void转换为PongPlayerThread”,我该如何解决?
2个回答

0

每当客户端连接到服务器时,服务器通常会为该客户端创建一个新的线程。以下是一些伪代码:

WHILE SERVER IS RUNNING

SERVER WAITS FOR A CLIENT TO CONNECT
SERVER ACCEPTS THE CLIENT IF THERE IS ENOUGH MEMORY TO CREATE A NEW THREAD
SERVER CREATES A NEW THREAD ROUTINE FOR THE CLIENT PASSING THE CLIENT INFORMATION TO THE THREAD
SERVER CONTINUES TO LISTEN WHILE EACH THREAD IS SPECIFICALLY TAILORED FOR THE CLIENTS

REPEAT

你问了如何减少延迟的步骤?首先,设置允许的最大连接数。您不希望5000个客户端有自己的线程,除非您的计算机可以处理所有这些并且仍然运行。使用UDP而不是TCP,并且尝试进行数据压缩以最小化带宽,在一次发送时不要发送50GB的信息;如果您只需要发送几个字节的信息,请尝试发送位置信息而不是字符串,例如,您可以将位置X = 5Y = 0发送为50,并将第一个小数位解析为X,第二个小数位解析为Y。

与其在线程例程中传递客户端套接字,不如传递客户端的唯一标识符。由于Pong是两个玩家,将连接限制为两个客户端。玩家1为0,玩家2为1。所以

new KKMultiServerThread(clientID).start(); // clientID is of type int

编辑:

int id = 0;
while(serverIsRunning)
{
    Client client = server.accept();
    if (id > 2) client.Close(); // Do not accept.
    Thread.New(id).Start();
    id++;
}

乒乓游戏需要先使用TCP/IP,然后再使用UDP进行制作 :D - Canvas
好的,我会放弃 UDP 部分。 - Daniel Lopez
好的,构造函数需要一个int类型的clientID参数,但是对于输入,我该如何检查它是否为该ID?new KKMultiServerThread(clientID).start(); // clientID是int类型但是我现在的构造函数只接受socket作为参数?它应该同时接受socket和clientID吗? - Canvas
那么在 KKMultiServerThread 内部,它将处理蝙蝠是否应该上下移动,对吧? - Canvas
@Canvas 等等,现在我明白了,将构造函数更改为接受套接字和 ID 作为两个参数。 - Daniel Lopez
显示剩余5条评论

0

你的数组声明缺少对象类型

KKMultiServerThread multi[0] = new KKMultiServerThread(serverSocket.accept()).start();

但是为什么要费心呢?除非线程需要相互通信,让线程自由运行就可以了。就服务器而言,Run()方法定义了套接字的整个生命周期。每个线程都有游戏状态的独立副本(只要不使用静态变量),并且可以在没有任何额外干预的情况下与客户端进行通信。

这是Java中Socket/Thread库为您提供的巨大帮助之一,除非您有特定需求,否则不要使其更加复杂。


好的,这里还有另一个问题,当客户端连接时,第一个客户端需要能够控制屏幕右侧的球拍,第二个客户端需要控制屏幕左侧的球拍,我该如何知道哪个客户端是哪个?另外,您给我的那行代码说KKMultiServerThread无法解析为变量。 - Canvas
你可以创建特定的线程实例,比如player1(左)和player2(右),并将服务器更改为仅在停止侦听之前接受两个连接。然后,玩家的代码有一个简单的标志,告诉它应该在哪一边。 - Kelly S. French
我该怎么做? - Canvas
@Canvas,请查看我修改后的答案,以便了解如何操作。 - Daniel Lopez

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接