Java學習筆記之--------網路程式設計之Socket通訊----聊天室實現
Socket通訊
網路上的兩個程式通過一個雙向的通訊連線實現資料的交換,這個連線的一端稱為一個socket。基於TCP/IP協議,建立穩定的點對點的通訊。
特點:實時、快速、安全性高、佔用系統資源多、效率低。
通常也稱作"套接字",套接字是一種程序間的資料交換機制。這些程序既可以在同一機器上,也可以在通過網路連線的不同機器上。換句話說,套接字起到通訊端點的作用。單個套接字是一個端點,而一對套接字則構成一個雙向通訊通道,使非關聯程序可以在本地或通過網路進行資料交換。一旦建立套接字連線,資料即可在相同或不同的系統中雙向或單向傳送,直到其中一個端點關閉連線。
“請求----響應”模式
客戶端:在網路通訊中,第一次主動發起通訊的程式被稱作客戶端(client)程式。
伺服器:第一次通訊中等待連線的程式被稱作伺服器端(server)程式。
這裡模擬了一個聊天室的簡單的聊天功能:
首先我們先準備一個關閉流的工具類CloseUtil,以後流的關閉都可以通過呼叫這個方法來實現:
public class CloseUtil { public static void closeAll(Closeable... io){ for (Closeable temp : io) { try { if (null != temp){ temp.close(); } } catch (IOException e) { e.printStackTrace(); } } } }
然後我們實現資料的接收和傳送:
public class Send implements Runnable{ //控制檯輸入流 private BufferedReader console; //管道輸出流 private DataOutputStream dos; //控制執行緒標識 private boolean isRunning = true; //名稱 private String name; public Send() { console = new BufferedReader(new InputStreamReader(System.in)); } public Send(Socket client, String name){ this(); try { dos = new DataOutputStream(client.getOutputStream()); this.name = name; send(this.name); } catch (IOException e) { isRunning = false; CloseUtil.closeAll(dos,console); } } /** * 1.從控制檯接收資料 * @return */ private String getMsgFromConsole(){ try { return console.readLine(); } catch (IOException e) { e.printStackTrace(); } return ""; } /** * 2.傳送資料 */ public void send(String msg){ try { if (null != msg && !msg.equals("")){ dos.writeUTF(msg); dos.flush();//強制重新整理 } } catch (IOException e) { isRunning = false; CloseUtil.closeAll(dos,console); } } @Override public void run() { while (isRunning){ send(getMsgFromConsole()); } } }
public class Receive implements Runnable{
//輸入流
private DataInputStream dis;
//執行緒標識
private boolean isRunning = true;
public Receive() {
}
public Receive(Socket client) {
try {
dis = new DataInputStream(client.getInputStream());
} catch (IOException e) {
isRunning = false;
CloseUtil.closeAll(dis);
}
}
/**
* 接收資料
* @return
*/
public String receive(){
String msg = "";
try {
msg = dis.readUTF();
} catch (IOException e) {
isRunning = false;
CloseUtil.closeAll(dis);
}
return msg;
}
@Override
public void run() {
//執行緒體
while (isRunning){
System.out.println(receive());
}
}
}
最後是我們模擬客戶端和伺服器的實現:
public class Server {
private List<MyChannel> all = new ArrayList<MyChannel>();
public static void main(String[] args) throws IOException {
new Server().start();
}
public void start() throws IOException {
ServerSocket server = new ServerSocket(9999);
while (true){
Socket client = server.accept();
MyChannel channel = new MyChannel(client);
all.add(channel);//統一管理
new Thread(channel).start();//一條道路
}
}
/**
* 一個客戶端一條道路
* 1.輸入流
* 2.輸出流
* 3.接收資料
* 4.傳送資料
*/
class MyChannel implements Runnable{
private DataInputStream dis;
private DataOutputStream dos;
private String name;
//執行緒標識
private boolean isRunning = true;
public MyChannel(Socket client) {
try {
dis = new DataInputStream(client.getInputStream());
dos = new DataOutputStream(client.getOutputStream());
this.name = dis.readUTF();
this.send("歡迎進入聊天室");
this.sendOthers(this.name + "進入了聊天室", true);
} catch (IOException e) {
CloseUtil.closeAll(dis,dos);
isRunning = false;
}
}
/**
* 讀取資料
* @return
*/
private String receive(){
String msg = "";
try {
msg = dis.readUTF();
} catch (IOException e) {
CloseUtil.closeAll(dis);
isRunning = false;
all.remove(this);//移除自身
}
return msg;
}
/**
* 傳送資料
* @return
*/
private void send(String msg){
if (null==msg || msg.equals("")){
return;
}
try {
dos.writeUTF(msg);
dos.flush();
} catch (IOException e) {
CloseUtil.closeAll(dos);
isRunning = false;
all.remove(this);//移除自身
}
}
/**
* 傳送給其他的客戶端
*/
private void sendOthers(String msg, boolean sys){
//是否為私聊
if (msg.startsWith("@") && msg.indexOf(":")>-1){ //約定私聊以@開始
//獲取name
String name = msg.substring(1, msg.indexOf(":"));
String content = msg.substring(msg.indexOf(":") + 1);
for (MyChannel other : all) {
if (other.name.equals(name)){
other.send(this.name + "對您悄悄的說:" + content);
}
}
} else {
//遍歷容器
for (MyChannel other : all) {
if (other == this){
continue;
}
if (sys){
//系統訊息
other.send("系統資訊:" + msg);
} else {
//傳送給其他客戶端
other.send(this.name + "對所有人說:" + msg);
}
}
}
}
@Override
public void run() {
while (isRunning){
sendOthers(receive(), false);
}
}
}
}
public class Client {
public static void main(String[] args) throws IOException {
System.out.println("請輸入名稱:");
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String name = br.readLine();
if (name.equals("")){
return;
}
Socket client = new Socket("localhost",9999);
new Thread(new Send(client, name)).start();//一條路徑
new Thread(new Receive(client)).start();//一條路徑
}
}
下面是我們的執行結果,先啟動Server。然後啟動Client,為第一個程序命名為a;然後再啟動一個程序,命名為b;最後再啟動一個程序並命名為c。在c程序中輸入“大家好”,則a程序和b程序都可以收到這條訊息,在c程序中輸入“@a:小a同學,你好。”,則我們只能在a程序中收到這條訊息。並且在b和c啟動的時候,會有系統訊息提示,b或者c進入聊天室。截圖如下,依次為abc三個程序的截圖:
以上就是模擬簡單的群聊功能的實現。還可以在結束程序的時候新增系統訊息提示某程序離開聊天室,只要對以上程式碼稍作修改即可。這裡沒有實現。
以上為尚學堂Java300集教學視訊中裴新老師所教授的網路程式設計相關課程的筆記。