socket 简单的学习多人聊天的实现
前言
新手驾到,请多多指教。心血来潮,忽然想了解下socket,那最好的方法就是写个demo然后记录下来。前面可能会有些bug,因为是在写博客的时候有些是直接修改的,不是直接复制代码的。完整的代码在最后面。
先来简要说下 客户端 与 服务端 聊天(详细看下代码注释吧)
服务端
// An highlighted block
try {
ServerSocket ss = new ServerSocket(10086); // 创建服务器 端口号 10086
Socket s = ss.accept(); // 监听是否有客户端加入
BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
//读取客户端发送来的消息
String mess = br.readLine(); // br.readLine()读取客户端的输入
System.out.println("客户端:" + mess);
// 输入数据传输到当前连接的客户端
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
bw.write(mess + "\n"); // 把数据写到客户端去 \n 必须要加,不然没有消息
bw.flush();
} catch (IOException e) {
e.printStackTrace();
}
客户端
try {
Socket s = new Socket("127.0.0.1", 10088);// 创建客户端new Socket(服务端IP, 服务端端口号)
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
//向服务器端发送一条消息
bw.write("测试客户端和服务器通信,服务器接收到消息返回到客户端\n");
bw.flush();
//读取服务器返回的消息
BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
String mess = br.readLine();
System.out.println("服务器:" + mess);
} catch (IOException e) {
e.printStackTrace();
}
从上面发现,服务端和客户端都差不多,获取消息,输出啊什么的。主要注意的是每次输出的时候需要加上 \n ,服务端带有监听是否有客户端加入这个功能,建议debug下,能够发现他们的流程是这样的。
- 开启服务端,进入监听方法accept()然后等待 客户端加入,程序等待中。。。
- 开启客户端,发送消息给服务端,然后进入readLine()方法,去获取服务端的输入的值,程序等待中。。。
- 进入服务端,readLine()方法获取到客户端输入的内容,out.println打印,然后write()输入消息给客户端,程序结束。
- 进入客户端, readLine()方法获取到服务端输入的值,读取打印,程序结束。
可以加入下面这段代码,让客户端和服务端不断的在聊天
boolean fals = true;
while (fals){
// 输入数据
System.out.println("请输入:");
String str = sc.nextLine();
if ("end".equals(str)){
fals = false;
}
// 读取数据
String info = br.readLine();
System.out.println("服务端:" + info);
bw.write(str + "\n");
bw.flush();
}
下面,我们来进行 群聊功能
首先,要群聊,服务端肯定需要不断的监听客户端,那么我们用 while(true),不断的监听。然后就能获取到对应的 客户端 Socket,既然有多个,那么我们就用一个集合来 存储它们。
然后我们要用到线程,为什么呢?
从上面的流程中可以看出,服务端和客户端是一直在进行获取消息,传入消息这种不断的更替交流中,你另外一个客户端根本不可能插入进来,因为服务端一直在和最先加入的客户端交流中。
服务端代码
public static Map<String, Socket> sockets = new HashMap<>();// 这是一个群聊 可以用List创建多个群聊
private Socket socket;
private BufferedReader br;
private BufferedWriter bw;
public ThreadServerSocket(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
Scanner sc = new Scanner(System.in);
String str = "";
while (!"end".equals(str)) {// 这里可以写成true ,不过下面关闭资源会报错,因为写成死循环了,暂时先这样
// 先读取默认传过来的数据
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
// 读取客户端数据
String info = br.readLine();
System.out.println("客户端(" + socket.getPort() + "):" + info);
// 传给所有人数据
String str = sc.nextLine();
for (Map.Entry<String ,Socket> entry : sockets.entrySet()) {
bw = new BufferedWriter(new OutputStreamWriter(entry.getValue().getOutputStream()));
// 输入数据
bw.write(str + "\n");
bw.flush();
}
}
bw.close();
br.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
try {
ServerSocket serverSocket = new ServerSocket(10086);
while (true) {
Socket socket = serverSocket.accept();
sockets.put(socket.getPort() + "", socket);
Thread thread = new Thread(new ThreadServerSocket(socket), socket.getPort() + "-线程:");
thread.start();
}
} catch (IOException e) {
System.out.println("创建失败~");
}
}
客户端
try {
Socket socket = new Socket("localhost", 10086);
Scanner sc = new Scanner(System.in);
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
boolean fals = true;
while (fals){
// 输入数据
System.out.println("请输入:");
String str = sc.nextLine();
if ("end".equals(str)){
fals = false;
}
bw.write(str + "\n");
bw.flush();
// 读取数据
String info = br.readLine();
System.out.println("服务端:" + info);
}
br.close();
bw.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
多添加几个客户端,然后你会发现客户端得输入后才能看到消息,而且多个客户端时,有几个客户端输入了,服务端就得输入多少遍才能返回到客户端,客户端输入的消息别人看不到。
问题我们一个个解决:
1、有几个客户端输入了,服务端就得输入多少遍
2、客户端得输入后才能看到消息
3、客户端输入的消息别人看不到
总结下:
消息返回显示的问题,服务端输入问题,客户端消息显示问题。
我们看下服务端这边显示的值,发现聊天的信息全集中的在服务端,客户端只显示服务端的,那么,我们是不是可以把聊天信息收集起来,由服务端输入给各个客户端,不就OK了?
然后是客户端输入后才能看到消息的问题,我们是不是可以不断的循环获取服务端输入的信息,但是就不能输入了,而且会死循环。那什么办法可以让程序继续执行下去,而且又不会死循环的?
------------ 没错,线程。我们可以开启一个线程去不断的获取信息,还可以用Thread.sleep()休眠。
好,问题解决,我们接下来撸代码。
完整的代码
服务端
package com.example.test;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class ThreadServerSocket implements Runnable {
public static Map<String, Socket> sockets = new HashMap<>();// 这是一个群聊 可以用List创建多个群聊
public static volatile List<String> strings = new ArrayList<>(); // 获取到消息集合,存储消息的
private Socket socket;
private BufferedReader br;
private BufferedWriter bw;
public ThreadServerSocket(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
String str = "";
while (!"end".equals(str)) {
// 先读取默认传过来的数据
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
// 读取客户端数据
String info = br.readLine();
strings.add("客户端(" + socket.getPort() + "):" + info);
System.out.println("客户端(" + socket.getPort() + "):" + info);
// 传给所有人数据
for (Map.Entry<String ,Socket> entry : sockets.entrySet()) {
bw = new BufferedWriter(new OutputStreamWriter(entry.getValue().getOutputStream()));
// 输入数据
bw.write(strings + "\n");
bw.flush();
}
}
bw.close();
br.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
try {
ServerSocket serverSocket = new ServerSocket(10086);
while (true) {
Socket socket = serverSocket.accept();
sockets.put(socket.getPort() + "", socket);
Thread thread = new Thread(new ThreadServerSocket(socket), socket.getPort() + "-线程:");
thread.start();
}
} catch (IOException e) {
System.out.println("创建失败~");
}
}
}
客户端
package com.example.test;
import java.io.*;
import java.net.Socket;
import java.util.Scanner;
public class MyClinet {
public static void main(String[] args) {
try {
Socket socket = new Socket("localhost", 10086);
Scanner sc = new Scanner(System.in);
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
boolean fals = true;
while (fals){
new Thread(new ReaderInfoThread(br)).start();// 开启的 读服务端消息的线程
// 输入数据
System.out.println("请输入:");
String str = sc.nextLine();
if ("end".equals(str)){
fals = false;
}
bw.write(str + "\n");
bw.flush();
}
sc.close();
br.close();
bw.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
读取服务端信息
package com.example.test;
import java.io.BufferedReader;
import java.io.IOException;
public class ReaderInfoThread implements Runnable {
private BufferedReader br;
public ReaderInfoThread(BufferedReader br){
this.br = br;
}
@Override
public void run() {
try {
while (true) {
// 读取数据
String info = br.readLine();
System.out.println("服务端:" + info);
Thread.sleep(1500);
}
} catch (IOException e) {
e.printStackTrace();
}catch (InterruptedException e) {
e.printStackTrace();
}
}
}
多写几个客户端,开启服务端,客户端,测试
客户端输入
服务端显示
客户端2显示
好,完成==========
写在最后
如果发现问题,最好多debug下,走走流程,就能发现问题出现在哪!这个是博主一点点写出来的,还有很多的不足!请大家多多关照。