Java——實現聊天室
阿新 • • 發佈:2019-01-30
學習Java的每一個人都知道,聊天室是每一個程式設計師都要過手的專案,根據要求的不同,聊天室的實現可易可難。
我今天的聊天室程式主要實現的功能是:
1、私聊功能
2、群聊功能
3、檢視成員列表功能
4、退出聊天室功能
5、傳送檔案功能
內容比較簡單,是學完JavaSE的一次知識總結,沒有用到介面等,都是在控制檯上進行輸出的。希望能給大家提供借鑑。程式碼如下,程式在最後打包,希望對於初學者的同學
package org.westos.client; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import java.util.Scanner; import org.westos.client.Config.Config; import org.westos.util.InputAndOutputUtil; import org.westos.util.InputUtil; /** * 客戶端 * @author 虎 * */ public class Client { private static Socket s; private static InputStream is; private static OutputStream os; private static Scanner sc; private static Config config; private static String clientName; public static void main(String[] args) { sc = new Scanner(System.in); try { s = new Socket("LocalHost", 9999); os = s.getOutputStream(); is = s.getInputStream(); //註冊------->不停的去註冊 while(true) { System.out.println("請設定您的暱稱"); clientName = sc.nextLine(); os.write(clientName.getBytes()); os.flush(); //讀取伺服器的反饋 byte[] result = new byte[1024]; int len = is.read(result); String str = new String(result, 0, len); if(str.equals("yes")) { System.out.println("註冊成功"); break; } else { System.out.println("請重新輸入"); } } ClientReadThread thread = new ClientReadThread(is); thread.start(); boolean isRun = true; //開始聊天 while(isRun) { System.out.println("請選擇要進行的操作: 1,私聊 2,公聊 3,線上列表 4 ,退出 聊天 5,傳送檔案6,下載檔案"); int chioce =InputUtil.inputIntType(new Scanner(System.in)); switch (chioce) { case 1: System.out.println(1); privateChat(); break; case 2: System.out.println(2); publicChat(); break; case 3: System.out.println(3); onlineList(); break; case 4: System.out.println(4); exitChat(); isRun = false; break; case 5: System.out.println(5); sendFile(); break; case 6 : System.out.println(6); downloadFile(); break; } } } catch (Exception e) { e.printStackTrace(); } System.exit(0); } private static void downloadFile() { // TODO Auto-generated method stub } private static void sendFile() throws IOException { //現主要實現私發 System.out.println("請輸入接收檔案的使用者姓名:"); String receiver = sc.nextLine(); System.out.println("請選擇您要傳送的檔案:(輸入具體路徑)"); String filePath = sc.nextLine(); //構建檔案 File file = new File(filePath); String fileName = file.getName(); long fileLength = file.length(); String msg = clientName + ":" + receiver +":" + (fileName + "#" + fileLength) + ":" + Config.MSG_SEBDFILE+":"; //使用記憶體操作流輸入 byte[] msgBytes = msg.getBytes(); //空位元組陣列 byte[] emptyBytes = new byte[1024*10-msgBytes.length] ; //利用工具將檔案轉化為位元組陣列 byte[] fileBytes = InputAndOutputUtil.readFile(filePath); //System.out.println("經過工具後,檔案變化為位元組的大小為:"+fileBytes.length); ByteArrayOutputStream baos = new ByteArrayOutputStream(); baos.write(msgBytes); baos.write(emptyBytes); baos.write(fileBytes); byte[] allBytes = baos.toByteArray(); //System.out.println("客戶端發出的資料總共位元組大小:"+allBytes.length); //通過輸出流輸出到伺服器中 os.write(allBytes); os.flush(); } private static void exitChat() throws IOException { //退出聊天 System.out.println("謝謝使用,再見"); String msg = clientName+ ":" + "null" + ":" + "null" +":"+ Config.MSG_EXIT; os.write(msg.getBytes()); //關閉 } private static void onlineList() throws IOException { System.out.println("******當前線上的好友有******"); String msg = clientName + ":" + "null" + ":" + "null" + ":" +Config.MSG_ONLINELIST; os.write(msg.getBytes()); } @SuppressWarnings("static-access") private static void publicChat() throws IOException { System.out.println("----------------歡迎您公聊模式--------------"); System.out.println("請輸入您要發的訊息:"); String msg = sc.next(); //沒有傳送者用null來代替 //訊息格式: 傳送者:接受者:所發信息:訊息型別 msg = clientName + ":"+"null"+":" + msg + ":" + config.MSG_PUBLICCHAT; os.write(msg.getBytes()); } @SuppressWarnings("static-access") private static void privateChat() throws IOException { System.out.println("----------------歡迎您進入私聊模式--------------"); //訊息個數:接受者+傳送訊息+訊息型別 System.out.println("請輸入接受者的姓名:"); String receiver = sc.nextLine(); System.out.println("請輸入您要給他(她)話:"); String msg = sc.next(); //訊息格式: 傳送者:接受者:所發信息:訊息型別 msg = clientName + ":" + receiver +":"+ msg +":"+ config.MSG_PRIVATECHAT ; os.write(msg.getBytes()); } }
package org.westos.client; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import org.westos.client.Config.Config; import org.westos.util.InputAndOutputUtil; /** * 客戶端讀取輸入流的執行緒 * @author 虎 * */ public class ClientReadThread extends Thread{ private InputStream is; public ClientReadThread(InputStream is) { super(); this.is = is; } @Override public void run() { try { while(true) { byte[] bytes = new byte[1024*10]; int len = is.read(bytes); String infomation = new String(bytes, 0, len); String[] info = infomation.split(":"); String clientName = info[0]; String receiver = info[1]; String msg = info[2]; int infoType =Integer.parseInt(info[3]); //私聊模式 //System.out.println(infomation); if(infoType == (Config.MSG_PRIVATECHAT)) { System.out.println(clientName+"@你說:"+msg); } else if(infoType == (Config.MSG_PUBLICCHAT)) { System.out.println(clientName+"對大家說:"+msg); } else if (infoType == Config.MSG_ONLINE) { System.out.println(clientName+msg); } else if(infoType == (Config.MSG_ONLINELIST)) { System.out.println(msg); } else if(infoType == Config.MSG_EXIT) { System.out.println(msg); } else if(infoType == Config.MSG_SEBDFILE) { String[] file = msg.split("#"); String fileName = file[0]; long fileLength = Long.parseLong(file[1]); System.out.println(clientName+"給您發來一個檔案:"+ fileName + "檔案大小為:"+fileLength); byte[] memoryBytes = new byte[1024]; int memoryLength = 0; //int len1 = infomation.getBytes().length; //不斷的去讀取 ByteArrayOutputStream baos = new ByteArrayOutputStream(); int len1 = infomation.getBytes().length; //System.out.println("讀取的訊息的長度:"+len1); while (true) { int len2= is.read(memoryBytes); baos.write(memoryBytes, 0, len2); //實際長度 memoryLength += len2; //判斷截止標記 //System.out.println(memoryLength); //System.out.println(fileLength); 31879 if (fileLength == memoryLength) { break; } //System.out.println("ha2"); } //System.out.println("最終的長度:"+memoryLength); byte[] fileBytes = baos.toByteArray(); //呼叫工具 boolean flag = InputAndOutputUtil.writeFile("D:/"+fileName, fileBytes); //針對不同情況進行判斷 //System.out.println("ha3"); if (flag) { System.out.println("檔案接受成功!儲存在D:/"+fileName+"中,請前往檢視"); break; } else { System.out.println("檔案接受失敗!"); } } } } catch (IOException e) { e.printStackTrace(); } } }
package org.westos.server; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; import java.util.HashMap; /** * * @author 代虎 * */ public class Server { private static ServerSocket ss; private static Socket s; private static InputStream is; private static OutputStream os; static HashMap<String, Socket> map = new HashMap<String, Socket>() ; public static void main(String[] args) { int num = 1; try { ss = new ServerSocket(9999); System.out.println("伺服器已啟動..."); while (true) { s = ss.accept(); is = s.getInputStream(); os = s.getOutputStream(); System.out.println("第"+ (num++)+"個客戶端接入"); new SaveClientThread(s, map).start();; } } catch (Exception e) { e.printStackTrace(); } } }
package org.westos.server;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.HashMap;
import org.westos.server.config.Config;
/**
* 存取客戶的資訊
* @author 代虎
*
*/
public class SaveClientThread extends Thread{
private Socket s;
private InputStream is;
private OutputStream os;
private String clientName;
private Config config;
private HashMap<String, Socket> map = new HashMap<String,Socket>();
public SaveClientThread(Socket s, HashMap<String, Socket> map) {
this.s = s;
this.map = map;
}
@Override
public void run() {
while(true) {
byte[] name = new byte[1024];
try {
is = s.getInputStream();
os = s.getOutputStream();
int len = is.read(name);
clientName = new String(name, 0, len);
if(map.containsKey(clientName)) {
os.write("no".getBytes());
}
else {
map.put(clientName, s);
os.write("yes".getBytes());
System.out.println("當前線上好友:");
for(String client:map.keySet()) {
System.out.println(client);
}
break;
}
} catch (IOException e) {
e.printStackTrace();
}
}
//給客戶端反饋上線的訊息
for(String userName:map.keySet()) {
//本人上線不需要踢提醒自己
if(userName.equals(clientName)) {
continue;
}
else {
Socket socket = map.get(userName);
String msg = clientName+":"+ userName +":"+ "上線了" +":"+ Config.MSG_ONLINE;
try {
socket.getOutputStream().write(msg.getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
//System.out.println("傳送成功");
}
new ServerTransmitThread(s, map).start();;
}
}
package org.westos.server;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.HashMap;
import org.westos.server.config.Config;
public class ServerTransmitThread extends Thread{
private OutputStream os;
private InputStream is;
private Socket s;
private HashMap<String, Socket> map = new HashMap<String, Socket>() ;
public ServerTransmitThread(Socket s, HashMap<String, Socket> map) {
super();
this.s = s;
this.map = map;
}
@SuppressWarnings("unused")
@Override
public void run() {
try {
is = s.getInputStream();
while(true) {
//先定義50kb
byte[] bytes = new byte[1024*10];
int len= is.read(bytes);
String infomation = new String(bytes, 0, len);
String[] info = infomation.split(":");
String sendName = info[0];
String receiver = info[1];
String context = info[2];
int infoType =Integer.parseInt(info[3]);
//公聊
if(infoType == (Config.MSG_PRIVATECHAT)) {
Socket socket = map.get(receiver);
socket.getOutputStream().write(infomation.getBytes());
}
//私聊
else if(infoType == (Config.MSG_PUBLICCHAT)) {
for(String str:map.keySet()) {
Socket socket = map.get(str);
socket.getOutputStream().write(infomation.getBytes());
}
}
//線上列表
else if(infoType == Config.MSG_ONLINELIST) {
Socket socket = map.get(sendName);
int num = 1;
StringBuffer sb = new StringBuffer();
for(String str:map.keySet()) {
sb.append(num++).append("、").append(str).append("\n");
}
String msg = sendName +":"+ receiver+":" + (sb.toString())+":" + Config.MSG_ONLINELIST;
Socket socket2 = map.get(sendName);
socket2.getOutputStream().write(msg.getBytes());
}
//退出聊天室
else if (infoType == Config.MSG_EXIT) {
//在map中移除該使用者
map.remove(sendName);
String msg = sendName+"下線了";
for(String str:map.keySet()) {
msg = "null"+ ":"+ str + ":" + msg + ":" + infoType;
Socket socket = map.get(str);
socket.getOutputStream().write(msg.getBytes());
}
}
//傳送檔案
else if (infoType == Config.MSG_SEBDFILE) {
//System.out.println("傳送到伺服器端檔案總共位元組大小"+infomation.getBytes().length);
Socket socket = map.get(receiver);
String[] file = context.split("#");
String fileName = file[0];
long fileLength = Long.parseLong(file[1]);
String msg = sendName + ":" + receiver +":" + context+ ":" + Config.MSG_SEBDFILE+":";
byte[] msgBytes = msg.getBytes();
byte[] emptyBytes = new byte[1024*10-msgBytes.length] ;
//讀取檔案
byte[] memoryBytes = new byte[1024];
int memoryLength = 0;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
//System.out.println("讀取開始了嗎?");
//不斷的去讀取
while (true) {
//System.out.println("1讀取開始了");
int len2= is.read(memoryBytes);
//System.out.println("2到這了嗎");
baos.write(memoryBytes, 0, len2);
//baos.flush();
//實際長度
memoryLength += len2;
//System.out.println("3到這了嗎");
//判斷截止標記
//System.out.println("1:"+memoryLength);
//System.out.println("2:"+len2);
if (fileLength == memoryLength) {
break;
}
//System.out.println("3");
}
byte[] fileBytes = baos.toByteArray();
baos.reset();
//重置,繼續使用該輸出流物件寫資料到記憶體中
//System.out.println("4");
baos.write(msgBytes);
baos.write(emptyBytes);
baos.write(fileBytes);
//記憶體中已經有這些資料,需要拼接成一個大的字元陣列發過去
byte[] allBytes = baos.toByteArray();
//System.out.println("傳送的長度:"+allBytes.length);
socket.getOutputStream().write(allBytes);
}
}
} catch (IOException e) {
// e.printStackTrace();
}
}
}
工具包:
package org.westos.server.config;
/**
* 常量(相當於客戶端與伺服器之間的人協議一樣)
* @author 代虎
*
*/
public class Config {
public static final int MSG_PRIVATECHAT = 100;//私聊
public static final int MSG_PUBLICCHAT = 200;//公聊
public static final int MSG_ONLINE = 300;//上線
public static final int MSG_ONLINELIST = 400;//線上列表
public static final int MSG_EXIT = 500; //退出
public static final int MSG_SEBDFILE= 600;//傳送檔案
public static final int MSG_DOWNLOAD= 700;//下載檔案
}
package org.westos.util;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
public class InputAndOutputUtil {
public static byte[] readFile(String path) {
File file = new File(path);
// 陣列用來儲存讀取的資料 相當於水池
byte datas[] = null;
if (!file.exists()) {
datas = null;
} else {
try {
// 位元組陣列輸出流 用來往記憶體中寫位元組陣列 可以用來拼接位元組陣列
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// 建立檔案輸入流
FileInputStream fis = new FileInputStream(file);
// 用來儲存每次讀的資料 相當水瓢(每次讀1024位元組 但是不一定每次能讀這麼多 實際讀取的長度用len儲存)
byte data[] = new byte[1024 * 1024];
// 用來儲存每次讀取的位元組大小
int len = 0;
// 不斷的讀取 直到資料讀完
while ((len = fis.read(data)) > 0) {
// 把每次讀入的資料 存放在位元組陣列流的記憶體中
baos.write(data, 0, len);
}
// 把位元組陣列流中的資料轉為位元組陣列
datas = baos.toByteArray();
baos.flush();
baos.close();
// 關閉流
fis.close();
} catch (Exception e) {
e.printStackTrace();
}
}
return datas;
}
public static boolean writeFile(String path, byte datas[]) {
try {
File file = new File(path);
FileOutputStream fos = new FileOutputStream(file);
fos.write(datas);
// 傾倒關閉
fos.flush();
fos.close();
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
}
package org.westos.util;
import java.util.Scanner;
public class InputUtil {
//一直要我們錄入整數為止
public static int inputIntType(Scanner sc) {
int choose = 0;
while (true) {
try {
//錄入使用者輸入的整數
choose = sc.nextInt();
break;
} catch (Exception e) {
sc = new Scanner(System.in);
System.out.println("輸入的型別不正確,請重新輸入:");
}
}
return choose;
}
}