使用Android网络编程实现简易聊天室

在Java中我们可以利用socket编程实现聊天室,在Android中也一样,因为Android完全支持JDK本身的TCP、UDP网络通信API。我们可以使用ServerSocket、Socket来建立基于TCP/IP协议的网络通信;也可以使用DatagramSocket、Datagrampacket、MulticastSocket来建立基于UDP协议的网络通信。下面实现一个简单的聊天室,服务器端完全用Java代码实现,跟Android无关,客户端用Android应用来实现。

服务器不断读取来自客户端的信息,并即时地将信息发送给每个连接到本服务器上的客户端,每个客户端可以向服务器发送消息,并不断地接收来自服务器的消息,并将消息显示在界面上。这样就实现了客户端与客户端之间的即时聊天功能,为简便起见,本文中只写了一个客户端,代码如下:

服务器端:

创建服务器的主类:

[java][/java] view plaincopy

  1. package com.home.server;
  2. import java.io.IOException;
  3. import java.net.ServerSocket;
  4. import java.net.Socket;
  5. import java.util.ArrayList;
  6. public class MyServer {
  7.     // 定义保存所有Socket的集合
  8.     public static ArrayList<Socket> socketList = new ArrayList<Socket>();
  9.     public static void main(String[] args) throws IOException {
  10.         ServerSocket ss = new ServerSocket(20000);
  11.         System.out.println(“服务器创建成功!”);
  12.         System.out.println(“等待客戶端的连接。。。”);
  13.         while (true) {
  14.             // 此行代码会阻塞,等待用户的连接
  15.             Socket socket = ss.accept();
  16.             System.out.println(“有客户端连接进来!”);
  17.             socketList.add(socket);
  18.             // 每当客户端连接后启动一条ServerThread线程为该客户端服务
  19.             new Thread(new ServerThread(socket)).start();
  20.         }
  21.     }
  22. }

服务器的线程类:

[java][/java] view plaincopy

  1. package com.home.server;
  2. import java.io.BufferedReader;
  3. import java.io.IOException;
  4. import java.io.InputStreamReader;
  5. import java.io.OutputStream;
  6. import java.net.Socket;
  7. public class ServerThread implements Runnable {
  8.     // 定义当前线程所处理的Socket
  9.     private Socket socket = null;
  10.     // 该线程所处理的Socket所对应的输入流
  11.     BufferedReader br = null;
  12.     public ServerThread(Socket socket) throws IOException {
  13.         this.socket = socket;
  14.         // 初始化该Socket对应的输入流
  15.         br = new BufferedReader(new InputStreamReader(socket.getInputStream(),
  16.                 “utf-8”));
  17.     }
  18.     @Override
  19.     public void run() {
  20.         try {
  21.             String content = null;
  22.             // 采用循环不断从Socket中读取客户端发送过来的数据
  23.             while ((content = readFromClient()) != null) {
  24.                 // 遍历socketList中的每个Socket,将读到的内容向每个Socket发送一次
  25.                 for (Socket s : MyServer.socketList) {
  26.                     OutputStream os = s.getOutputStream();
  27.                     os.write((content + “\n”).getBytes(“utf-8”));
  28.                 }
  29.             }
  30.         } catch (Exception e) {
  31.             e.printStackTrace();
  32.         }
  33.     }
  34.     /**
  35.      * 定义读取客户端数据的方法
  36.      *
  37.      * @return
  38.      */
  39.     private String readFromClient() {
  40.         try {
  41.             return br.readLine();
  42.         }
  43.         // 如果捕捉到异常,表明该Socket对应的客户端已经关闭
  44.         catch (Exception e) {
  45.             // 删除该Socket
  46.             MyServer.socketList.remove(socket);
  47.             e.printStackTrace();
  48.         }
  49.         return null;
  50.     }
  51. }

客户端主类(Activity):

