java 局域网聊天
生活随笔
收集整理的這篇文章主要介紹了
java 局域网聊天
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
局域網聊天軟件
需要的前置了解:
- 了解java窗體
- 了解Socket通信
- 了解線程使用
- 了解TCP協議
只要你能了解以上內容,通過自身的應用,即可實現一個簡單的局域網聊天軟件。
聊天軟件實現難點
本項目中
- 聊天信息
- 新加入的用戶姓名(未完善)
- 退出的用戶姓名
- 聊天信息
- 有用戶加入
- 服務器斷開
- 由用戶退出
在本項目中會對發送的數據進行處理,在每個數據前加上一個標識,以便判斷是何種情況的數據。
服務器
一開始選擇服務器作為第一個實現的目標,實現步驟如下:
大體思路就是這個,詳細實現(為了方便理解,按照調用方法的順序來展現代碼):
public class ChatServerFrame extends JFrame implements ActionListener{//此處設計其實不太好,沒有實現封裝,日后需注意static String mString;static JTextArea ChatWindow;JTextArea InputMessage;static JTextArea UserName;//存儲用戶名稱ArrayList<String> Userlist = new ArrayList<String>();//存儲所有的客戶端連接,以便之后進行使用ArrayList<Socket> socketList = new ArrayList<Socket>();JTextField getPort;String Ip;String stringPort;int port;ServerSocket serverSocket = null;static boolean judge;public static void main(String[] args) {ChatServerFrame chatFrame = new ChatServerFrame();chatFrame.setVisible(true);}//窗口實現(不是很重要)public ChatServerFrame() {setTitle("服務器聊天窗口");setSize(700, 550);setLocationRelativeTo(null);setLayout(new FlowLayout());try {Ip = Inet4Address.getLocalHost().getHostAddress();} catch (UnknownHostException e) {// TODO Auto-generated catch blocke.printStackTrace();}JPanel jPanel = new JPanel(new BorderLayout());//消息顯示區(莫名其妙就成繁體字了....)JPanel jp1 = new JPanel(new BorderLayout());ChatWindow = new JTextArea(17, 45);ChatWindow.setEditable(false);ChatWindow.setLineWrap(true);JScrollPane scrollPane = new JScrollPane(ChatWindow);jp1.setBorder(new TitledBorder("消息顯示"));jp1.add(scrollPane,"Center");//發出信息框JPanel jp2Top = new JPanel(new BorderLayout());JPanel jp2_1 = new JPanel(new BorderLayout());JPanel jp2_2 = new JPanel(new BorderLayout());JToolBar toolBar = new JToolBar();JLabel linkServer = new JLabel("服務器Ip");JTextField getServer = new JTextField(12);JLabel linkPort = new JLabel("端口");getPort = new JTextField(12);JButton createServer = new JButton("創建服務器");getServer.setText(Ip);getServer.setEditable(false);InputMessage = new JTextArea(5,45);InputMessage.setLineWrap(true);createServer.addActionListener(this);JScrollPane scrollPane2 = new JScrollPane(InputMessage);toolBar.add(linkServer);toolBar.add(getServer);toolBar.add(linkPort);toolBar.add(getPort);toolBar.add(createServer);jp2_1.setBorder(new TitledBorder("信息輸入"));jp2_2.add(toolBar,"North");jp2_1.add(scrollPane2,"Center");jp2Top.add(jp2_2,"North");jp2Top.add(jp2_1,"Center");//按鈕區JPanel jp3 = new JPanel();JButton send = new JButton("發送");send.addActionListener(this);jp3.add(send);//用戶區JPanel jp4 = new JPanel(new BorderLayout());jp4.setBorder(new TitledBorder("用戶區"));UserName = new JTextArea(29,8);UserName.setEditable(false);JScrollPane scrollPane3 = new JScrollPane(UserName);jp4.add(scrollPane3);jPanel.add(jp1,"North");jPanel.add(jp2Top,"Center");jPanel.add(jp3,"South");this.add(jp4);this.add(jPanel);//添加窗口監聽器,主要設置關閉窗口時,斷開各個客戶端的連接WindowAction windowAction = new WindowAction();this.addWindowListener(windowAction);//將服務器姓名傳上String name = "服務器";Userlist.add(name);UserName.append(name + "\n"); }//此處是想著為之后的登陸留接口,并無用處public ChatServerFrame(String mString) {this.mString = mString;}... ...//添加窗口監聽器class WindowAction extends WindowAdapter {//在窗口關閉時,斷開和所有客戶端的連接@Overridepublic void windowClosing(WindowEvent e) {//如果已經創建服務端并服務端未關閉if (serverSocket != null && (!serverSocket.isClosed())) { //TODO 向所有用戶發送用戶退出的信息,此處可更優化for (String string : Userlist) {try {sendAllClient(string, 4);} catch (IOException e1) {e1.printStackTrace();}}try {//發送服務端退出信息sendAllClient(null, 3);} catch (IOException e2) {e2.printStackTrace();}//關閉和客戶端的連接for (Socket socket : socketList) {try {socket.close();} catch (IOException e1) {e1.printStackTrace();}}Userlist.clear();try {serverSocket.close();} catch (IOException e1) {e1.printStackTrace();}System.exit(0);}super.windowClosing(e);}}//添加按鈕監聽器@Overridepublic void actionPerformed(ActionEvent e) {String cmd = e.getActionCommand();if (cmd.equals("創建服務器")) {if (serverSocket != null) {JOptionPane.showMessageDialog(this, "已經創建好服務器!");} else {stringPort = getPort.getText().toString();if (stringPort.isEmpty()) {JOptionPane.showMessageDialog(this, "端口不能為零");} else {port = Integer.parseInt(getPort.getText().toString());createServer();}}} else if (cmd.equals("發送")) {if (InputMessage.getText().toString().isEmpty()) {JOptionPane.showMessageDialog(this, "不能發送空信息!");} else if (socketList.isEmpty()) {JOptionPane.showMessageDialog(null, "沒有用戶連接,不能發信息");} else {String message = "服務端: " + InputMessage.getText().toString();ChatWindow.append(message + "\n");try {//此處的1在接受客戶端數據處有注釋解釋(class ReceiveClient)sendAllClient(message, 1);} catch (IOException e1) {JOptionPane.showMessageDialog(this, "沒有客戶端連接");}}InputMessage.setText("");}}//承接上文的創建服務器public void createServer() {try {serverSocket = new ServerSocket(port);JOptionPane.showMessageDialog(this, "服務器創建成功!");//創建一個單獨的線程,用來接收客戶端的連接new LinkClient(serverSocket).start();} catch (IOException e) {JOptionPane.showMessageDialog(this, "服務器創建失敗!請修改端口或重啟");e.printStackTrace();}}//將服務器的信息發送給所有客戶端,此處的flag是為了判斷發送數據的類型public void sendAllClient(String message,int flag) throws IOException {String info = flag + message;PrintWriter printWriter = null;for (Socket socket : socketList) {printWriter = new PrintWriter(socket.getOutputStream(), true);printWriter.println(info);}}... //對客戶端的邏輯,接受客戶端連接class LinkClient extends Thread{ServerSocket serverSocket;public LinkClient(ServerSocket serverSocket) {this.serverSocket = serverSocket;}@Overridepublic void run() {//TODO 此處應該有比while(true)更好的方法,但我暫時不會while(true) {try {//此處會阻塞該進程,直到獲取到客戶端連接(查看源碼之后,仍不清楚accept是如何阻塞的,據網上說法,是有一個while(Listen())一直在運行)Socket socket = serverSocket.accept();socketList.add(socket);//將新加入用戶的名稱發給此時的其他用戶for (String string : Userlist) {sendAllClient(string, 2);}//新建一個線程,接收客戶端的信息//此處的邏輯是每接收一個客戶端就創建一個線程new ReceiveClient(socket).start();//TODO 連接數據庫,將socket的IP地址傳入,與數據庫中的名稱進行比較 OR 在剛開始連接時,首先將姓名傳出來} catch (IOException e) {JOptionPane.showMessageDialog(null, "服務器已退出!");e.printStackTrace();}}}}//接受客戶端的數據class ReceiveClient extends Thread {Socket socket;public ReceiveClient(Socket socket) {this.socket = socket;}@Overridepublic void run() {try {//等待接受該socket發送的數據BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));//TODO 循環接受之后的數據while(true) {try {int flag = bufferedReader.read() - '0';String message = bufferedReader.readLine();/** 當從客戶端接收信息時,讀取第一個字符,若為* 1,則之后的字符串為客戶發送的信息 接收信息* 2,則之后的字符串為新加入的客戶的姓名 添加新用戶* 3,則之后的字符串為退出的客戶的姓名 刪除用戶*/switch (flag) {case 1:ChatWindow.append(message + "\n");sendAllClient(message, 1);break;case 2:if (!Userlist.contains(message))Userlist.add(message);UserName.setText("");for(String string : Userlist) {UserName.append(string + "\n");sendAllClient(string, 2);try {sleep(1000);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}break;case 3:Userlist.remove(message);UserName.setText("");for(String string : Userlist) {System.out.println(string);UserName.append(string + "\n");}socketList.remove(socket);sendAllClient(message, 4);break;default:break;}} catch (IOException e) {e.printStackTrace();break;}}} catch (IOException e1) {// TODO Auto-generated catch blocke1.printStackTrace();}}}客戶端
做完服務器之后,繼續完成客戶端內容(此處建議:在完成服務器的ServerSocket()內容后,寫一個簡單的客戶端,用于測試相關功能);實現步驟如下:
額外內容:實現了查詢局域網內服務器ip
額外內容
獲取開啟對應端口的服務器
總結
以上是生活随笔為你收集整理的java 局域网聊天的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Flash:帧属性
- 下一篇: 万能开头结尾(申论)