1. 程式人生 > >線上聊天(JavaSE實戰專案)學習筆記

線上聊天(JavaSE實戰專案)學習筆記

Chat0.1

搭建客戶端ChatClient視窗,有兩種方式:

一、繼承Frame類(比較靈活)

呼叫Frame類中的setLocation(int x, int y)方法設定視窗位置,setSize(int width, int height)方法設定視窗大小,setVisible(true)方法將視窗顯示出來

setSize(int width, int height):其實就是定義控制元件的大小,有兩個引數,分別對應寬度和高度;

setLocation(int x, int y):將元件移到新位置,用x 和 y 引數來指定新位置的左上角

setBounds(int x, int y, int width, int height):四個引數,既定義元件的位置,也定義控制元件的大小; 其實它就是上面兩個函式的功能的組合

import java.awt.Frame;

public class ChatClient extends Frame{

	public static void main(String[] args) {

		new ChatClient().launchFrame();
	}
	
	public void launchFrame()
	{
		setLocation(400, 300);
		setSize(300, 300);
		setVisible(true);
	}
}

二、直接使用Frame類

Chat0.2

向客戶端視窗中新增文字輸入框TextField和文字輸入區TextArea(可以輸入多行文字

),使用add方法

import java.awt.*;

public class ChatClient extends Frame{

	TextField tfTxt = new TextField();
	TextArea taContent = new TextArea();
	
	public static void main(String[] args) {
		new ChatClient().launchFrame();
	}
	
	public void launchFrame(){
		setLocation(400, 300);
		setSize(300, 300);
		add(tfTxt, BorderLayout.SOUTH);
		add(taContent, BorderLayout.NORTH);
		pack();//void pack() 使此視窗的大小適合其子元件的首選大小和佈局。 
		setVisible(true);
	}

}

Chat0.3

新增客戶端視窗關閉功能,使用視窗的 addWindowListener 方法

WindowListener是java中的介面。主要作用:用於接收視窗事件的偵聽器介面。旨在處理視窗事件的類要麼實現此介面(及其包含的所有方法),要麼擴充套件抽象類WindowAdapter(僅重寫所需的方法)。然後使用視窗的 addWindowListener 方法將從該類所建立的偵聽器物件向該 Window 註冊。當通過開啟、關閉、啟用或停用、圖示化或取消圖示化而改變了視窗狀態時,將呼叫該偵聽器物件中的相關方法,並將 WindowEvent 傳遞給該方法。
import java.awt.*;
import java.awt.event.*;

public class ChatClient extends Frame{
	
	TextField tfTxt = new TextField();
	TextArea taContent = new TextArea();
	
	public static void main(String[] args){
		new ChatClient().launchFrame();
	}

	public void launchFrame() {
		setLocation(400, 300);
		setSize(300, 300);
		add(tfTxt, BorderLayout.SOUTH);
		add(taContent, BorderLayout.NORTH);
		pack();
		addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent e) {
				System.exit(0);
			}
		});
		setVisible(true);
	}
}

Chat0.4

向客戶端中新增功能:在文字輸入框TextFiled中輸入內容按回車鍵後,內容會顯示到文字輸入區TextArea中

取文字內容getText()方法,設定文字內容setText()方法

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;

public class ChatClient extends Frame{
	
	TextField tfTxt = new TextField();
	TextArea taContent = new TextArea();
	
	public static void main(String[] args){
		new ChatClient().launchFrame();
	}

	public void launchFrame() {
		setLocation(400, 300);
		setSize(300, 300);
		add(tfTxt, BorderLayout.SOUTH);
		add(taContent, BorderLayout.NORTH);
		pack();
		addWindowListener(new WindowAdapter() {

			@Override
			public void windowClosing(WindowEvent e) {
				System.exit(0);
			}
			
		});
		tfTxt.addActionListener(new TFListener());
		setVisible(true);
	}
	
	private class TFListener implements ActionListener{

		@Override
		public void actionPerformed(ActionEvent e) {
			String s = tfTxt.getText().trim();
			taContent.setText(s);
			tfTxt.setText("");
		}
		
	}
}

Chat0.5

建立ChatSever服務端

1、使用ServerSocket建立服務端(ServerSocket物件用於監聽來自客戶端的Socket連線)

        ServerSocket(int port):用指定的埠port來建立一個ServerSocket。該埠應該有一個有效的埠整數值,即0~65535。

2、接收來自客戶端Socket的連線請求,使用accept()方法

        ServerSocket包含一個監聽來自客戶端連線請求的方法,即accept()方法

        Socket accept():如果接收到一個客戶端Socket的連線請求,該方法將返回一個與客戶端Socket對應的Socket;否則該方法將一直處於等待狀態,執行緒也被阻塞。