[java][/java] view plaincopy

  1. package com.home.activity;
  2. import java.io.OutputStream;
  3. import java.net.Socket;
  4. import android.app.Activity;
  5. import android.os.Bundle;
  6. import android.os.Handler;
  7. import android.os.Message;
  8. import android.view.View;
  9. import android.view.View.OnClickListener;
  10. import android.widget.Button;
  11. import android.widget.EditText;
  12. import com.home.R;
  13. import com.home.util.ClientThread;
  14. public class MultiThreadClient extends Activity {
  15.     private EditText input, show;
  16.     private Button sendBtn;
  17.     private OutputStream os;
  18.     private Handler handler;
  19.     @Override
  20.     protected void onCreate(Bundle savedInstanceState) {
  21.         super.onCreate(savedInstanceState);
  22.         setContentView(R.layout.activity_main);
  23.         input = (EditText) findViewById(R.id.main_et_input);
  24.         show = (EditText) findViewById(R.id.main_et_show);
  25.         sendBtn = (Button) findViewById(R.id.main_btn_send);
  26.         handler = new Handler() {
  27.             @Override
  28.             public void handleMessage(Message msg) {
  29.                 // 如果消息来自子线程
  30.                 if (msg.what == 0x234) {
  31.                     // 将读取的内容追加显示在文本框中
  32.                     show.append(“\n” + msg.obj.toString());
  33.                 }
  34.             }
  35.         };
  36.         Socket socket;
  37.         try {
  38.             socket = new Socket(“192.168.0.101”, 20000);
  39.             // 客户端启动ClientThread线程不断读取来自服务器的数据
  40.             new Thread(new ClientThread(socket, handler)).start();
  41.             os = socket.getOutputStream();
  42.         } catch (Exception e) {
  43.             e.printStackTrace();
  44.         }
  45.         sendBtn.setOnClickListener(new OnClickListener() {
  46.             @Override
  47.             public void onClick(View v) {
  48.                 try {
  49.                     // 将用户在文本框内输入的内容写入网络
  50.                     os.write((input.getText().toString() + “\r\n”).getBytes());
  51.                     // 清空input文本框数据
  52.                     input.setText(“”);
  53.                 } catch (Exception e) {
  54.                     e.printStackTrace();
  55.                 }
  56.             }
  57.         });
  58.     }
  59. }

客户端线程类:

[java][/java] view plaincopy

  1. package com.home.util;
  2. import java.io.BufferedReader;
  3. import java.io.IOException;
  4. import java.io.InputStreamReader;
  5. import java.net.Socket;
  6. import android.os.Handler;
  7. import android.os.Message;
  8. public class ClientThread implements Runnable {
  9.     private Handler handler;
  10.     // 该线程所处理的Socket所对应的输入流
  11.     private BufferedReader br = null;
  12.     public ClientThread(Socket socket, Handler handler) throws IOException {
  13.         this.handler = handler;
  14.         br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
  15.     }
  16.     @Override
  17.     public void run() {
  18.         try {
  19.             String content = null;
  20.             // 不断读取Socket输入流的内容
  21.             while ((content = br.readLine()) != null) {
  22.                 // 每当读到来自服务器的数据之后,发送消息通知程序界面显示该数据
  23.                 Message msg = new Message();
  24.                 msg.what = 0x234;
  25.                 msg.obj = content;
  26.                 handler.sendMessage(msg);
  27.             }
  28.         } catch (Exception e) {
  29.             e.printStackTrace();
  30.         }
  31.     }
  32. }

Activity的布局XML:

[html][/html] view plaincopy

  1. <LinearLayout xmlns:android=”http://schemas.android.com/apk/res/android”
  2.     android:layout_width=”match_parent”
  3.     android:layout_height=”match_parent”
  4.     android:orientation=”vertical” >
  5.     <LinearLayout
  6.         android:layout_width=”match_parent”
  7.         android:layout_height=”wrap_content”
  8.         android:orientation=”horizontal” >
  9.         <EditText
  10.             android:id=”@+id/main_et_input”
  11.             android:layout_width=”240dp”
  12.             android:layout_height=”wrap_content” />
  13.         <Button
  14.             android:id=”@+id/main_btn_send”
  15.             android:layout_width=”match_parent”
  16.             android:layout_height=”wrap_content”
  17.             android:paddingLeft=”10dp”
  18.             android:text=”发送” />
  19.     </LinearLayout>
  20.     <EditText
  21.         android:id=”@+id/main_et_show”
  22.         android:layout_width=”match_parent”
  23.         android:layout_height=”match_parent”
  24.         android:cursorVisible=”false”
  25.         android:editable=”false”
  26.         android:gravity=”top” />
  27. </LinearLayout>

权限:

[html][/html] view plaincopy

  1. <uses-permission android:name=”android.permission.INTERNET”/>

标签