網絡編程_一個簡單的聊天雛形


                        此聊天程序,可以作爲java網絡學習的入門例子,程序雖小,五臟俱全,不過很多細節問題需要花時間完善。

1、聊天系統主要有兩個模塊:Client和Server
2、Client端功能:
       ①產生一個聊天窗口
      ②根據服務端的ip+端口後,通過Socket連接到服務端
       ③向服務端發送消息,並接受服務端的消息
   ④同時將消息顯示在聊天窗口上面
   
3、Server端功能:
 ①監聽端口
  ②接受服務端的請求,並與服務端建立連接
 ③接受消息,轉發消息至所有服務端


4、代碼ChatClient.java

    

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 {

	private Socket s = null;
	DataOutputStream dos;
	DataInputStream dis;
	private boolean bConnected = false;

	TextField tfTxt;
	TextArea taContent;
	// 定義接收消息的線程
	Thread tRecv = new Thread(new RecvThread());

	public static void main(String[] args) {
		new ChatClient().lanuchFrame();
	}

	public void lanuchFrame() {
		this.setLocation(400, 300);
		this.setSize(300, 300);

		tfTxt = new TextField();
		taContent = new TextArea();

		taContent.setEditable(false);
		this.add(tfTxt, BorderLayout.SOUTH);
		this.add(taContent, BorderLayout.NORTH);
		this.pack();
		// 添加匿名監聽器
		this.addWindowListener(new WindowAdapter() {
			@Override
			public void windowClosing(WindowEvent e) {
				disConnect();
				System.exit(0);
			}

		});
		tfTxt.addActionListener(new TFListener());
		this.setVisible(true);
		connect();
		tRecv.start();
	}

	/**
	 * 連接服務器
	 */
	public void connect() {
		try {   
			s = new Socket("127.0.0.1", 8888); //這個地方用來添加服務器ip+端口,根據實際改
			dos = new DataOutputStream(s.getOutputStream());
			dis = new DataInputStream(s.getInputStream());
			System.out.println("connected!");
			bConnected = true;
		} catch (UnknownHostException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public void disConnect() {

		/*
		try {
			// ①讓接收消息的線程停下來
			bConnected = false;
			// ②結束線程
            tRecv.join();
            
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			try {
				dos.close();
				dis.close();
				s.close();
			} catch (IOException e) {
				// TODO Auto-generated catch
				e.printStackTrace();
			}
		}
		*/

		try {
			// ①讓接收消息的線程停下來
			bConnected = false;
			// ②結束線程
			tRecv.join();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	// 添加監聽器的三種方式:①外部類 ②內部類 ③匿名類
	private 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();
				/*
				 * //當dis關閉時,裏面包裝的流都會被關閉, 導致發一次消息就不能再放第二次了,所有要註釋掉dos.close();
				 */
				// dos.close();
			} catch (IOException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}

		}

	}

	private class RecvThread implements Runnable {

		@Override
		public void run() {

			try {
				while (bConnected) {
					String str = dis.readUTF();
					// System.out.println(str);
					taContent.setText(taContent.getText() + str + "\n");
				}
			} catch (SocketException e) {
				System.out.println("退出了!和你們說88了");

			} catch (IOException e) {
				// TODO Auto-generated catch block
				System.out.println("退出了!和你們說88了");
			}

		}

	}

}

5、代碼ChatServer.java

     

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;

/**
 * 設計思路:1 在主線程裏面啓動一個ServerSocket,不停的監聽客戶端的請求
 * 2:每當接收到了一個客服端,將其Socket包裝到一個線程裏面,讓線程來 處理服務器與客戶端的通信
 *  3:這樣主線程用來接收客服端,子線程用來和每個客戶端通信
 *
 * 注意:內部類的權限和非靜態實例的一樣,不能在靜態方法裏面new內部類, 只能用外部實例來new內部類
 * 
 * @author Administrator
 * 
 */
public class ChatServer {
	ServerSocket ss = null;
	boolean started = false;

	List<Client> clients = new ArrayList<Client>();

	public static void main(String[] args) {
		new ChatServer().start();
	}

	public void start() {
		// ①建立一個ServerSocket

		try {
			ss = new ServerSocket(8888);
			started = true;
		} catch (BindException e1) {
			// TODO Auto-generated catch block
			System.out.println("端口使用中...");
			System.out.println("請關閉相關的程序並重新運行服務器!");
			System.exit(0);
		} catch (IOException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}

		// ②連接客戶端
		try {
			while (started) {
				Socket s = ss.accept();
				// 服務器接收一個Socket後,啓動一個線程來處理客服端的
				// 通信

				Client c = new Client(s);
				System.out.println("a client connected!");
				// 使用線程來接收客服端的信息
				new Thread(c).start();
				clients.add(c);
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			try {
				ss.close();// 關閉ServerSocket
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

	}

	/*
	 * 客戶端在服務端的一個包裝
	 */
	class Client implements Runnable {

		private Socket s;
		private DataInputStream dis = null;
		private DataOutputStream dos = null;
		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) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

		public void send(String str) {
				try {
					dos.writeUTF(str);
				} catch (IOException e) {
					clients.remove(this);
					System.out.println("對方退出了!我從List裏面去掉了");
//					e.printStackTrace();
				}
		}

		@Override
		public void run() {
			Client c=null;
			try {
				while (bConnected) {
					String str = dis.readUTF();
					// 每接收一個字符串,就全部轉發給其他的客戶端
					System.out.println(str);
//					for (Client c : clients) {
//						c.send(str);
//					}
					for(int i=0;i<clients.size();i++){
						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 (dos != null)
						dos.close();
					if (s != null) {
						s.close();
//						s=null;
					}
					
				} catch (IOException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}
				
			}

		}

	}
}

6、運行:1)運行ChatServer.java:①爲其指定監聽端口,我這裏設置爲8888,

                 2)運行ChatClient.java:①爲其指定連接的ip+端口:這裏我用的是本地127.0.0.1:8888,這根據ChatServer運行的主機爲主

    3)效果圖:

      



  

發佈了31 篇原創文章 · 獲贊 5 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章