注意:服務端只有一個,客戶端有多個,需使用while迴圈來接收多個客戶端

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class ChatServer {
	public static void main(String[] args) {
		try{
			ServerSocket ss = new ServerSocket(8888);
			while(true) {//用於接收多個客戶端
				Socket s = ss.accept();
                                System.out.println("a client connected");//用於驗證客戶端是否已連線成功			
			}
		}
		catch(IOException e) {
			e.printStackTrace();
		}	
	}
}

Chat0.6

ChatClient

1、在顯示客戶端視窗的同時,將客戶端與服務端進行連線,將連線步驟封裝成connect()方法

2、connect()方法

(1)建立客戶端物件:客戶端通常使用Socket的構造器來連線到指定的伺服器

Socket(InetAddress, int port):建立連線到指定遠端主機、遠端埠的Socket,該構造器沒有指定本地地址、本地埠,預設使用本地主機的預設IP地址,預設使用系統動態分配的埠。

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.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;

public class ChatClient extends Frame{
	Socket s = null;
	TextField tfTxt = new TextField();
	TextArea taContent = new TextArea();
	
	public static void main(String[] args) {
		new ChatClient().launchFrame();
	}
	
	public void launchFrame() {
		setLocation(400, 300);
		setSize(300, 300);
		add(tfTxt, BorderLayout.SOUTH);
		add(taContent, BorderLayout.NORTH);
		pack();
		addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent e) {
				System.exit(0);
			}
		});
		tfTxt.addActionListener(new TFListener());
		setVisible(true);
		connect();
	}
	
	public void connect() {
		try {
			s = new Socket("127.0.0.1", 8888);
System.out.println("connected");
		}catch(UnknownHostException e) {
			e.printStackTrace();
		}catch(IOException e) {
			e.printStackTrace();
		}
	}
	
	public class TFListener implements ActionListener{
		public void actionPerformed(ActionEvent e) {
			String str = tfTxt.getText().trim();
			taContent.setText(str);
			
		}
	}

}

ChatServer

接收客戶端傳送過來的資料

當客戶端、服務端產生了對應的Socket之後,,程式無須再區分伺服器端、客戶端,而是通過各自的Socket通訊。Socket提供瞭如下兩個方法來獲取輸入流和輸出流。

(1)InputStream getInputStream():返回該Socket物件對應的輸入流,讓程式通過該輸入流從Socket中取出資料。

(2)OutputStream getOutputStream():返回該Socket物件對應的輸出流,讓程式通過該輸出流向Socket中輸出資料。

import java.io.DataInputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class ChatServer {

	public static void main(String[] args) {
		try {
			ServerSocket ss = new ServerSocket(8888);
			while(true) {
				Socket s = ss.accept();
System.out.println("a client connected");
				DataInputStream dis = new DataInputStream(s.getInputStream());
				String str = dis.readUTF();
				System.out.println(str);
				dis.close();
			}
		}catch(IOException e) {
			e.printStackTrace();
		}
	}

}

Chat0.7

ChatClient

將從文字輸入框TextField中獲取到資料傳送到服務端(客戶端---->服務端)

OutputStream getOutputStream():返回該Socket物件對應的輸出流,讓程式通過該輸出流向Socket中輸出資料。

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.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;


public class ChatClient extends Frame{
	Socket s = null;
	TextField tfTxt = new TextField();
	TextArea taContent = new TextArea();
	
	public static void main(String[] args) {
		new ChatClient().launchFrame();
	}

	public void launchFrame() {
		setLocation(400, 300);
		setSize(300, 300);
		add(tfTxt, BorderLayout.SOUTH);
		add(taContent, BorderLayout.NORTH);
		pack();
		addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent e) {
				System.exit(0);
			}
		});
		tfTxt.addActionListener(new TFListener());
		setVisible(true);
		connect();
	}
	
	public void connect() {
		try {
			s = new Socket("127.0.0.1", 8888);
System.out.println("connected");
		}catch(UnknownHostException e) {
			e.printStackTrace();
		}catch(IOException e) {
			e.printStackTrace();
		}
	}
	
	public class TFListener implements ActionListener{
		public void actionPerformed(ActionEvent e) {
			String str = tfTxt.getText().trim();
			taContent.setText(str);
			tfTxt.setText("");
			
			try {
				DataOutputStream dos = new DataOutputStream(s.getOutputStream());
				dos.writeUTF(str);
				dos.flush();
				dos.close();
			} catch (IOException e1) {
				e1.printStackTrace();
			}
		}
	}
}

