I. Description:
This example is a simple Java TCP multiplayer chat rooms, start the server, and then start the client, the client typing a user name, and then you can start chatting, type information, each online user will receive the appropriate information .
As shown below:
II. Server-side code
ServerMain.java
import java.io.*; public class ServerMain { public static void main(String[] args) { ChatServer server = new ChatServer(); try { server.ServerStart(); } catch (IOException e) { e.printStackTrace(); } } }
ChatServer.java
import java.io.*; import java.net.*; import java.util.HashMap; public class ChatServer { // 设定端口号 private final int port = 12345; public static HashMap<ClientThread,Socket> clientMap = new HashMap<>(); public static int onlineNum = 0; public static void ServerSendToAll(ClientThread thread,String msg) throws IOException { for (ClientThread t : clientMap.keySet()) { if (t.clientName.equals(thread.clientName)) { Message.printMessage(thread.clientName + "说:" + msg); continue; } t.objOutput.writeObject(new DataPacket(Message.CMD.SENDALL, thread.clientName + "说:" + msg)); } } public static void ServerSendToAll(ClientThread thread) throws IOException { onlineNum++; // 服务端记录 Message.printMessage(thread.clientName + "上线了,当前在线人数" + onlineNum + "人"); // 群发 for (ClientThread t : clientMap.keySet()) { if (t.login == true) { t.objOutput.writeObject(new DataPacket(Message.CMD.LOGIN, "当前在线人数" + onlineNum + "人")); } } } public void ServerStart() throws IOException { ServerSocket serverSocket = new ServerSocket(port); Message.printMessage("Server Start."); // 死循环等待客户端连接 while (true) { // 每来一个客户端连接,保存Socket对象 Socket clientSocket = serverSocket.accept(); ClientThread client = new ClientThread(clientSocket); // 每连接一个客户端,创建一个对应的线程类 Thread clientThread = new Thread(client); // 添加对象-Socket映射 clientMap.put(client, clientSocket); // 启动线程 clientThread.start(); } } }
ClientThread.java
import java.io.*; import java.net.*; /* 线程类:处理每一个登录成功的客户端 */ public class ClientThread implements Runnable { // 保存每个客户端Socket private Socket client = null; public ObjectInputStream objInput = null; public ObjectOutputStream objOutput = null; public String clientName = null; public boolean login = false; public ClientThread(Socket client) { this.client = client; try { this.objInput = new ObjectInputStream(this.client.getInputStream()); this.objOutput = new ObjectOutputStream(this.client.getOutputStream()); } catch (IOException e) { Message.printLog(Message.logType.ERROR, "ClientThread:IOException!", true); e.printStackTrace(); } } public int HandleReceiveMsg(Object data) throws IOException { DataPacket dataPacket = (DataPacket) data; switch (dataPacket.cmd) { case LOGIN: clientName = dataPacket.msg; login = true; ChatServer.ServerSendToAll(this); break; case SEND: // 收到消息,调用群发其他用户 ChatServer.ServerSendToAll(this, dataPacket.msg); break; case SENDALL: // 收到其他用户群发的消息 SendMessage(dataPacket); break; } return 0; } public void SendMessage(DataPacket data) throws IOException { this.objOutput.writeObject(data); } public Object ReceiveMessage() throws ClassNotFoundException, IOException { try { Object obj = this.objInput.readObject(); return obj; } catch (EOFException e) { } return null; } @Override public void run() { try { Object data; while ((data = ReceiveMessage()) != null) { HandleReceiveMsg(data); } } catch (SocketException e) { login = false; } catch (IOException e) { Message.printLog(Message.logType.ERROR, "ClientThread-run:IOException!", true); e.printStackTrace(); } catch (ClassNotFoundException e) { Message.printLog(Message.logType.ERROR, "ClientThread-run:ClassNotFoundException!", true); e.printStackTrace(); } finally { // 用户断开连接 if (login == false) { // 打印信息 Message.printMessage(clientName + "下线了!"); // 从HashMap 移走映射 ChatServer.clientMap.remove(this); // 释放资源 if (client != null) { try { client.close(); } catch (IOException e) { e.printStackTrace(); } } } } } }
III. The client code
ClientMain.java
public class ClientMain { public static void main(String[] args) { ChatClient client = new ChatClient(); client.ClientStart(); } }
ChatClient.java
import java.io.*; import java.net.*; import java.util.Scanner; public class ChatClient { private static final int port = 12345; private static String serverAddress = "localhost"; private Socket client = null; private ObjectOutputStream objOutput = null; private ObjectInputStream objInput = null; public void SendMessage(DataPacket data) throws IOException { // 发送数据给服务器 objOutput.writeObject(data); } public void ClientStart() { Scanner in = new Scanner(System.in); String userInput = null; // 用户输入用户名登录 Message.printLog(Message.logType.INFO, "请输入你的用户名:", false); userInput = in.nextLine(); // 连接服务器 try { this.client = new Socket(serverAddress, port); // 服务器已连接 if (this.client.isConnected()) { // 获取输出流 this.objOutput = new ObjectOutputStream(this.client.getOutputStream()); // 获取输入流 this.objInput = new ObjectInputStream(this.client.getInputStream()); SendMessage(new DataPacket(Message.CMD.LOGIN, userInput)); // 创建一个线程用于接收信息 ClientReceiveMessageThread receiveMessage = new ClientReceiveMessageThread(this.objInput, userInput); Thread receiveThread = new Thread(receiveMessage); receiveThread.start(); // 这个线程用于发送信息 while (true) { userInput = in.nextLine(); if (userInput != null) { SendMessage(new DataPacket(Message.CMD.SEND, userInput)); } } } } catch (ConnectException e) { Message.printLog(Message.logType.ERROR, "ChatClient-ClientStart:ConnectException!", true); e.printStackTrace(); } catch (SocketException e) { Message.printLog(Message.logType.ERROR, "服务器异常!", true); System.exit(0); } catch (IOException e) { Message.printLog(Message.logType.ERROR, "ChatClient-ClientStart:IOException!", true); e.printStackTrace(); } } } class ClientReceiveMessageThread implements Runnable { private ObjectInputStream in = null; private String clientName = null; public ClientReceiveMessageThread(ObjectInputStream in, String name) { this.in = in; this.clientName = name; } public int HandleReceiveMsg(Object data) throws IOException { DataPacket dataPacket = (DataPacket) data; switch (dataPacket.cmd) { case LOGIN: case SEND: case SENDALL: Message.printMessage(dataPacket.msg); break; } return 0; } @Override public void run() { Object data; try { while ((data = in.readObject()) != null) { HandleReceiveMsg(data); } } catch (EOFException e) { } catch (SocketException e) { Message.printLog(Message.logType.ERROR, "服务器异常!", true); System.exit(0); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } }
IV. Common class
Message.java
import java.text.SimpleDateFormat; import java.util.Date; public class Message { // 是否打开debug输出 private static boolean debug = false; // 设定日期时间格式 public static SimpleDateFormat dateFormat = new SimpleDateFormat("yyy-MM-dd hh:mm:ss"); public static enum logType { ERROR, INFO, WARN, DEBUG, }; public static enum CMD { LOGIN, SEND, SENDALL, } public static void printLog(logType type, String msg, boolean newLine) { if( debug == true || type != logType.DEBUG ) { StringBuilder sb = new StringBuilder(128); sb.append("[").append(type).append("]").append(msg); if (newLine) System.out.println(sb); else System.out.print(sb); } } public static void printMessage(String msg) { StringBuilder sb = new StringBuilder(1024); sb.append("[").append(dateFormat.format(new Date())).append("]").append(msg); System.out.println(sb); } }
DataPacket.java
import java.io.Serializable; public class DataPacket implements Serializable { /** * 自动生成的UID */ private static final long serialVersionUID = -5366828169862565011L; public Message.CMD cmd; public String msg; public DataPacket(Message.CMD cmd, String msg) { this.cmd = cmd; this.msg = msg; } }