一、思路简介
8 P. a" t. T9 G4 x 分为客户端和服务器两个类,所有的客户端将聊的内容发送给服务器,服务器接受后,将每一条内容发送给每一个客户端,客户端再显示在终端上。
@: ~2 K+ v- q& s1 p 二、客户端设计 * P$ c$ U4 ]/ D& P
客户端包含2个线程,1个用来接受服务器的信息,再显示,1个用来接收键盘的输入,发送给服务器。9 q, @# Z) _2 @5 Y
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;
public class WeChatClient { //WeChat的客户端类
private Socket client;
private String name;
private InputStream in;
private OutputStream out;
private MassageSenter massageSenter;
private MassageGeter massageGeter;
class MassageGeter extends Thread{ //一个子线程类,用于客户端接收消息
MassageGeter() throws IOException{
in = client.getInputStream();
}
@Override
public void run() {
int len;
byte[] bytes = new byte[1024];
try {
while ((len = in.read(bytes)) != -1) { //此函数是阻塞的
System.out.println(new String(bytes,0,len, StandardCharsets.UTF_8));
}
}catch (IOException e){
System.out.println(e.toString());
}
System.out.println("Connection interruption");
}
}
class MassageSenter extends Thread{ //一个子线程类,用于发送消息给服务器
MassageSenter() throws IOException{
out = client.getOutputStream();
}
@Override
public void run() {
Scanner scanner = new Scanner(System.in);
try {
while (scanner.hasNextLine()) { //此函数为阻塞的函数
String massage = scanner.nextLine();
out.write((name + " : " + massage).getBytes(StandardCharsets.UTF_8));
if(massage.equals("//exit"))
break;
}
}catch (IOException e){
e.printStackTrace();
}
}
}
WeChatClient(String name, String host, int port) throws IOException {//初始化,实例化发送和接收2个线程
this.name = name;
client = new Socket(host,port);
massageGeter = new MassageGeter();
massageSenter = new MassageSenter();
}
void login() throws IOException{//登录时,先发送名字给服务器,在接收到服务器的正确回应之后,启动线程
out.write(name.getBytes(StandardCharsets.UTF_8));
byte[] bytes = new byte[1024];
int len;
len = in.read(bytes);
String answer = new String(bytes,0,len, StandardCharsets.UTF_8);
if(answer.equals("logined!")) {
System.out.println("Welcome to WeChat! "+name);
massageSenter.start();
massageGeter.start();
try {
massageSenter.join();//join()的作用是等线程结束之后再继续执行主线程(main)
massageGeter.join();
}catch (InterruptedException e){
System.err.println(e.toString());
}
}else{
System.out.println("Server Wrong");
}
client.close();
}
public static void main(String[] args) throws IOException{//程序入口
String host = "127.0.0.1";
WeChatClient client = new WeChatClient("Uzi",host,7777);
client.login();
}
}
三、服务器设计
2 x$ r6 g3 S, C+ P1 j8 l: l3 K$ c2 ] 服务器包含3个线程类,端口监听线程,客户端接收信息线程,发送信息线程。服务器类还包含并维护着一个已经连接的用户列表,和一个待发送信息列表。+ k7 s! u/ |, M, ]' |# \6 A
服务器有一个负责监听端口的线程,此线程在接收到客户端的连接请求后,将连接的客户端添加进用户列表;并为每一个连接的客户端实例化一个接受信息的线程类,从各个客户端接收员信息,并存入待发送信息列表。发送信息线程查看列表是否为空,若不为空,则将里面的信息发送给用户列表的每一个用户。) k, a. n( }% H: q( A7 m$ z