实现思路:
要实现消息的发送,客户端每次在连接服务器端时都需要告诉服务器自己的用户名,以便能够接收到发送给自己的消息。服务器端在接收到消息时,能够查到对应用户名的客户端,将消息发送给该客户端。
服务端需要实现多个客户端的同时请求,因此需要使用多线程来处理客户端的请求。同时,它还需要保存一份用户名列表,以便在转发消息时能够查找到对应的用户。
对于客户端来说,客户端需要能够随时收取服务器端转发来的消息,并能够随时通过键盘输入发送消息,因此它的读取消息和发送消息功能是并行的,需要独立的线程来实现。
实现过程:
根据以上思路,客户端和服务端都需要独立的线程,因此分别包含了一个主程序和一个子线程类,共需要实现4个类。
(1)、客户端主程序ChatClient.java
该程序根据启动时输入的用户名参数,负责启动客户端的子线程,由子线程建立客户端和服务器的连接。在主线程中,需要循环读取键盘的输入,将输入的信息通过子线程发送给服务器,交由服务器来转发该消息给客户端。
(2)、客户端子线程ClientThread.java
该线程封装了客户端和服务器的所有操作。
a、在构造函数中,根据用户名创建与服务端的连接,然后发送用户名给服务端进行注册
b、在线程函数中,循环读取服务端发送的消息,并显示的控制台
c、还应有一个发送消息的函数,外部的主线程在接收到键盘输入后,调用该函数给服务端发送消息
d、关闭函数:在客户端输入bye命令后,关闭客户端和服务端连接。
(3)、服务端主程序ChatServer.java
该类负责启动服务器,并负责监听客户端的请求,在接收到新的用户端后,交给子线程处理。
(4)、服务端子线程ServerThread.java
该线程负责处理客户端的输入\输出和消息转发任务,因此需要包含以下功能
a、在构造函数中,建立与客户端的输入\输出流,并在第一次建立连接时读取客户端发送的用户名,并将该用户添加到用户列表中。
b、在主线程函数中,读取客户端的输入数据,如果发送的消息时bye,则推出监听关闭该客户端,否则根据命令格式进行解析。冒号(:)前的部分为用户名,根据该用户名在客户端列表中找到对应的客户端处理线程,发送数据给客户端
c、发送数据函数:在收到用户消息进行转发时调用
参考样例
1、客户端
package org.test.example; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; /** * @author kucs * 客户端主程序 * 负责调用子线程创建客户端 * 监听键盘输入 * 根据系统启动参数取得用户名 * 调用子线程创建该用户的客户端线程并启动 */ public class ChatClient { public static void main(String[] args) { try { ClientThread client = new ClientThread(args[0]); client.start(); //输入\输出流 BufferedReader sin = new BufferedReader(new InputStreamReader(System.in)); //循环读取键盘输入 String readLine; while((readLine = sin.readLine()) != null){ if(readLine.equals("bye")){ client.close(); System.exit(0); } client.send(readLine); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
2、客户端线程
package org.test.example; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.Socket; import java.net.UnknownHostException; /** * @author kucs * * 受主线程委托,来创建与服务端的连接 * 监听服务端的消息 * 负责发送消息给服务端 */ public class ClientThread extends Thread { Socket socket; //socket对象 BufferedReader is; //输入流 PrintWriter os; //输出流 //启动客户端 public ClientThread(String userName) { //连接服务器 try { socket = new Socket("127.0.0.1",12345); //输入输出流 is = new BufferedReader(new InputStreamReader(socket.getInputStream())); os = new PrintWriter(socket.getOutputStream()); //发送用户名 os.println(userName); os.flush(); } catch (UnknownHostException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } @Override public void run() { try { // TODO 循环读取服务端转发的消息 String readLine; while((readLine = is.readLine()) != null){ System.out.println(readLine); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public void close() { try { // TODO 关闭客户端 is.close(); os.close(); socket.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public void send(String readLine) { // TODO 发送消息 os.print(readLine); os.flush(); } }
3、服务端
package org.test.example; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; import java.util.Hashtable; /** * @author kucs * */ public class ChatServer { public static void main(String[] args) { ServerSocket server = null; try { // TODO 启动服务端 server = new ServerSocket(12345); System.out.println("服务端启动:"+ server.getInetAddress().getHostAddress()+":"+ server.getLocalPort()); //客户端列表 Hashtable<String, ServerThread> clientList = new Hashtable<String, ServerThread>(); //监听客户端 while(true){ Socket socket = server.accept(); //启动服务端处理线程 new ServerThread(socket,clientList).start(); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally{ try { server.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
4、服务端线程
package org.test.example; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.Socket; import java.util.Hashtable; public class ServerThread extends Thread { Socket socket; //Socket对象 BufferedReader is; //输入流 PrintWriter os; //输出流 Hashtable<String, ServerThread> clientList; //客户端列表 String userName; //用户名 public ServerThread(Socket socket, Hashtable<String, ServerThread> clientList) { this.socket = socket; this.clientList = clientList; try { //输入输出流 is = new BufferedReader(new InputStreamReader(socket.getInputStream())); os = new PrintWriter(socket.getOutputStream()); //读取用户名 this.userName = is.readLine(); clientList.put(userName, this); //显示连接信息 System.out.println(userName+"连接:" + socket.getInetAddress().getHostAddress()+":" + socket.getLocalPort()); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } @Override public void run() { // TODO 循环处理客户端输入 try { String line; while((line = is.readLine()) != null){ //用户输入信息 System.out.println(userName + "发给"+line); if(line.equals("bye")){ break; }else{ //转发消息给客户端 String[] arr = line.split(":"); if(arr.length == 2){ if(clientList.containsKey(arr[0])){ clientList.get(arr[0]).send(userName+":"+arr[1]); } } } } is.close(); os.close(); //关闭客户端 socket.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } private void send(String msg) { // TODO 发送消息 System.out.println("发给" + userName +" "+msg); os.println(msg); os.flush(); } }
本文出自 “阿酷博客源” 博客,请务必保留此出处http://aku28907.blog.51cto.com/5668513/1782187
原文地址:http://aku28907.blog.51cto.com/5668513/1782187