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