看看今天實現的Android聊天室的聊天功能吧,先說說服務器端的代碼及其功能吧
server.java : 負責服務器的界面,以及更服務器主線程ServerThread的啟動,產生了BroadCast廣播,產生ClientThread線程
ServerThread.java:服務器監聽的端口線程,負責創建ServerSocket及監聽是否有新的客戶端連接,並記錄客戶端連接及需要發送的信息,產生了BroadCast廣播
BroadCast.java: 服務器向客戶端廣播線程,負責向客戶端發送消息,產生ClientThread線程
ClientThread.java:維持服務器和單個客戶端的連接線程,負責接受客戶端發來是信息
好了接著就看看他們的代碼吧!!
1.server.java-------創建ServerThread對象啟動run方法
- package com.wang;
-
- import java.awt.BorderLayout;
- import javax.swing.*;
- import java.awt.event.*;
-
- public class Server extends JFrame implements ActionListener {
-
- /****
- *服務器端主程序負責界面,以及服務段主線程ServerThread的啟動
- * 服務端主線程ServerThread又產生BroadCast及ClientThread線程 建立服務器端主界面中所用到的布局方式
- ***/
- // 邊框容器
- BorderLayout borderLayout1 = new BorderLayout();
- BorderLayout borderLayout2 = new BorderLayout();
- // 創建面板
- JPanel jPanel1 = new JPanel();
- JPanel jPanel2 = new JPanel();
- // 創建按鈕
- JButton jButton1 = new JButton();
- JButton jButton2 = new JButton();
- JScrollPane jScrollPane1 = new JScrollPane();
-
- // 創建服務器端接收信息文本框
- static JTextArea jTextArea1 = new JTextArea();
- boolean bool = false, start = false;
-
- // 聲明ServerThread線程類對象
- ServerThread serverThread;
- Thread thread;
-
- // 構造函數,用於初始化
- public Server() {
- super("Server");
- // 設置內容面板布局方式
- getContentPane().setLayout(borderLayout1);
-
- // 初始化按鈕組件
- jButton1.setText("啟動服務器");
- // 按鈕的動作設置監聽事件
- jButton1.addActionListener(this);
- jButton2.setText("關閉服務器");
- // 按鈕的動作設置監聽事件
- jButton2.addActionListener(this);
-
- // 初始化jPanel1面板對象,並向其中加入組件,上北
- this.getContentPane().add(jPanel1, java.awt.BorderLayout.NORTH);
- jPanel1.add(jButton1);
- jPanel1.add(jButton2);
-
- // 初始化jPanel2面板對象,並向其中加入組件,
- jTextArea1.setText("");
- jPanel2.setLayout(borderLayout2);
- jPanel2.add(jScrollPane1, java.awt.BorderLayout.CENTER);
- jScrollPane1.getViewport().add(jTextArea1);
- this.getContentPane().add(jPanel2, java.awt.BorderLayout.CENTER);
-
- this.setSize(400, 400);
- this.setVisible(true);
- }
-
- public static void main(String[] args) {
- Server sever = new Server();
- sever.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
- }
-
- // 服務器界面中按鈕事件處理
- public void actionPerformed(ActionEvent e) {
- if (e.getSource() == jButton1) {
- // 聲明一個ServerThread對象
- serverThread = new ServerThread();
- serverThread.start();
- } else if (e.getSource() == jButton2) {
- bool = false;
- start = false;
- serverThread.finalize();
- this.setVisible(false);
- }
- }
- }
2.ServerThread.java -----創建Broadcast對象,啟動該線程,實現run方法後,不斷的向客戶端發送消息,ServerThread開啟後,不斷的獲取新的客戶端並監聽是否發送消息
- package com.wang;
-
- import java.util.*;
- import java.io.*;
- import java.net.*;
-
- public class ServerThread extends Thread
- // 服務器監聽端口線程
- {
- // 聲明ServerSocket類對象
- ServerSocket serverSocket;
- // 指定服務器監聽端口常量
- public static final int PORT = 80;
-
- /**
- * 創建一個Vector對象,用於存儲客戶端連接的ClientThread對象 , ClientThread類維持服務器與單個客戶端的連接線程
- * 負責接收客戶端發來的信息,clients負責存儲所有與服務器建立連接的客戶端
- **/
-
- Vector<ClientThread> clients;
- // 創建一個Vector對象,用於存儲客戶端發送過來的信息
- Vector<Object> messages;
- // BroadCast類負責服務器向客戶端廣播消息
- BroadCast broadcast;
-
- String ip;
- InetAddress myIPaddress = null;
-
- public ServerThread() {
- /***
- * 創建兩個Vector數組非常重要 , clients負責存儲所有與服務器建立連接的客戶端,
- * messages負責存儲服務器接收到的未發送出去的全部客戶端的信息
- *
- **/
- clients = new Vector<ClientThread>();
- messages = new Vector<Object>();
-
- try {
- // 創建ServerSocket類對象
- serverSocket = new ServerSocket(PORT);
- } catch (IOException E) {
- }
- // 獲取本地服務器地址信息
- try {
- myIPaddress = InetAddress.getLocalHost();
- } catch (UnknownHostException e) {
- System.out.println(e.toString());
- }
- ip = myIPaddress.getHostAddress();
- Server.jTextArea1.append("服務器地址:" + ip + "端口號:"
- + String.valueOf(serverSocket.getLocalPort()) + "\n");
-
- // 創建廣播信息線程並啟動
- broadcast = new BroadCast(this);
- broadcast.start();
- }
-
- /**
- * 注意:一旦監聽到有新的客戶端創建即new Socket(ip, PORT)被執行,
- * 就創建一個ClientThread來維持服務器與這個客戶端的連接
- **/
- public void run() {
- while (true) {
- try {
- // 獲取客戶端連接,並返回一個新的Socket對象
- Socket socket = serverSocket.accept();
-
- System.out.println(socket.getInetAddress().getHostAddress());
- // 創建ClientThread線程並啟動,可以監聽該連接對應的客戶端是否發送來消息, 並獲取消息
- ClientThread clientThread = new ClientThread(socket, this);
- clientThread.start();
- if (socket != null) {
- synchronized (clients) {
- // 將客戶端連接加入到Vector數組中保存
- clients.addElement(clientThread);
- }
- }
- } catch (IOException E) {
- System.out.println("發生異常:" + E);
- System.out.println("建立客戶端聯機失敗!");
- System.exit(2);
- }
- }
- }
-
- public void finalize() {
- try {
- // 關閉serverSocket方法
- serverSocket.close();
- } catch (IOException E) {
- }
- serverSocket = null;
- }
- }
3.BroadCast.java------
- package com.wang;
-
- import java.io.*;
-
- public class BroadCast extends Thread { // 服務器向客戶端廣播線程
- ClientThread clientThread;
- // 聲明ServerThread對象
- ServerThread serverThread;
- String str;
-
- public BroadCast(ServerThread serverThread) {
- this.serverThread = serverThread;
- }
-
- // 該方法的作用是不停地向所有客戶端發送新消息
- public void run() {
- while (true) {
- try {
- // 線程休眠200 ms
- Thread.sleep(200);
- } catch (InterruptedException E) {
- }
-
- // 同步化serverThread.messages
- synchronized (serverThread.messages) {
- // 判斷是否有未發的消息
- if (serverThread.messages.isEmpty()) {
- continue;
- }
- // 獲取服務器端存儲的需要發送的第一條數據信息
- str = (String) this.serverThread.messages.firstElement();
- }
- // 同步化serverThread.clients
- synchronized (serverThread.clients) {
- // 利用循環獲取服務器中存儲的所有建立的與客戶端的連接
- for (int i = 0; i < serverThread.clients.size(); i++) {
- clientThread = (ClientThread) serverThread.clients
- .elementAt(i);
- try {
- // 向記錄的每一個客戶端發送數據信息
- clientThread.out.writeUTF(str);
- } catch (IOException E) {
- }
- }
- // 從Vector數組中刪除已經發送過的那條數據信息
- this.serverThread.messages.removeElement(str);
- }
- }
- }
- }
4.ClientThread.java----獲得Socket的輸入輸出流,向客戶端接收或者發送數據
- package com.wang;
-
- import java.net.*;
- import java.io.*;
- public class ClientThread extends Thread
- {
- /**
- * 維持服務器與單個客戶端的連接線程,負責接收客戶端發來的信息,
- * 聲明一個新的Socket對象,
- * 用於保存服務器端用accept方法得到的客戶端的連接
- **/
- Socket clientSocket;
-
- //聲明服務器端中存儲的Socket對象的數據輸入/輸出流
- DataInputStream in = null;
- DataOutputStream out = null;
-
- //聲明ServerThread對象
- ServerThread serverThread;
-
- public ClientThread(Socket socket,ServerThread serverThread)
- {
- clientSocket=socket;
- this.serverThread=serverThread;
- try
- {
- //創建服務器端數據輸入/輸出流
- in = new DataInputStream(clientSocket.getInputStream());
- out = new DataOutputStream(clientSocket.getOutputStream());
- }
- catch (IOException e2)
- {
- System.out.println("發生異常"+e2);
- System.out.println("建立I/O通道失敗!");
- System.exit(3);
- }
- }
- //該方法監聽該連接對應得客戶端是否有消息發送
- public void run()
- {
- while(true)
- {
- try
- {
- //讀入客戶端發送來的信息
- String message=in.readUTF();
- synchronized(serverThread.messages)
- {
- if(message!=null)
- {
- //將客戶端發送來得信息存於serverThread的messages數組中
- serverThread.messages.addElement(message);
- //在服務器端的文本框中顯示新消息
- Server.jTextArea1.append(message+'\n');
- }
- }
- }
- catch(IOException E){break;}
- }
- }
- }
5.接著看看手機客戶端的布局main.xml
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:orientation="vertical" >
-
- <LinearLayout
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal" >
-
- <EditText
- android:id="@+id/username"
- android:layout_width="270dp"
- android:layout_height="wrap_content"
- android:hint="請輸入用戶名:"
- android:maxLength="10" >
- </EditText>
-
- <Button
- android:id="@+id/LoginButton"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="登陸" />
- </LinearLayout>
-
- <LinearLayout
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal" >
-
- <EditText
- android:id="@+id/ip"
- android:layout_width="270dp"
- android:layout_height="wrap_content"
- android:digits=".1234567890"
- android:hint="10.254.1.62" >
- </EditText>
-
- <Button
- android:id="@+id/LeaveButton"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="退出" />
- </LinearLayout>
-
- <EditText
- android:id="@+id/history"
- android:layout_width="fill_parent"
- android:layout_height="280dp" >
- </EditText>
-
- <LinearLayout
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal" >
-
- <EditText
- android:id="@+id/message"
- android:layout_width="270dp"
- android:layout_height="wrap_content" >
- </EditText>
-
- <Button
- android:id="@+id/SendButton"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="發送" />
- </LinearLayout>
-
- </LinearLayout>