![](/img/trans.png)
[英]Best way to import/export data from Elasticsearch using Java client
[英]Java the best way of waiting & getting data from your client
我开始使用JDK中的主要网络软件包学习网络,在举几个例子之后,这非常简单。 但是现在,我对制作像聊天系统这样的多客户端应用程序很感兴趣。
到目前为止,我的结构思想是这样的:
连接处理程序类,用于处理传入的连接并保存客户端列表。 如果找到了新的连接,则创建一个新的客户端对象,启动它的线程(Client对象将实现可运行,因此它将启动它自己的循环服务,它将为收到的新数据包循环),并将其添加到列表中。
我为每个客户端创建一个新线程,而不是遍历所有客户端,因为从客户端进程中读取将停止整个执行过程,并且将等待客户端发送数据,这让我很烦,这是我的问题。
我创建了一个简单的控制台应用程序,可以接收来自客户端的消息,但是现在我想检测断开连接。 我读到的是,如果用户未连接,则bufferedReader .read()方法将返回-1,因此我认为我可以循环执行此操作,并且每隔数秒便对每个客户端进行一次操作,但事实是,客户端必须向发送一个数据包。 read(),所以,如果您执行.read(),它将等待并停止整个线程,直到接收到数据包为止(我认为)。
这是我当前从客户端获取消息的代码:
public boolean isConnected() {
try {
this.in.read();
this.lastCheck = System.currentTimeMillis();
return true;
} catch (IOException e) {
if (!inConnection()) {
System.out.println("User disconnected");
try {
this.destruct();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
return false;
}
private boolean inConnection() {
return System.currentTimeMillis() - lastCheck < this.maxTime;
}
public void startClientService() throws IOException {
while(!this.session.isClosed()) {
if (System.currentTimeMillis() - this.checkTime > 600) {
System.out.println(System.currentTimeMillis() - this.checkTime);
if (this.isConnected()) {
int packetType = this.dataIn.readInt();
packets.getPacket(packetType);
}
}
}
}
public void destruct() throws IOException {
this.session.close();
this.connection.removeClient(this);
System.out.println("Session killed");
}
基本上发生了什么,我从客户端发送一个打包的整数,我可能有很多事情要做,因此我可以设置许多唯一的数据包ID,因此,如果我想接收和处理聊天消息,则数据包ID为216,客户端发送一个int 216,服务器读取该数据包,进入所有数据包ID的切换循环,并检测其是否真正为216,如果是,它将获取处理消息的打包类的实例并获取接收到的消息的字节,如下所示:
public class Chat implements Packet {
@Override
public void processPacket(Session c) {
String message = readMessage(c);
System.out.println("Message: " + message);
}
private String readMessage(Session c) {
byte[] data = c.readBytes();
String message = null;
try {
message = new String(data, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return message;
}
}
这就是我读取字节的方式:
public byte[] readBytes() {
int len;
byte[] data = null;
try {
len = this.dataIn.readInt();
data = new byte[len];
if (len > 0) {
this.dataIn.readFully(data);
}
} catch (IOException e) {
e.printStackTrace();
}
return data;
}
好的,我的问题是:
添加“断开连接检测”后,当我发送消息时,什么也没有发生。 这可能是由于.read()停止并正在等待响应。 但是,如果我再次写一条消息,我将在服务器中收到该消息。
这是我临时的,丑陋的客户:
public class Client {
public static void main(String[] args) throws UnknownHostException, IOException {
Socket socket = new Socket("127.0.0.1", 43594);
Scanner r = new Scanner(System.in);
PrintWriter out = new PrintWriter(socket.getOutputStream());
String input;
while(true) {
input = r.next();
if (input != null) {
sendMessage(input, out);
}
}
}
public static void sendMessage(String message, PrintWriter out) {
byte[] encoded = encode(message);
out.write(0);
out.println(encoded + "\n");
out.flush();
}
public static byte[] encode(String s) {
return DatatypeConverter.parseBase64Binary(s);
}
public static String decode(byte[] s) {
return DatatypeConverter.printBase64Binary(s);
}
}
我的问题是:有什么更好的方法从客户端读取数据,而无需让应用程序等待数据并实际上每次循环? 或者,也许我应该有一个新线程来检查用户是否在线,所以每1个客户端2个线程?
如果有人需要我的会话对象(客户端对象):
public class Session extends Thread implements Runnable {
private Socket session;
private Client client;
private PrintWriter out;
private BufferedReader in;
private PacketHandler packets;
private DataInputStream dataIn;
private ConnectionHandler connection;
private final int checkTime = 1600;
private final int maxTime = 22000;
private long lastCheck;
public Session(Socket session) {
this.session = session;
this.client = new Client(this);
try {
this.setStream();
} catch (IOException e) {
e.printStackTrace();
}
this.packets = new PacketHandler(this);
System.out.println("[New session created]: " + session.getRemoteSocketAddress());
}
public void setConnectionHandler(ConnectionHandler c) {
this.connection = c;
}
public void run() {
try {
this.startClientService();
} catch (IOException e) {
e.printStackTrace();
}
}
public void setStream() throws IOException {
this.out = new PrintWriter(this.session.getOutputStream());
this.in = new BufferedReader(new InputStreamReader(this.session.getInputStream()));
this.dataIn = new DataInputStream(this.session.getInputStream());
}
public Client getClient() {
return this.client;
}
public byte[] readBytes() {
int len;
byte[] data = null;
try {
len = this.dataIn.readInt();
data = new byte[len];
if (len > 0) {
this.dataIn.readFully(data);
}
} catch (IOException e) {
e.printStackTrace();
}
return data;
}
public String readMessage() {
try {
return this.in.readLine();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public boolean isConnected() {
try {
this.in.read();
this.lastCheck = System.currentTimeMillis();
return true;
} catch (IOException e) {
if (!inConnection()) {
System.out.println("User disconnected");
try {
this.destruct();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
return false;
}
private boolean inConnection() {
return System.currentTimeMillis() - lastCheck < this.maxTime;
}
public void startClientService() throws IOException {
while(!this.session.isClosed()) {
if (System.currentTimeMillis() - this.checkTime > 600) {
System.out.println(System.currentTimeMillis() - this.checkTime);
if (this.isConnected()) {
int packetType = this.dataIn.readInt();
packets.getPacket(packetType);
}
}
}
}
public void destruct() throws IOException {
this.session.close();
this.connection.removeClient(this);
System.out.println("Session killed");
}
}
谢谢!
虽然我没有时间查看所有代码,但是有两点可以帮助您。
1)使用定义的消息头。 定义客户端将发送到服务器的每个消息的X字节数。 使用这些字节来定义消息将持续多长时间以及消息的类型。 服务器知道此标头的长度和布局,并使用它以特定方式处理消息。 示例可能是一个字节的标头。 值1可能是我已连接的消息。 2可能是我即将断开连接。 3可能是我当前不在,4可能是传入的聊天消息。
2)有两种方法可以处理输入。 首先是使用阻塞IO,并创建一个单独的线程来接收来自每个客户端的消息。 我相信这就是您当前正在做的事情。 第二个是使用非阻塞IO,并在打开的套接字上迭代一个单独的线程并进行读取。 非阻塞将检查是否有要读取的数据,但如果没有,线程将继续执行。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.