Chat0.8

ChatClient

在關閉視窗的同時關閉輸出流,關閉Socket

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.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;


public class ChatClient extends Frame{
	Socket s = null;
	DataOutputStream dos = null;
	TextField tfTxt = new TextField();
	TextArea taContent = new TextArea();
	
	public static void main(String[] args) {
		new ChatClient().launchFrame();
	}

	public void launchFrame() {
		setLocation(400, 300);
		setSize(300, 300);
		add(tfTxt, BorderLayout.SOUTH);
		add(taContent, BorderLayout.NORTH);
		pack();
		addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent e) {
				disconnect();
				System.exit(0);
			}
		});
		tfTxt.addActionListener(new TFListener());
		setVisible(true);
		connect();
	}
	
	public void connect() {
		try {
			s = new Socket("127.0.0.1", 8888);
			dos = new DataOutputStream(s.getOutputStream());
System.out.println("connected");
		}catch(UnknownHostException e) {
			e.printStackTrace();
		}catch(IOException e) {
			e.printStackTrace();
		}
	}
	
	public void disconnect() {
		try{
			dos.close();
			s.close();
		}catch(IOException e) {
			e.printStackTrace();
		}
		
	}
	public class TFListener implements ActionListener{
		public void actionPerformed(ActionEvent e) {
			String str = tfTxt.getText().trim();
			taContent.setText(str);
			tfTxt.setText("");
			
			try {
				dos.writeUTF(str);
				dos.flush();
				//dos.close();
			} catch (IOException e1) {
				e1.printStackTrace();
			}
		}
	}
}

ChatServer

在之前的版本中服務端不能連續讀取客戶端傳送過來的資料。

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.io.DataInputStream;

public class ChatServer {

	public static void main(String[] args) {
		boolean started = false;
		try{
			ServerSocket ss = new ServerSocket(8888);
			started = true;
			while(started) {//該while迴圈用於接收多個客戶端
				boolean bConnected = false;
				Socket s = ss.accept();
System.out.println("a client connected");
				bConnected = true;
				DataInputStream dis = new DataInputStream(s.getInputStream());
				while(bConnected) {//該while迴圈可以連續讀取客戶端傳送過來的資訊
					String str = dis.readUTF();
					System.out.println(str);
				}
				
				dis.close();
			}
		}catch(IOException e) {
			e.printStackTrace();
		}
		
	}

}

Chat0.9

ChatClient無改動

ChatServer

異常處理:

(1)伺服器端啟動後,若再次啟動服務端,則會產生BindException異常。原因是伺服器端啟動後,埠已經被佔用,無法二次啟動。

(2)啟動伺服器端和客戶端後,若關閉客戶端則會發生EOFException。

產生原因:服務端讀取客戶端發過來的資料使用的是readUTF()方法,該方法是阻塞式方法。在客戶端關閉後,服務端並不知曉,仍在等待接收資料。



通過這個API,我們可以得出以下資訊:

  • 這是一個IO異常的子類,名字也是END OF FILE的縮寫,當然也表示流的末尾
  • 它在表明一個資訊,流已經到末尾了,而大部分做法是以特殊值的形式返回給我們,而不是拋異常

  也就是說這個異常是被主動丟擲來的,而不是底層或者編譯器返回給我的,就像NullPointerException或IndexOutOfBoundsException一樣。

詳細解釋見:https://www.cnblogs.com/yiwangzhibujian/p/7107084.html

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.io.DataInputStream;

public class ChatServer {
	public static void main(String[] args) {
		boolean started = false;
		try{
			ServerSocket ss = new ServerSocket(8888);
			started = true;
			while(started) {//該while迴圈用於接收多個客戶端
				boolean bConnected = false;
				Socket s = ss.accept();
System.out.println("a client connected");
				bConnected = true;
				DataInputStream dis = new DataInputStream(s.getInputStream());
				while(bConnected) {//該while迴圈可以連續讀取客戶端傳送過來的資訊
					String str = dis.readUTF();
					System.out.println(str);
				}
				dis.close();
			}
		}catch(IOException e) {
			e.printStackTrace();
		}	
	}
}

Chat1.0

啟動伺服器端後,若開啟多個客戶端視窗,則伺服器端顯示只能連線一個客戶端,其他客戶端視窗連線不上伺服器,向伺服器傳送資料也無法顯示。

原因:伺服器端讀取客戶端傳送過來的資料使用的是readUTF()方法,該方法為阻塞式方法,主方法執行後,會卡在該方法處,一直等待接收資料。這也就導致伺服器端只能處理一個客戶端。

