【Java】Socket实现的C/S模式半UI多人聊天程序
前言
這個程序也是以前寫的,大概是一個命令行的S端支持幾個半UI的C端進行網絡聊天的程序。
還有很多使用上的問題,萌新讀者若能把本例作為一個殼子改裝改裝,作出你的作品,就是我幸運之至。
S端程序
import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.EOFException; import java.io.IOException; import java.net.BindException; import java.net.ServerSocket; import java.net.Socket; //import java.net.SocketException; import java.util.ArrayList; import java.util.List;public class ChatSeverEdition {// 服務器端是否啟動private boolean isStarted = false;private ServerSocket serverSocket = null;private List<Client> clients = new ArrayList<>();public static void main(String[] args) {new ChatSeverEdition().start();}public void start() {try {serverSocket = new ServerSocket(8888);isStarted = true;} catch (BindException e) {// 多次啟動服務器System.out.println("端口使用中。。。。");System.out.println("請關掉相關程序,并重新運行服務器!");System.exit(0);} catch (IOException e) {e.printStackTrace();} try {while (isStarted) {// 客戶端是否連接Socket socket = serverSocket.accept();// main()方法是static方法,不能在其中new動態類ClientClient client = new Client(socket);// 線程啟動new Thread(client).start();// 將當前啟動的客戶端保存下來clients.add(client);System.out.println("new Client成功!");System.out.println("A Client connected!");}} catch (IOException e) {e.printStackTrace();} finally {try {serverSocket.close();} catch (IOException e) {e.printStackTrace();}}}class Client implements Runnable {private Socket socket;private DataInputStream dataInputStream = null;private boolean isConnected = false;private DataOutputStream dataOutputStream;public Client(Socket socket) {this.socket = socket;try {dataInputStream = new DataInputStream(socket.getInputStream());dataOutputStream = new DataOutputStream(socket.getOutputStream());isConnected = true;System.out.println("有一個新的客戶端程序在運行");} catch (IOException e) {e.printStackTrace();}// 一個客戶端連接成功以后,一直在執行該while部分的代碼,其他客戶端無法連接/*** 解決方法: * 1.異步解決方法:一直監聽接收,其他在另外的地方執行 * 2.使用單獨的線程: 線程:接受客戶端連接* 另外的單獨的線程:處理通訊*/}public void sendString(String str) {try {dataOutputStream.writeUTF(str);} catch (IOException e) {clients.remove(this);System.out.println("對方退出了!!!我從List中去掉了!!!");}}public void run() {Client client = null;// 注意這部分的包圍關系try {while (isConnected) {String string = dataInputStream.readUTF();System.out.println(string);for (int i = 0; i < clients.size(); i++) {client = clients.get(i);client.sendString(string);}}} catch (EOFException e) {System.out.println("Client closed!");} catch (IOException e) {e.printStackTrace();System.exit(0);} finally {try {if (dataInputStream != null) {dataInputStream.close();}if (socket != null) {socket.close();}} catch (IOException e) {e.printStackTrace();}}}}}C端程序
import java.awt.BorderLayout; import java.awt.Frame; import java.awt.TextArea; import java.awt.TextField; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.EOFException; import java.io.IOException; import java.net.BindException; import java.net.Socket; import java.net.SocketException; import java.net.UnknownHostException;public class ChatClientEdition extends Frame {private static final long serialVersionUID = 1L;private TextField textField = new TextField();private TextArea textArea = new TextArea();private Socket socket = null;private DataOutputStream dataOutputStream = null;private DataInputStream dataInputStream = null;private boolean isConnected = false;public static void main(String[] args) {new ChatClientEdition().launchFrame();}/*** 加載窗口*/public void launchFrame() {setLocation(400, 300);this.setSize(300, 300);this.setTitle("在線聊天時--Quiana");add(textField, BorderLayout.SOUTH);add(textArea, BorderLayout.NORTH);pack();// 窗口關閉的時間監聽this.addWindowListener(new WindowAdapter() {@Overridepublic void windowClosing(WindowEvent e) {disconnect();System.exit(0);}});textField.addActionListener(new TFListener());this.setVisible(true);connect();new Thread(new RecvThread()).start();}// 文字處理的時間監聽private class TFListener implements ActionListener {// 敲擊回車時,文字的變化事件@Overridepublic void actionPerformed(ActionEvent arg0) {String s = textField.getText().trim(); // 去掉兩端的空格textField.setText("");try {dataOutputStream.writeUTF(s);dataOutputStream.flush();} catch (IOException e) {e.printStackTrace();}}}/*** 連接到服務器*/public void connect() {try {// 連接到服務器socket = new Socket("127.0.0.1", 8888);isConnected = true;dataOutputStream = new DataOutputStream(socket.getOutputStream());dataInputStream = new DataInputStream(socket.getInputStream());System.out.println("連接到服務器!!!");} catch (BindException e) {e.printStackTrace();} catch (UnknownHostException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}/*** 斷開連接*/public void disconnect() {try {dataOutputStream.close();System.out.println("dataOutputStream成功關閉");socket.close();System.out.println("客戶端socket成功關閉");} catch (IOException e) {e.printStackTrace();}}private class RecvThread implements Runnable {public void run() {try {while (isConnected) {String string = dataInputStream.readUTF();if (textArea.getText().length() == 0) {textArea.setText(string);} else {textArea.setText(textArea.getText() + "\n" + string);}}} catch (SocketException e) {System.out.println("退出了,ByeBye!");} catch (EOFException e) {System.out.println("退出了,ByeBye!");} catch (IOException e) {e.printStackTrace();}}}}運行示例
先啟動S端:
此時不能啟動另外的S端(因為這個系統只支持一個服務器),啟動之后會被提示端口使用中然后被terminated掉:
我們這時使用S端發東西是沒用的,它只是服務器,更何況是沒聯客戶端的服務器。
然后啟動C端:
UI顯示C端程序界面,命令行提示S端連接了。
我們可以開啟兩個,進行talk:
這里極大的一個不足是不能提示誰發的消息,只是一個記錄的傳輸(S端也會有記錄,這個并未加密)。
下圖是在某一個C端發消息:
兩個C端和S端都會有記錄:
停掉一個C端程序:UI只剩一個(未terminated的),已經停掉的C端提示結束:
S端會顯示又一個C端用戶退出了,這時如果用戶繼續發消息,會發現沒人陪聊,此時會傳給S端消息表示從自己聊天列表中去掉了(只剩一個C端用戶了,所以就沒得玩),剩下的一個可以繼續發給S端,只是沒啥用(因為沒人收啊),這畢竟是一個實時聊天系統……:
退出List以后,繼續發完全沒問題,頂多只是尬聊客服機器(更何況此處客服不會聊天):
別尬聊了,就當這孩子生氣了吧,所以他也不陪聊了,撤——此時C端一個也沒開啟,但S端還得跑(真勤奮):
S端還是需要繼續發光放熱、榨干自己的,所以還要開一個C端:
這次我們不停C端,我們停掉S端,嘿嘿,C端必是懵了:
此時最尷尬,它不論干啥都沒用,只能心甘情愿的退出~~
程序就徹底Over了……
結語
分析了很多種運行情況,但畢竟很久沒玩了,感興趣的Reader可以自己改造改造,讓它發光放熱!
總結
以上是生活随笔為你收集整理的【Java】Socket实现的C/S模式半UI多人聊天程序的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【算法分析与设计】排序算法的时间复杂度与
- 下一篇: 【Vue 快速入门】从零开始搭建 VUE