一、项目分析
1-1、简单需求分析
我们先来分析一下这个聊天工具需要实现的基础功能。
1、客户端与客户端之间互相收发信息(谁发的,发给谁,时间,数据内容)
2、指定发送功能(群发或私聊)。
3、用户是否在线(类似登录、退出功能)。
4、允许异步发送消息、接收消息。
5、注册、以及更多的功能,后期看有没有机会加,先占个位(一般是烂尾项目的,哈哈,如果想了解的直接私信提醒我)。
开发用到的语言和开发工具:
- 服务端全程使用java + Eclipse开发;
- 客户端本章使用java + Eclipse进行思路熟悉开发;
- 客户端 后期会使用Android + AndroidStudio开发。
ps:版本都是JDK1.8
1-2、实例分析需求
为了便于理解,举个例子
张三、李四、王五组成一个地下秘密工作小组,使用一个特制客户端进行通信。
由于是团队作战,三人的消息需要互通,所以张三发送的消息可以让王五和李四同时接收到并且精确定位发送消息的时间,且在接收的同时可以发送。
一次行动后,张三想发消息给王五,告诉他,李四是内奸。那么此时,张三就要告诉服务端,这条消息只能发送给王五,李四无法接收到。
1-3、整体实现原理(思路)
通过传输过来的对象 (参数1:类型, 参数2:一个操作对象) 取出类型进行判断,判断本次传送的是用户接入的请求还是用户发送信息的请求。
- 如果是用户接入的请求,则取出参数二,转型为User对象,并将这个对象放入List中。
- 如果是信息发送请求,参数二则转型为MagData对象,并根据MsgData对象里面的属性,选择性传达信息。
二、基础理解代码
看完分析可能还是不太能理解,那么接下来我们直接上代码,照着敲一遍这个基础代码,应该会对进一步开发本项目有很大的帮助。
(如果基础较好,仅仅缺乏思路,看懂就行,敲不敲都行)。
这章代码有大部分会用在后面的进一步开发中,所以不是白敲的噢。
0、本章项目目录
0-1、三个对象类
User类
// 继承Serializable接口,对对象进行序列化和反序列化
public class User implements Serializable{
// 自动生成序列号
private static final long serialVersionUID = -4456125616575782442L;
private String userID; // 用户ID/用户名
private Socket socket;
public User(String userID) {
this.userID = userID;
}
public User() {
}
public String getUserID() {
return userID;
}
public void setUserID(String userID) {
this.userID = userID;
}
public void setSocket(Socket socket) {
this.socket = socket;
}
public Socket getSocket() {
return socket;
}
}
MsgData类
public class MsgData implements Serializable{
private static final long serialVersionUID = 7303068744021046271L;
private String msg; // 具体传递的消息
private String byUser; // 发送消息用户
private String toUser; // 接收消息用户
private long timestmp; // 时间戳
public MsgData(String msg, String byUser, String toUser, long timestmp) {
this.msg = msg;
this.byUser = byUser;
this.toUser = toUser;
this.timestmp = timestmp;
}
public MsgData() {
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public String getByUser() {
return byUser;
}
public void setByUser(String byUser) {
this.byUser = byUser;
}
public String getToUser() {
return toUser;
}
public void setToUser(String toUser) {
this.toUser = toUser;
}
public void setTimestmp(long timestmp) {
this.timestmp = timestmp;
}
public long getTimestmp() {
return timestmp;
}
@Override
public String toString() {
return this.byUser + "对" + this.toUser + "说:" + this.msg + "---" + this.timestmp;
}
}
PackageObj类
public class PackageObj implements Serializable{
private static final long serialVersionUID = 7438720444784119922L;
private int pagType; // 请求类型
private Object object; // 请求的对象
public PackageObj(int pagType, Object object) {
this.pagType = pagType;
this.object = object;
}
public void setObject(Object object) {
this.object = object;
}
public Object getObject() {
return object;
}
public void setPagType(int pagType) {
this.pagType = pagType;
}
public int getPagType() {
return pagType;
}
}
1、服务端代码(java、Eclipse)
package server;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import objpackage.MsgData;
import objpackage.PackageObj;
import objpackage.User;
// 服务器主类
public class ServerMain {
static List<User> userList; // 创建一个列表保存已经连接的客户端
public static void main(String[] args) {
try {
userList = new ArrayList<User>(); // 实例化
// 实例化ServerSocket,设置监听的端口号
ServerSocket serverSocket = new ServerSocket(9090);
Socket socket = null;
System.out.println("****** 服务端启动 ******");
// 阻塞监听客户端的接入,一旦接入,将继续向下执行
socket = serverSocket.accept();
// 实例化输入字节流(准备写入客户端数据阶段)
InputStream is = socket.getInputStream();
// 将输入字节流转换为对象输入流
ObjectInputStream obj = new ObjectInputStream(is);
// 读取PackageObj对象
PackageObj packageObj = (PackageObj) obj.readObject();
// 取出请求类型并判断
int objType = packageObj.getPagType();
if (objType == 0) {
System.out.println("收到登录包");
User user = (User)packageObj.getObject();
user.setSocket(socket); // 为该用户绑定一个socket
userList.add(user); // 添加进列表
System.out.println(userList.get(0).getUserID() + "登录");
}else if(objType == 1){
System.out.println("收到信息包");
MsgData msgData = (MsgData)packageObj.getObject();
System.out.println(msgData.toString());
}else {
System.out.println("数据有误");
}
socket.close(); //关闭连接
serverSocket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
2、客户端代码(java、Eclipse)
package client;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.Socket;
import objpackage.MsgData;
import objpackage.PackageObj;
import objpackage.User;
public class Client {
public static void main(String[] args) {
try {
Socket socket = new Socket("127.0.0.1", 9090);
// 登录测试包
User user = new User();
user.setUserID("李四");
PackageObj packageObj = new PackageObj(0,user);
信息测试包
// String msg = "李四是内奸"; // 信息内容
// String byUser = "张三"; // 发送者
// String toUser = "王五"; // 接收者
// long timestmp = System.currentTimeMillis(); // 时间戳
实例化自定义对象
// MsgData msgData = new MsgData(msg, byUser, toUser, timestmp);
// PackageObj packageObj = new PackageObj(1, msgData);
// 打开输出流,准备发送数据
OutputStream os = socket.getOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(os);
// 写入数据(对象)并发送
objectOutputStream.writeObject(packageObj);
objectOutputStream.flush();
// 释放资源
socket.shutdownOutput();
socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
3、测试结果
三、本章结束
好了,如果还是不理解我说的原理以及实现的思路的话,建议跟着我的代码来敲一遍,然后仔细理解。
代码适合初学者进行参考学习理解,不追求性能,代码有些地方不规范,还望原谅。
第二章产出时间还不确定,因为最近比较忙,学完本章想继续深入的,如果遇到问题欢迎私信我,很乐意和你一起进步。
最后,由于个人水平问题,文章如若有错,还望指出,谢谢。