處理方法:開啟多執行緒

import java.io.*;
import java.net.*;

public class ChatServer {
	boolean started = false;
	ServerSocket ss = null;
	
	public static void main(String[] args) {
		new ChatServer().start();
	}

	public void start() {
		try {
			ss = new ServerSocket(8888);
		} catch(BindException e) {
			System.out.println("埠使用中......");
			System.out.println("請關掉相關程式,並重新執行!");
			System.exit(0);
		} catch(IOException e) {
			e.printStackTrace();
		}
		
		try {
			started = true;
			while (started) {
				Socket s = ss.accept();
				Client c = new Client(s);
				new Thread(c).start();
			}
		} catch (IOException e) {
			e.printStackTrace();	
		} finally {
			try {
				ss.close();
			} catch(IOException e1) {
				e1.printStackTrace();
			}
		}
	}
	class Client implements Runnable {
		private Socket s;
		private DataInputStream dis;
		private boolean bConnected = false;
		
		public Client(Socket s) {
			this.s = s;
			try {
				dis = new DataInputStream(s.getInputStream());
				bConnected = true;
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		
		public void run() {
			System.out.println("a client connected");
			try {
				while (bConnected) {
					String str = dis.readUTF();
					System.out.println(str);
				}
			} catch(EOFException e) {
				System.out.println("Client closed");
			} catch (IOException e) {
				e.printStackTrace();	
			} finally {
				try {
					dis.close();
					s.close();
				} catch(IOException e1) {
					e1.printStackTrace();
				}
			}
		}
	}
}

Chat1.1

將從某一客戶端接收到的資料轉發給其他客戶端。

List<Client> clients = new ArrayList<>();//將執行緒物件儲存起來,一個執行緒物件即代表一個客戶端
while (bConnected) {
	String str = dis.readUTF();
        System.out.println(str);
	for(int i = 0; i < clients.size(); i++) {
		Client c = clients.get(i);
		c.send(str);
	}
}
將從客戶端讀取到的資料,通過對List集合的逐一遍歷,傳送給每個客戶端物件。
import java.io.*;
import java.net.*;
import java.util.*;

public class ChatServer {
	ServerSocket ss = null;
	boolean started = false;
	List<Client> clients = new ArrayList<>();
	
	public static void main(String[] args) {
		new ChatServer().start();
	}
	
