java線上聊天項目0.7版 串連多個用戶端問題,開啟多個用戶端後伺服器端只接收到一個 對各種異常的補充處理,線上聊天0.7
問題的原因是
while(connected) {
String str=dis.readUTF();
System.out.println(str);
}
不斷迴圈執行,一直在死迴圈擷取socket發送的資訊,
使得前面的語句
s = ss.accept();
connected=true;
服務端接收新的用戶端不能再被執行
解決的方法——通過多線程技術
當獲得一個socket後,則開啟一個多線程,讓
while(connected) {
String str=dis.readUTF();
System.out.println(str);
}
迴圈讀取操作在多線程中完成
這樣每個socket獲得之後都在各自的現成中完成自己的迴圈讀取
具體方法:
首先在主函數main中建立一個關於線程的內部類,因為這個類外部其他類不需要,所以弄成內部類
解決問題後:
伺服器端代碼有較大改動,當中遇到各種異常逐一解決,邏輯思路上不複雜,細節上注意解決了
先開啟用戶端的各種異常
用戶端關閉後的異常提示
服務端增加了ServerSocket的關閉
服務端增加線程處理後無效果的調試——根據socket closed異常查明是
在內部類Client中while迴圈寫在了try的外邊,導致socket已經被關閉,但迴圈依然在盡心,不斷提示出錯
0.7版的最終伺服器端代碼如下:
package com.swift;import java.io.DataInputStream;import java.io.EOFException;import java.io.IOException;import java.net.BindException;import java.net.ServerSocket;import java.net.Socket;public class ChatServer { public static void main(String[] args) { new ChatServer().fun(); } private void fun() { boolean started = false; ServerSocket ss = null; Socket s = null; try { ss = new ServerSocket(8888); started = true; } catch (BindException e) { System.out.println("連接埠使用中......"); } catch (IOException e1) { e1.printStackTrace(); } try { while (started) { s = ss.accept(); System.out.println("a client connected success"); Client c = new Client(s); new Thread(c).start(); } } catch (EOFException e) { System.out.println("client has closed."); } catch (Exception e) { e.printStackTrace(); } finally { try { ss.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } class Client implements Runnable { private Socket s; private DataInputStream dis; private boolean connected = false; public Client(Socket s) { this.s = s; try { this.dis = new DataInputStream(s.getInputStream()); connected = true; } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } @Override public void run() { try {//注意:要包括while迴圈,如果try在while迴圈裡,則出現socket closed異常 while (connected) { String str = dis.readUTF(); System.out.println(str); } } catch (IOException e) { e.printStackTrace(); } finally { if (dis != null) { try { dis.close(); } catch (IOException e) { e.printStackTrace(); } } if (s != null) { try { s.close(); } catch (IOException e) { e.printStackTrace(); } } } } }}
0.7版本的用戶端代碼改動不大,代碼如下:
package com.swift;import java.awt.BorderLayout;import java.awt.Color;import java.awt.event.ActionEvent;import java.awt.event.ActionListener;import java.awt.event.WindowAdapter;import java.awt.event.WindowEvent;import java.io.DataOutputStream;import java.io.IOException;import java.net.ConnectException;import java.net.Socket;import java.net.UnknownHostException;import javax.swing.JButton;import javax.swing.JFrame;import javax.swing.JLabel;import javax.swing.JPanel;import javax.swing.JScrollPane;import javax.swing.JTextArea;import javax.swing.JTextField;public class ChatClientFrame extends JFrame { private static final long serialVersionUID = -118470059355655240L; Socket s = null; DataOutputStream dos = null; JLabel label_shang = new JLabel(); JLabel label_xia = new JLabel(); JTextField tf = new JTextField(38); JTextArea ta = new JTextArea(15, 50); JButton button = new JButton(); public ChatClientFrame() { setBounds(200, 200, 500, 400); setTitle("用戶端聊天工具 —— 0.7"); // 對視窗進行大的布局,分為三行一列,在pBasic面板上添加三個面板shang zhong xia JPanel pBasic = new JPanel(); pBasic.setLayout(new BorderLayout());// 不設定預設也是這種配置模式 setContentPane(pBasic);// 把面板放在視窗上,不記得用this.關鍵字 JPanel shang = new JPanel(); JPanel zhong = new JPanel(); JPanel xia = new JPanel(); // 設定JPanel面板的大小 shang.setSize(470, 25); zhong.setSize(470, 180); xia.setSize(470, 40); pBasic.add(shang, BorderLayout.NORTH); pBasic.add(zhong, BorderLayout.CENTER); pBasic.add(xia, BorderLayout.SOUTH); shang.setBackground(Color.red); zhong.setBackground(Color.yellow); xia.setBackground(Color.blue); label_shang.setText("聊天記錄"); shang.add(label_shang); ta.setLineWrap(true);// 自動換行 JScrollPane scroll = new JScrollPane(ta);// 增加捲軸,以便不增加行數 zhong.add(scroll); label_xia.setText("輸入資訊"); xia.add(label_xia, BorderLayout.WEST); /* * 增加功能,視窗監聽事件,視窗開啟時設定游標焦點在tf文本域中 */ this.addWindowListener(new WindowAdapter() { @Override public void windowOpened(WindowEvent e) { tf.requestFocus(); } }); xia.add(tf, BorderLayout.CENTER); button.setText("發送"); xia.add(button, BorderLayout.EAST); final class ShareListener implements ActionListener { @Override public void actionPerformed(ActionEvent e) { String taText = ta.getText(); String tfText = tf.getText() + "\r\n"; String tfText1 = tf.getText(); ta.setText(taText + tfText); tf.setText(""); // 當斷行符號或發送按鈕時,tfText發送到伺服器 try { dos.writeUTF(tfText1); dos.flush(); } catch (IOException e1) { e1.printStackTrace(); } } } button.addActionListener(new ShareListener()); tf.addActionListener(new ShareListener()); // 通過壓縮自動調整各個面板 pack(); this.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { disconnect(); System.exit(0); } }); setVisible(true); // 建立表單直接調用串連伺服器 connect(); } public void connect() { try { s = new Socket("127.0.0.1", 8888); System.out.println("connected!"); dos = new DataOutputStream(s.getOutputStream()); } catch (ConnectException e) { System.out.println("服務端異常........."); System.out.println("請確認服務端是否開啟........."); } catch (IOException e) { e.printStackTrace(); } } public void disconnect() { try { if (dos != null) dos.close(); if (s != null) s.close(); } catch (IOException e) { e.printStackTrace(); } } public static void main(String[] args) { new ChatClientFrame(); }}