基于TCP的QQ聊天工具

###前言:
基于JAVA语言开发的一款网络聊天工具,通过Socket实现TCP编程,使用多线程实现了多客户端的连接。模仿腾讯QQ的界面,功能较为简单,但是使用了最基本的网络编程技术,如socket、tcp、I/O阻塞、多线程、MySQL数据库等。

###功能介绍:

这里写图片描述

用户注册:系统随机生成账号,用户按照要求填入相应的信息。
用户登录:用户根据自己的账号和密码进行登录。
好友聊天:登录成功后,点击好友列表中的好友进行聊天。

###注册模块

这里写图片描述

该界面主要给用户提供注册新账号的功能。注册账号时除了自动生成的账号和输入的密码外,还需要进行身份的验证,其中包括真实姓名、性别 、手机号。
  客户端获取用户输入的信息后,将用户信息封装为User对象,将请求封装为CommandTranser对象,发送给服务器。代码如下:

User user = new User(username, password,realname,sex,phone);
CommandTranser msg = new CommandTranser();
msg.setCmd("checkregist");
msg.setData(user);
msg.setReceiver(username);
msg.setSender(username);
//实例化客户端 并且发送数据 这个client客户端 直到进程死亡 否则一直存在
Client client = new Client();
client.sendData(msg);
msg = client.getData();

在客户端对输入的信息进行了校验,不允许输入的信息为空,并给出相应的系统提示信息,通过客户端向服务器发送的注册请求,服务器进行数据库的校验,同样返回系统提示信息。

//注册时,查询是否已经注册该账号
public boolean checkregistUser(User user) {
	PreparedStatement stmt = null;
	Connection conn = null;
	ResultSet rs = null;
	conn = DBHelper.getConnection();
	String sql = "select * from user where username=?";
	try {
		stmt = conn.prepareStatement(sql);
		stmt.setString(1, user.getUsername());
		rs = stmt.executeQuery();
		if (rs.next()) {
			return true;
		}
	}
	return false;
}

这里写图片描述
这里写图片描述

###登录模块

点击了登录后,首先判断账号和密码是否为空,然后封装为CommandTranser对象,向服务器发送数据,服务器通过与数据库进行比对来验证账号密码。
  客户端获取用户输入的信息以及发送请求的代码如下:

if (e.getSource() == login) {
	String username = text_name.getText().trim();
	String password = new String(text_pwd.getPassword()).trim();
	if ("".equals(username) || username == null) {
		JOptionPane.showMessageDialog(null, "请输入帐号!!");
		return;
	} if ("".equals(password) || password == null) {
		JOptionPane.showMessageDialog(null, "请输入密码!!");
		return;
	}
	User user = new User(username, password);
	CommandTranser msg = new CommandTranser();//封装msg对象发送给服务器
	msg.setCmd("login");msg.setData(user);
	msg.setReceiver(username);msg.setSender(username);
	// 实例化客户端,并且发送数据,这个client客户端直到进程死亡,否则一直存在
	Client client = new Client();
	client.sendData(msg);
	msg = client.getData();
	if (msg != null) {
		if (msg.isFlag()) {
			JOptionPane.showMessageDialog(null, "登陆成功!");
			new FriendsUI(username, client); // 显示好友列表界面
		} else {
			JOptionPane.showMessageDialog(this, msg.getResult());
		}
	}
}

服务器Userservice类操作数据库对登录进行验证,如果账号和密码正确,则返回true,否则返回false。实现代码如下:

public boolean checkUser(User user) {
	PreparedStatement stmt = null;
	Connection conn = null;
	ResultSet rs = null;
	conn = DBHelper.getConnection();
	String sql = "select * from user where username=? and password =?";
	try {
		stmt = conn.prepareStatement(sql);
		stmt.setString(1, user.getUsername());
		stmt.setString(2, user.getPassword());
		rs = stmt.executeQuery();
		if (rs.next()) {
			return true;
		}
	}
	return false;
}

如果登录成功,则将生成一个SocketThread对象,包括一个socket,一个用户账号,将该SocketThread加入到HashMap<String,Socket>集合中,并且开启此用户的接收线程。代码如下:

private CommandTranser execute(CommandTranser msg) {
if ("login".equals(msg.getCmd())) { //登录请求
	UserService userService = new UserService();
	User user = (User) msg.getData();
	msg.setFlag(userService.checkUser(user));
	/*
	 * 如果登陆成功,将该客户端加入已经连接成功的map集合里面 并且开启此用户的接受线程
	 */
	if (msg.isFlag()) {
		// 将该线程加入连接成功的map集合
		SocketThread socketThread = new SocketThread();
		socketThread.setName(msg.getSender());
		socketThread.setSocket(socket);
		SocketList.addSocket(socketThread);
		msg.setResult("登陆成功");
	} else {
		msg.setResult("账号密码输入错误!");
	}
}

HashMap<String,Socket>用于记录所有已经成功登录的客户端:

public class SocketList {
	private static HashMap<String, Socket> map=new HashMap<String, Socket>();
	//将SocketThread入集合
	public static void addSocket(SocketThread socketThread){
		map.put(socketThread.getName(), socketThread.getSocket());
	}
	public static Socket getSocket(String name){ //通过昵称返回socket
		return map.get(name);
	}
}

一旦聊天功能启动,为每一个用户开启一个线程,通过I/O阻塞,client.getData()接收服务器发送的数据,如果服务器处理数据成功,接收消息,否则弹出该好友不在线的系统提示信息。
  客户端获取消息内容、发送方和接收方,通过CommandTranser向服务器发送数据,代码如下:

public void actionPerformed(ActionEvent e) 
	if (e.getSource() == send_btn) {// 如果点击了发送按钮
		Date date = new Date();
		SimpleDateFormat sdf = new SimpleDateFormat("hh:mm:ss a");
		String message = "你说:" + message_txt.getText() + "\t"
				+ sdf.format(date) + "\n";
		chat_txt.append(message);// 在本地文本区追加发送的信息
		CommandTranser msg = new CommandTranser();//msg为客户端向服务器发送的数据
		msg.setCmd("message");
		msg.setSender(owner);
		msg.setReceiver(friend);
		msg.setData(message_txt.getText());
		client.sendData(msg);
		message_txt.setText(null);// 发送信息完毕 写信息的文本框设空
	}
}

服务器时刻监听客户端发送的消息,并调用CommandTranser execute(CommandTranser msg)方法传入message进行处理,如果处理成功,则可以向该好友发送消息,如果服务器处理信息失败,信息发送给自己,代码如下:

public void run() {
	ObjectInputStream ois = null;
	ObjectOutputStream oos = null;
	while (socket != null) {// 时刻监听 客户端发送来的数据
		try {
			ois = new ObjectInputStream(socket.getInputStream());
			CommandTranser msg = (CommandTranser) ois.readObject();
			msg = execute(msg);
			if ("message".equals(msg.getCmd())) {//服务器处理消息
				if (msg.isFlag()) {
					oos = new ObjectOutputStream(SocketList.getSocket(
							msg.getReceiver()).getOutputStream());
				} else {
					oos = new ObjectOutputStream(socket.getOutputStream());
				}
			}
			oos.writeObject(msg);
		} 
}

服务器对发送的消息进行处理,判断好友是否在线,然后将msg返回:

// 处理客户端发送的信息
private CommandTranser execute(CommandTranser msg) {
	//如果是发送消息的指令,判断当前用户是否在线
	if ("message".equals(msg.getCmd())) {
		// 如果要发送的用户在线 发送信息
		if (SocketList.getSocket(msg.getReceiver()) != null) {
			msg.setFlag(true);
		} else {
			msg.setFlag(false);
			msg.setResult("当前用户不在线");
		}
	}
	return msg;
}

服务器将所有的操作做完后,客户端再通过getData()接收从服务器返回的数据,并在聊天界面中进行展示,代码如下:

public class ClientThread extends Thread {
	private Client client;//客户端对象
	public void run() {
		while (isOnline) {
			//I/O阻塞,接收服务端发送的数据
			CommandTranser msg = client.getData();
			if (msg != null) {
				if (msg.isFlag()) {
					Date date = new Date();
					SimpleDateFormat sdf = new SimpleDateFormat(
							"hh:mm:ss a");
					String message = msg.getSender() + "说:"+ (String) msg.getData() + "\t" + sdf.format(date)+ "\n";
					chat_txt.append(message);
				} else {
					JOptionPane.showMessageDialog(chat_txt, msg.getResult());
				}
			}
		}
	}
}

####注: 由于水平有限,做的比较low,项目全部代码以及文档请查看我的Github,如果喜欢,请给我点个Star。(❤ ω ❤)
项目全部代码以及文档介绍

发布了128 篇原创文章 · 获赞 239 · 访问量 33万+

猜你喜欢

转载自blog.csdn.net/HLK_1135/article/details/63710126