	public void start() {
		try {
			ss = new ServerSocket(8888);
			started = true;
		} catch(BindException e) {
			System.out.println("埠使用中");
			System.out.println("請關閉相關程式重新執行伺服器");
			System.exit(0);
		} catch(IOException e) {
			e.printStackTrace();
		}
		
		try {
			while(started) {
				Socket s = ss.accept();
				System.out.println("a client connected");
				Client c = new Client(s);
				new Thread(c).start();	
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				ss.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
			
		}
	}
	
	class Client implements Runnable {
		private Socket s;
		private DataInputStream dis;
		private DataOutputStream dos;
		private boolean bConnected = false;
		public Client (Socket s) {
			this.s = s;
			try {
				dis = new DataInputStream(s.getInputStream());
				dos = new DataOutputStream(s.getOutputStream());
				bConnected = true;
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		
		public void send(String str) {
			try {
				dos.writeUTF(str);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		
		public void run() {
			try {
				while (bConnected) {
					String str = dis.readUTF();
System.out.println(str);
					for(int i = 0; i < clients.size(); i++) {
						Client c = clients.get(i);
						c.send(str);
					}
				}
			} catch (EOFException e) {
				System.out.println("Client closed!");
			} catch (IOException e) {
				e.printStackTrace();
			} finally {
				try {
					if(dis != null) {
						dis.close();
					}
					if(s != null) {
						s.close();
					}	
					if(dos != null) {
						dos.close();
					}
				} catch (IOException e) {
					e.printStackTrace();
				}
				
			}
		}
	}
}

Chat1.2

客戶端接收從服務端傳送過來的每個客戶端的資料,並將資料顯示到文字輸入區TextArea中

當我們在向伺服器端傳送本地客戶端的資料時,需要同時接收其他客戶端傳送過來的資料。因此需要使用多執行緒。

1.2異常處理:

開啟多個客戶端,同時進行聊天,當某一個客戶端關閉後,會產生SocketException

原因:開啟多個客戶端後,在關閉其中某一個客戶端時(主執行緒結束),會呼叫disconnect()方法,將客戶端Socket關閉,而此時接收資料的執行緒中的輸入流可能並未關閉,仍會從客戶端Socket中讀取資料,此時就會產生SocketException異常。

簡單處理方式:直接進行捕捉,給出相應提示。

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.IOException;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;

public class ChatClient extends Frame{
	TextField tfTxt = new TextField();
	TextArea taContent = new TextArea();
	Socket s = null;
	DataOutputStream dos = null;
	DataInputStream dis = null;
	private boolean bConnected = false;
	
	public static void main(String[] args) {
		new ChatClient().launchFrame();
	}

	public void launchFrame() {
		setLocation(400, 300);
		setSize(300, 300);
		add(tfTxt, BorderLayout.SOUTH);
		add(taContent, BorderLayout.NORTH);
		pack();
		addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent e) {
				disconnect();
				System.exit(0);
			}	
		});
		tfTxt.addActionListener(new TFListener());
		setVisible(true);
		connect();
		new Thread(new RecvThread()).start();
	}
	
	public void connect() {
		try {
			s = new Socket("127.0.0.1", 8888);
			dos = new DataOutputStream(s.getOutputStream());
			dis = new DataInputStream(s.getInputStream());
			System.out.println("connected");
			bConnected = true;
		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public void disconnect() {
		try {
			dos.close();
			dis.close();
			s.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public class TFListener implements ActionListener {

		@Override
		public void actionPerformed(ActionEvent e) {
			String str = tfTxt.getText().trim();
			//taContent.setText(str);
			tfTxt.setText("");
			
			try {
				dos.writeUTF(str);
				dos.flush();
			} catch (IOException e1) {
				e1.printStackTrace();
			}
			
		}
		
	}

	private class RecvThread implements Runnable {
		public void run() {
			try {
				while(bConnected) {
					String str = dis.readUTF();
					taContent.setText(taContent.getText() + str + '\n');
				}	
			} catch(SocketException e) {
				System.out.println("退出了,bye!");
			} catch(IOException e) {
				e.printStackTrace();
			}
		}	
	}
}
Chat1.3

異常處理:

(1)同時開啟多個客戶端進行聊天,當其中某個客戶端關閉,繼續進行聊天時會產生SocketException。


產生原因:伺服器端會通過Socket向每個客戶端傳送資料,我們將每個客戶端的Socket用List集合進行儲存,伺服器端向每個客戶端傳送資料時,會通過遍歷List集合逐一發送,而此時關閉的客戶端的Socket仍在其中,並未從List集合中移除,因此伺服器端仍然會像關閉的客戶端傳送資料,此時便會產生SocketException異常。

處理方式:在傳送資料時直接從List集合中移除,並給出提示資訊。

import java.io.*;
import java.net.*;
import java.util.*;

public class ChatServer {
	ServerSocket ss = null;
	boolean started = false;
	List<Client> clients = new ArrayList<>();
	
	public static void main(String[] args) {
		new ChatServer().start();
	}
	
	public void start() {
		try {
			ss = new ServerSocket(8888);
			started = true;
		} catch(BindException e) {
			System.out.println("埠使用中");
			System.out.println("請關閉相關程式重新執行伺服器");
			System.exit(0);
		} catch(IOException e) {
			e.printStackTrace();
		}
		
		try {
			while(started) {
				Socket s = ss.accept();
				System.out.println("a client connected");
				Client c = new Client(s);
				new Thread(c).start();
				clients.add(c);
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				ss.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
			
		}
	}
	
	class Client implements Runnable {
		private Socket s;
		private DataInputStream dis;
		private DataOutputStream dos;
		private boolean bConnected = false;
		
		public Client (Socket s) {
			this.s = s;
			try {
				dis = new DataInputStream(s.getInputStream());
				dos = new DataOutputStream(s.getOutputStream());
				bConnected = true;
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		
		public void send(String str) {
			try {
				dos.writeUTF(str);
			} catch (SocketException e) {
				clients.remove(this);
				System.out.println("對方退出了!我從List裡面去掉了!");
			} catch (IOException e) {
				clients.remove(this);
				System.out.println("對方退出了");
			}
		}
		
		public void run() {
			try {
				while (bConnected) {
					String str = dis.readUTF();
System.out.println(str);
					for(int i = 0; i < clients.size(); i++) {
						Client c = clients.get(i);
						c.send(str);
					}
				}
			} catch (EOFException e) {
				System.out.println("Client closed!");
			} catch (IOException e) {
				e.printStackTrace();
			} finally {
				try {
					if(dis != null) {
						dis.close();
					}
					if(s != null) {
						s.close();
					}	
					if(dos != null) {
						dos.close();
					}
				} catch (IOException e) {
					e.printStackTrace();
				}
				
			}
		}
	}
}

注意:EOFException、SocketException均是IOException的子類