我正在尝试使用Java创建多线程聊天客户端 - 服务器。我正在使用本教程作为开头:http://pguides.net/java-tutorial/java-tcp-clientserver-chat/
我希望客户端在我输入String“quit”时停止,但我不明白我是怎么做到的。此外,当客户端断开连接时,我需要从连接的缺刻列表中删除客户端。
服务器
/* ChatServer.java */
import java.net.ServerSocket;
import java.net.Socket;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.Hashtable;
public class ChatServer {
private static int port = 1001;
public static void main (String[] args) throws IOException {
ServerSocket server = null;
try {
server = new ServerSocket(port);
} catch (IOException e) {
System.err.println("Could not listen on port: " + port);
System.err.println(e);
System.exit(1);
}
Socket client = null;
while(true) {
try {
client = server.accept();
} catch (IOException e) {
System.err.println("Accept failed.");
System.err.println(e);
System.exit(1);
}
/* start a new thread to handle this client */
Thread t = new Thread(new ClientConn(client));
t.start();
}
}
}
class ChatServerProtocol {
private String nick;
private ClientConn conn;
private static Hashtable<String, ClientConn> nicks = new Hashtable<String, ClientConn>();
private static final String msg_OK = "OK";
private static final String msg_INVALID = "INVALID COMMAND";
private static final String msg_SEND_FAILED = "FAILED TO SEND";
private static boolean add_nick(String nick, ClientConn c) {
if (nicks.containsKey(nick)) {
return false;
} else {
nicks.put(nick, c);
return true;
}
}
private static boolean remove_nick(String nick, ClientConn c) {
if (!(nicks.containsKey(nick))) {
return false;
} else {
nicks.remove(nick);
return true;
}
}
public ChatServerProtocol(ClientConn c) throws IOException {
nick = null;
conn = c;
}
private boolean sendMsg(String recipient, String msg) {
if (nicks.containsKey(recipient)) {
ClientConn c = nicks.get(recipient);
c.sendMsg(nick + ": " + msg);
return true;
} else {
return false;
}
}
public String process(String msg) {
if (msg.startswith("Nick"){
String output = "";
if(add_nick(tryauthor, this.conn)) {
this.nick = tryauthor;
output = "Welcome "+tryauthor;
} else {
output = "Nick already in";
}
}
}
else if (msg.startsWith("msg")) {
String[] msg_parts = msg.split(":");
for(int i=0; i<msg_parts.length; i++){
System.out.println(msg_parts[i]);
}
String msg_type = msg_parts[0];
if(msg_type.equals("msg")) {
if(msg_parts.length < 3) output = msg_INVALID;
if(sendMsg(msg_parts[1], msg_parts[2])) output = msg_OK;
else output = msg_SEND_FAILED;
} else {
output = msg_INVALID;
}
}
return output;
}
}
class ClientConn implements Runnable {
private Socket client;
private BufferedReader in = null;
private PrintWriter out = null;
public ClientConn(Socket client) {
this.client = client;
try {
/* obtain an input stream to this client ... */
in = new BufferedReader(new InputStreamReader(client.getInputStream()));
/* ... and an output stream to the same client */
out = new PrintWriter(client.getOutputStream(), true);
} catch (IOException e) {
System.err.println(e);
return;
}
}
public void run() {
String msg, response;
try {
ChatServerProtocol protocol = new ChatServerProtocol(this);
/* loop reading lines from the client which are processed
* according to our protocol and the resulting response is
* sent back to the client */
while ((msg = in.readLine()) != "quit\r\n") {
response = protocol.process(msg);
out.println("SERVER: " + response);
}
this.close();
} catch (IOException e) {
System.err.println(e);
}
}
public void sendMsg(String msg) {
out.println(msg);
}
public void close() throws IOException {
in.close();
out.close();
client.close();
}
}
客户端
/* ChatClient.java */
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
public class ChatClient {
private static int port = 1001;
private static String host = "localhost";
private static BufferedReader stdIn;
public static void main (String[] args) throws IOException {
Socket server = null;
try {
server = new Socket(host, port);
} catch (UnknownHostException e) {
System.err.println(e);
System.exit(1);
}
stdIn = new BufferedReader(new InputStreamReader(System.in));
PrintWriter out = new PrintWriter(server.getOutputStream(), true);
BufferedReader in = new BufferedReader(new InputStreamReader(server.getInputStream()));
System.out.print("Nick: ");
String auth = stdIn.readLine();
out.println("Nick: " + auth);
String serverResponse = in.readLine();
System.out.println(serverResponse);
if (serverResponse.startsWith("SERVER: welcome")) {
/* create a thread to asyncronously read messages from the server */
ServerConn sc = new ServerConn(server);
Thread t = new Thread(sc);
t.start();
String msg;
/* loop reading messages from stdin and sending them to the server */
while ((msg = stdIn.readLine()) != "quit\r\n") {
out.println(msg);
}
sc.close();
System.out.println("Exit.");
System.out.println("---Client Error---");
}
else {
System.out.println("Exit.");
System.out.println("---Client Error---");
}
}
}
class ServerConn implements Runnable {
private BufferedReader in = null;
private Socket server;
public ServerConn(Socket s) throws IOException {
server = s;
in = new BufferedReader(new InputStreamReader(s.getInputStream()));
}
public void run() {
String msg;
try {
/* loop reading messages from the server and show them
* on stdout */
while ((msg = in.readLine()) != "quit\r\n") {
System.out.println(msg);
}
this.close();
} catch (IOException e) {
System.err.println(e);
}
}
public void close() throws IOException {
in.close();
server.close();
}
}
我应该在何处以及如何关闭连接?
答案 0 :(得分:1)
我希望客户端在我输入String“quit”时停止,但我不明白我该怎么做。
您可以使用System.exit(status)
立即终止当前的Java程序。由于操作系统会自动释放进程终止时获取的所有资源(如tcp套接字),这也会关闭连接。
此外,当客户端断开连接时,我需要从连接的缺刻列表中删除客户端。
那更难了。请记住,除客户端终止之外的其他原因可能会发生断开连接,例如因为网络电缆已拔下。也就是说,客户端无法可靠地通知服务器它不再存在,服务器必须自己检测到它。我希望服务器操作系统的tcp实现能够检测到客户端不再存在(可能与实现有多快),并使用该套接字向应用程序抛出异常,您可以捕获并从中删除缺陷列表。
答案 1 :(得分:1)
现在只有当他的名字是我之前创建的图表的一部分时,才能对客户端进行身份验证。
这似乎写在代码中。在Process()
方法中,您在authorsgraph
中查找作者,如果找不到,则会返回错误。这不是你想要的吗?如果找不到作者,是否应该添加它们?也许add_nice()
调用应该在else
中,如果您还没有找到它们以及将作者添加到authorsgraph
的某种方式?
在调试器中处理新连接可能会对您有所帮助。自由使用System.out.println()
消息也可能有用。
我希望客户端在我输入String“quit”时停止,但我不明白我是怎么做到的。
这段代码有错误。您必须使用.equals()
来检查String
平等。 !=
仅测试msg
没有相同的String
引用,而不是相同的字符串内容:
// can't use != on a `String` to check contents
while ((msg = stdIn.readLine()) != "quit\r\n") {
应该是:
while (true) {
msg = stdIn.readLine();
if (msg == null || msg.equals("quit")) {
break;
}
...
另请注意,我没有检查“\ r \ n”。 readLine()
方法返回字符串(从javadoc引用)“不包括任何行终止字符”。此外,如果套接字关闭,您应该针对null
进行测试。您的客户端代码中还有2个位置具有相同的错误。
当客户端断开连接时,我需要从连接的缺刻列表中删除客户端。
如果客户端套接字关闭或进入while
,则上面的"quit"
循环将退出。然后,您致电close()
。致电close()
后,您可以致电protocol.remove_nicks()
将他从循环中删除。但是你没有"nick"
String
。
实际上,在nicks
内维护ChatServerProtocol
列表可能不适合它。我会自己将ChatServerProtocol
推送到ClientConn
课程,因为每个连接都有一个。{无论哪种方式,某个对象都需要存储作者登录的"nick"
字符串,然后在输入remove_nick()
或连接关闭后使用该字符串调用"quit"
。
最后,自Java 5以来,不推荐使用HashTable
。我会改用Collections.synchronizedMap(new HashMap<...>())
。
希望这有帮助。