1. TCP多人聊天室实现
1.1 分析
客户端
功能:
1. 数据发送
2. 数据接收
技术:
1. socket
2. 输入流和输出流
3. 多线程,客户端功能模块有两个线程
聊天:
1. 群聊
2. 私聊 私聊前缀 @服务器用户ID号:msg
package com.my.chatroom.client;
import com.my.chatroom.utils.CloseUtil;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
public class Sender implements Runnable {
private DataOutputStream dop ;
private BufferedReader br ;
private boolean connect ;
/**
* @param socket
* @param userName
*/
public Sender(Socket socket , String userName) {
try {
dop = new DataOutputStream(socket.getOutputStream()) ;
br = new BufferedReader(new InputStreamReader(System.in)) ;
send(userName);
connect = true ;
} catch (IOException e) {
e.printStackTrace();
connect = false ;
}
}
/**
* 从键盘获得输入的消息
* @return 返回用户输入的消息字符串
*/
public String getMessage() {
String message = null ;
try {
message = br.readLine() ;
} catch (IOException e) {
e.printStackTrace();
connect = false ;
CloseUtil.closeAll(br,dop);
}
return message ;
}
/**
* 发送消息给服务器
* @param message
*/
public void send(String message) {
if (message != null && ! "".equals(message)) {
try {
dop.writeUTF(message);
dop.flush();
} catch (IOException e) {
e.printStackTrace();
connect = false ;
CloseUtil.closeAll(dop,br);
}
}
}
/**
* 线程任务:只要是连接状态就不停获取和发送消息
*/
@Override
public void run() {
while (connect) {
send(getMessage());
}
}
}
package com.my.chatroom.client;
import com.my.chatroom.utils.CloseUtil;
import java.io.DataInputStream;
import java.io.IOException;
import java.net.Socket;
public class Receiver implements Runnable {
private DataInputStream dis ;
private boolean connect ;
/**
* 根据客户端连接服务器时的Socket对象获取输入流对象
* @param socket
*/
public Receiver(Socket socket) {
try {
dis = new DataInputStream(socket.getInputStream()) ;
connect = true ;
} catch (IOException e) {
e.printStackTrace();
connect = false ;
}
}
/**
* 接受并展示消息
*/
public void receive() {
String message = null ;
try {
message = dis.readUTF() ;
} catch (IOException e) {
e.printStackTrace();
connect =false ;
CloseUtil.closeAll(dis);
}
System.out.println(message);
}
/**
* 只要是连接状态就一直接收消息
*/
@Override
public void run() {
while (connect) {
receive();
}
}
}
服务器
功能:
1. 数据转发
2. 用户注册
技术:
1. ServerSocket
2. 每一个用户对应的Sokcet对象
3. 多线程同时在线
4. HashMap<Integer, 用户>
数据转发: 私聊前缀判断 群聊所有人发送
package com.my.chatroom.server;
import com.my.chatroom.utils.CloseUtil;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Collection;
import java.util.HashMap;
public class Server {
private HashMap<Integer , UserSocket> userMap ;
private static int count = 0 ;
public Server() {
userMap = new HashMap<Integer, UserSocket>() ;
}
public void start() throws IOException {
ServerSocket serverSocket = new ServerSocket(8080) ;
System.out.println("服务器启动!");
while (true) {
Socket socket = serverSocket.accept() ;
count += 1 ;
UserSocket userSocket = new UserSocket(count , socket) ;
userMap.put(count , userSocket) ;
new Thread(userSocket).start();
}
}
class UserSocket implements Runnable {
private DataInputStream dis ;
private DataOutputStream dos ;
private int userId ;
private String userName;
private boolean connect ;
/**
* 创建UserSocket对象需要userid和socket对象
* @param userid
* @param socket
*/
public UserSocket(int userid , Socket socket) {
this.userId = userid ;
try {
dis = new DataInputStream(socket.getInputStream()) ;
dos = new DataOutputStream(socket.getOutputStream()) ;
connect = true ;
} catch (IOException e) {
e.printStackTrace();
connect = false;
}
try {
assert dis != null ;
this.userName = dis.readUTF() ;
chatStyle("ID:" + this.userId + " " + this.userName + "来到直播间", true);
send("欢迎来到聊天室!");
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 接收用户发送的消息并做转发处理
* @return
*/
public String receice() {
String message = null ;
try {
message = dis.readUTF() ;
} catch (IOException e) {
e.printStackTrace();
connect = false ;
CloseUtil.closeAll(dis , dos);
}
return message ;
}
/**
* 转发消息给用户
* @param message
*/
public void send(String message) {
try {
dos.writeUTF(message);
dos.flush();
} catch (IOException e) {
e.printStackTrace();
connect = false;
CloseUtil.closeAll(dis , dos);
}
}
/**
* 判断聊天方式:私聊或者群聊,并判断是否是系统消息
* @param message
* @param sys
*/
public void chatStyle(String message , boolean sys){
if (message.startsWith("@") && message.contains(":")) {
Integer id = Integer.parseInt(message.substring(1,message.indexOf(":"))) ;
String newMessage = message.substring(message.indexOf(":")) ;
UserSocket userSocket = userMap.get(id) ;
if (userSocket != null) {
userSocket.send("ID" + this.userId + " " + this.userName + "悄悄对你说:" + message);
}
} else {
Collection<UserSocket> userSockets = userMap.values() ;
for (UserSocket userSocket : userSockets) {
if (userSocket != this) {
if (sys) {
System.out.println("系统公告:" + message);
} else {
System.out.println("ID" + this.userId + " " + this.userName + message);
}
}
}
}
}
@Override
public void run() {
while (connect) {
chatStyle(receice() , false);
}
}
}
public static void main(String[] args) {
Server server = new Server() ;
try {
server.start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
1.2 客户端实现
数据发送: 使用输出流发送数据给服务器 遵从Runnable接口
数据接收: 使用输入流从服务器端接收数据 遵从Runnable接口
客户端主方法: 用户名提交 数据发送 数据接收 多线程启动
package com.my.chatroom.client;
import com.my.chatroom.utils.CloseUtil;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Client {
public static void main(String[] args) {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in)) ;
Socket socket = null ;
String name = null ;
try {
System.out.println("请输入您的用户名:");
name = br.readLine() ;
if (!"".equals(name)) {
return;
}
socket = new Socket(InetAddress.getLocalHost() , 8080) ;
} catch (IOException e) {
System.out.println("连接失败!");
CloseUtil.closeAll(br);
System.exit(0);
}
ExecutorService pool = Executors.newFixedThreadPool(2) ;
pool.submit(new Sender(socket , name)) ;
pool.submit(new Receiver(socket)) ;
}
}
1.3 资源关闭问题
代码中操作了大量的输入流和输出流,这里都需要进行关闭操作。
DataInputStream, DataOutputStream, BufferedReader, Socket
以上这些资源都是Closeable接口的实现类,都有对应的 Close方法
封装一个工具类: 提供一个closeAll方法,参数为符合Closeable接口的 实现类对象。
这里可以考虑可变长参数
Closeable… closeable
可变长参数在方法中使用的过程里面是对应一个数组,这 里完成可以使用增强for来使用
工具类名: CloseUtil public static void closeAll(Closeable… closeable)
package com.my.chatroom.utils;
import java.io.Closeable;
import java.io.IOException;
public class CloseUtil {
public static void closeAll(Closeable... closeables) {
try {
for (Closeable closeable : closeables) {
if (closeable != null) {
closeable.close();
}
}
}catch (IOException e) {
e.printStackTrace();
}
}
}
1.4 功能拓展
1. 用户退出
用户输入指定字段之后可以退出 客户端Socket服务 服务端Socket服务 涉及资源关闭,线程关闭
2. 用户异常退出
在运行过程中发现问题,需要及时处理,关闭对应的资 源,终止对应的线程
3. 服务器保存所有的聊天记录
2. JSON
2.1 JSON格式概述
JSON JavaScript Object Notation(JavaScript Object Notation,JavaScript对象表示法,读作/ˈdʒeɪsən/)是一种由道格拉斯·克罗克福特构想 和设计、轻量级的数据交换语言,该语言以易于让人阅读的文 字为基础,用来传输由属性值或者序列性的值组成的数据对 象。尽管JSON是JavaScript的一个子集,但JSON是独立于语 言的文本格式,并且采用了类似于C语言家族的一些习惯
2.2 数据格式
2.2.1 JSON对象
特征:
1. 数据形式键值对形式 “键”:值
2. 数据支持 字符串,数字,true false
3. {} 大括号以内的数据
2.2.2 JSON对象数组
特征:
1. 数据使用[]包含
2. 在[]都是JSON格式对象
3. 每一个对象之间使用逗号隔开,同时最后一个元素不 需要逗号
2.2.3 JSON数据验证
2.3 解析JSON格式工具
常用的工具: Gson,fastjson, Jackson 以上都是第三方工具,需要导入对应的jar包按使用 XML导包
2.3.1 FastJson内容
JSON核心类
JSON核心类提供解析和转化方法,用于解析JSON数据格 式,同时用于转换类对象到JSON格式,该类对象需要符合 JavaBean规范
–| JSONArray 存在按照键值对方式解析获取数据,同时存在一定 的List方法
–| JSONObject 获取对应的类对象,指定键值对对应数据的方法
package com.my.demo1;
/*
完成一个符合JavaBean规范的类
*/
public class Student {
private String name;
private Integer age;
// 这里根据需要完成对应的Setter和getter方法
public Student() {
}
public Student(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
package com.my.demo1;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import java.util.ArrayList;
import java.util.List;
public class Demo1 {
public static void main(String[] args) {
ArrayList<Student> students = new ArrayList<>() ;
students.add(new Student("飞哥" , 22)) ;
students.add(new Student("小明" , 22)) ;
students.add(new Student("老段" , 23)) ;
students.add(new Student("羽羽" , 21)) ;
String str = JSON.toJSONString(students) ;
System.out.println(str);
System.out.println("----------------------------------------------------------------------");
Student student = new Student("自豪" , 23) ;
String str2 = JSON.toJSONString(student) ;
System.out.println(str2);
Student student2 = JSON.parseObject(str2 , Student.class) ;
System.out.println(student2);
JSONObject jsonObject = JSON.parseObject(str2) ;
String name = jsonObject.getString("name") ;
Integer age = jsonObject.getInteger("age") ;
System.out.println(name);
System.out.println(age);
System.out.println("----------------------------------------------------------------------");
JSONArray objects = JSON.parseArray(str) ;
System.out.println(objects);
for (Object object : objects) {
JSONObject jsonObject1 = (JSONObject) object;
System.out.println(jsonObject1.getString("name"));
System.out.println(jsonObject1.getInteger("age"));
}
Student student3 = objects.getObject(1 , Student.class) ;
System.out.println(student3);
List<Student> students2 = objects.toJavaList(Student.class) ;
for (Student stu : students2) {
System.out.println(stu);
}
}
}