基於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。(❤ ω ❤)
項目全部代碼以及文檔介紹

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章