我有一个问题。
在我的代码中,我有一系列玩家,每个人都有“进入”和“出去”服务器。
从服务器到客户端我可以很好地沟通,没有任何问题,每个客户端在他的GUI中显示服务器发送的更改。
问题是从客户端到服务器的通信。作为服务器,我想收到每个玩家的请愿做一些工作。问题是,我不知道从哪个索引收到请愿书,我无法检查每个players[index].in.readUTF()
因为它阻止该程序。
有些帮助吗? in.avaiable()
对我不起作用。
更清楚地说明:
当玩家登录服务器时,我在服务器端执行此操作。
socket = serverSocket.accept();
out = new DataOutputStream(menuPrincipal.socket.getOutputStream());
in = new DataInputStream(menuPrincipal.socket.getInputStream());
players[i] = new player(i,out,in);
然后我在服务器端有一个线程在等待消息。
问题是,我不知道哪个玩家阵列的索引(来自哪个玩家)会收到消息。我无法检查没有向服务器发送任何内容的玩家的索引,因为它会阻止该程序。
这就是它的运作方式:
从服务器到客户端我只是这样做:
player[i].out.writeUTF(message)
in.readUTF()
,一切正常从客户端到服务器我做:
out.writeUTF(message)
player[i].in
来阅读我不知道哪个播放器。 答案 0 :(得分:1)
所以你说你已经设法将数据从服务器发送到客户端,它工作正常。从客户端向服务器发送数据时也适用相同的原则。
在服务器端有一个监听线程。在数据包内,您可以包含发送数据的用户的名称或IP地址。 IE:
Data Recieved by Server: 'message, hello chris, sender, reciever'
然后,您的服务器可以使用字符串标记器将数据包拆分为有意义的部分。
Action: 'message'
Message: 'hello chris'
Sender: 'sender'
Reciever: 'reciever'.
然后您拥有继续所需的所有数据。
此实现意味着您需要的所有数据都包含在您收到的数据包中,而您无需检查它来自何处。额外的几个字节的数据不会减慢你每次收到数据包时必须执行搜索的速度。
编辑以响应OP编辑
或者,您可以实现HashMap<String,Player>
,而不是实现数组。这样,您需要做的就是使用上面的描述从发送的数据包中获取用户名,并将其传递给hashmap。 hashmap可以返回表示客户端的User对象,并且不需要再次进行索引查找。
您可以在Java Hashmap here上找到相关文档。
最后,我注意到你有一个player
课程。 Java约定是所有类都以大写字母开头。那就是:
player
应为Player
。
针对您的评论进行最终修改
你的问题是设计糟糕的问题,我担心。在这种情况下,客户端应该都通过相同的通道与服务器通信。这样,服务器所要做的就是监听任何消息,解析它们并移动它们。调整您的实施,以便您的客户通过相同的流与服务器通信,这将消除您的问题。
答案 1 :(得分:1)
好的,首先让我先介绍一下套接字如何工作,然后再向您提供解决方案,了解正在发生的事情至关重要。
套接字解释(Reference)
套接字是一种软件端点,用于在服务器程序与一个或多个客户端程序之间建立双向通信。请注意单向双向。这意味着对于客户端和服务器之间的每个连接,您有两个流:输入流和输出流。这些可以由服务器端和客户端设置的套接字对象访问。
您的问题
从服务器到客户端我只做播放器[i] .out.writeUTF(消息)和 然后客户端只检查他的“in”,执行in.readUTF()并且所有工作 精细。从客户端到服务器我从客户端执行out.writeUTF(消息)但是 然后,在服务器端,我必须选择一个播放器[i] .in来阅读 我不知道哪个球员是。
因此,如果我理解正确,您在如何确定哪个客户端向您发送消息方面存在困难。如果正确使用多线程,这很容易解决,因为每个客户端都将在一个单独的线程上处理。
关键步骤是如何最初处理客户。请考虑以下代码。为了简短起见,我没有把重点放在惯例上。
ArrayList<ClientWorker> clientWorkers = new ArrayList<ClientWorker>();
int idCounter = 0;
public void listenSocket(){
try{
int port = 9090
server = new ServerSocket();
} catch (IOException e) {
System.exit(-1);
}
while(true){
ClientWorker w;
try{
//server.accept returns a client connection
clientWorker = new ClientWorker(server.accept(), idCounter, );
idCounter++;
clientWorkers.add(clientWorker);
Thread t = new Thread(clientWorker);
t.start();
} catch (IOException e) {
System.exit(-1);
}
}
}
好的,通过该代码,我们正在处理新客户端并为其分配唯一密钥。而不是数组,我使用arraylists,以便它可以根据连接到服务器的用户数增加和减少大小。收到客户端后,我创建了一个clientWorker实例。此外,为每个连接的用户分配一个唯一键,可用于从arraylist中获取其对象(如果需要)。
ClientWorker类看起来像这样:
class ClientWorker implements Runnable {
private Socket client;
public int id
//Constructor
ClientWorker(Socket client, int id) {
this.client = client;
this.id = id;
}
public void run(){
String line;
BufferedReader in = null;
PrintWriter out = null;
try{
in = new BufferedReader(new InputStreamReader(client.getInputStream()));
out = new PrintWriter(client.getOutputStream(), true);
} catch (IOException e) {
System.out.println("in or out failed");
System.exit(-1);
}
while(true){
try{
//ANY DATA I GET FROM THIS STREAM IS FROM THIS PARTICULAR CLIENT ONLY!
line = in.readLine();
System.out.println("RECEIVED FROM CLIENT "+ id +" " + line);
}catch (IOException e) {
System.exit(-1);
}
}
}
}
该线程调用run()方法。由于每个客户端都有一个单独的线程,因此从特定客户端处理的数据是从该特定客户端发送的,而不是其他人发送的。因此,可以轻松区分每个客户的数据。
对不起它的答案很长。花了一些时间才达到我的目的。