一.服务器端思路整理
? ? ? ? 想要实现聊天室,对于服务器端由于需要一直监听用户的连接以及用户的消息发送,所以需要多线程来为每一个用户分配一个线程来实现功能。
实现start方法,创建serverSocket链接,死循环用以监听客户端用户的链接,当一个用户链接之后,就为其新开一个线程。通过这个新开的线程来实现对每个用户的功能。
public void start() throws IOException {
ServerSocket server = new ServerSocket(8888);
while(true) {
Socket socket = server.accept();
System.out.println(socket.getInetAddress().getHostAddress()+"正在链接!!!");
ThreadSocket client = new ThreadSocket(socket);
new Thread(client).start();
}
}
创建一个成员变量用来储存每一个用户的线程:
List<ThreadSocket> clients = new ArrayList<ThreadSocket>();
?我是创建了一个线程内部类,方便我直接使用这个线程数组,因为在一个用户登录的时候,服务器会给每一个用户广播一条欢迎消息,每个用户连接只有一次,所以我选择在线程的构造函数中将此线程添加到线程数组,并执行广播方法send(),此方法通过遍历线程数组,给每个线程的socket添加输出流,推送欢迎消息
public class ThreadSocket implements Runnable{
Socket socket;
String name;
public ThreadSocket(Socket socket) throws IOException {
this.socket = socket;
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
name = br.readLine();
String welcome = name+"["+socket.getInetAddress().getHostAddress()+"]来到了房间";
clients.add(this);
send(welcome);
System.out.println(welcome);
}
private void send(String welcome) throws IOException {
for (ThreadSocket c : clients) {
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(c.socket.getOutputStream()));
bw.write(welcome);
bw.newLine();
bw.flush();
}
}
}
单独讲此线程的run方法:由于用户发送消息需要服务器一直监听,等待接受,所以必须写在死循环中,需要同客户端约定一个退出方式,因为一旦一方关闭,另一方socket.getInputStream()就会接受不到,此时程序会不断循环输出异常。
public void run() {
while(true) {
BufferedReader br;
try {
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String msg = br.readLine();
String name1 = name+"["+socket.getInetAddress().getHostAddress()+"]";
if(msg.equals("byebye")) {
System.out.println(name1+"离开了聊天室");
send(name1+"离开了聊天室");
clients.remove(this);
socket.close();
break;
}
send(name1+":"+msg);
System.out.println(name1+":"+msg);
} catch (IOException e) {
e.printStackTrace();
}
}
}
主方法:通过对象调用非静态方法
public static void main(String[] args) throws IOException {
new Server().start();
}
二.客户端
主要注意的就是客户端与服务器端的发送接受关系,必须一一对应好,同样,客户端单线程实现不了一直监听服务器推送信息以及一直监听用户发送信息,在规定的退出指令中,需要注意当客户端输入推出指令后,客户端等待服务器推送的消息的线程仍在等待,所以我让服务器给本人也推送一个退出信息,不然也会爆出异常。
public class Client {
public static void main(String[] args) {
Socket socket=null;
try {
socket = new Socket("10.7.184.72",8888);
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
System.out.println("请输入用户名:");
Scanner sc = new Scanner(System.in);
bw.write(sc.next());
bw.newLine();
bw.flush();
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
System.out.println(br.readLine());
ClientThread clientThread = new ClientThread(socket);
clientThread.start();
while(true) {
System.out.print("请输入发送的内容:");
String input = sc.next();
bw.write(input);
bw.newLine();
bw.flush();
if(input.equals("byebye")) {
clientThread.interrupt();
break;
}
}
} catch (IOException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
}
客户端新开线程类:当用户输入退出指令后,while循环判定为false,但是此时线程还在br = new BufferedReader(new InputStreamReader(socket.getInputStream()));等待消息,所以跟服务器的关系发送要对应好。
class ClientThread extends Thread{
Socket socket;
public ClientThread(Socket socket) {
this.socket=socket;
}
@Override
public void run() {
while (!Thread.interrupted()) {
BufferedReader br;
try {
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
System.out.println(br.readLine());
} catch (IOException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
}
}
三.运行截图:
服务器端:
客户端:
?
?
此为新手向简易聊天室,大家有什么问题和建议,欢迎提出,大家一起讨论。
?
|