Android TCP Socket 聊天室

上一章节中我们已经实现了使用 TCP Socket 和服务器端进行了简短通讯,当然,这样的效果满足一般的只负责拉去内容的 APP 是足够了

但,如果是强交互性的,比如微信、QQ 等可以多人聊天的又要怎么实现呢? 本章节我们就来探索下吧

为了录屏方便,我这里实现自己跟自己聊,你也可以拿两个手机一起聊的

最后效果图


服务器端

要实现多人聊天,最重要的就是将读写 socket的操作放到自定义线程当中,创建 ServerSocket 后,循环 调用 accept 方法,当有新客户端接入,将 socket 加入集合当中,同时在线程池新建一个线程

在读取信息的方法中,对输入字符串进行判断,如果为 bye 字符串,将 socket 从集合中 移除,然后 close()

范例

在某个目录下创建 TcpChatServer.java,比如 d:\dev

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.BufferedWriter;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TcpChatServer {

    //定义相关的参数,端口,存储Socket连接的集合,ServerSocket对象
    //以及线程池
    private static final int PORT = 12345;
    private List<Socket> mList = new ArrayList<Socket>();
    private ServerSocket server = null;
    private ExecutorService myExecutorService = null;


    public static void main(String[] args) {
        new TcpChatServer();
    }

    public TcpChatServer()
    {
        try
        {
            server = new ServerSocket(PORT);
            System.out.println("服务器已经运行在" + getServAddr() + ":12345 ,等待客户端发送数据");

            //创建线程池
            myExecutorService = Executors.newCachedThreadPool();
            Socket client = null;
            while(true)
            {
                client = server.accept();
                mList.add(client);
                myExecutorService.execute(new Service(client));
            }

        }catch(Exception e){e.printStackTrace();}
    }

    class Service implements Runnable
    {
        private Socket socket;
        private BufferedReader in = null;
        private String msg = "";

        public Service(Socket socket) {
            this.socket = socket;
            try
            {
                in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                 msg = "用户:" +this.socket.getInetAddress() + ":" + String.valueOf(this.socket.getPort()) + "~加入了聊天室"  
                            +"当前在线人数:" +mList.size();  
                this.sendmsg();
            }catch(IOException e){e.printStackTrace();}
        }



        @Override
        public void run() {
            try{
                while(true)
                {
                    if((msg = in.readLine()) != null)
                    {
                        if(msg.equals("bye"))
                        {
                            System.out.println("~~~~~~~~~~~~~");
                            mList.remove(socket);
                            in.close();
                            msg = "用户:" + this.socket.getInetAddress() + ":" + String.valueOf(this.socket.getPort())  
                                    + "退出:" +"当前在线人数:"+mList.size();  
                            socket.close();  
                            this.sendmsg();  
                            break;
                        }else{
                            msg = this.socket.getInetAddress() + ":" + String.valueOf(this.socket.getPort()) + "   说: " + msg;  
                            this.sendmsg(); 
                        }
                    }
                }
            }catch(Exception e){e.printStackTrace();}
        }

        //为连接上服务端的每个客户端发送信息
        public void sendmsg()
        {
            System.out.println(msg);
            int num = mList.size();
            for(int index = 0;index < num;index++)
            {
                Socket mSocket = mList.get(index);  
                PrintWriter pout = null;  
                try {  
                    pout = new PrintWriter(new BufferedWriter(  
                            new OutputStreamWriter(mSocket.getOutputStream(),"UTF-8")),true);  
                    pout.println(msg);  
                }catch (IOException e) {e.printStackTrace();}  
            }
        }

    }

        // 获取内网端口号
    public static String getServAddr() {

        try {
            Enumeration<NetworkInterface> allNetInterfaces = NetworkInterface.getNetworkInterfaces();
            while (allNetInterfaces.hasMoreElements()) {
                NetworkInterface netInterface = (NetworkInterface) allNetInterfaces.nextElement();

                // 去除回环接口,子接口,未运行和接口
                if (netInterface.isLoopback() || netInterface.isVirtual() || !netInterface.isUp()) {
                    continue;
                }

                Enumeration<InetAddress> addresses = netInterface.getInetAddresses();
                while (addresses.hasMoreElements()) {
                    InetAddress ip = addresses.nextElement();
                    if (ip != null) {
                        // ipv4
                        if (ip instanceof Inet4Address) {
                            if (ip.getHostAddress().startsWith("192") || ip.getHostAddress().startsWith("10")
                                    || ip.getHostAddress().startsWith("172") || ip.getHostAddress().startsWith("169")) {
                                return ip.getHostAddress();
                            }
                        }
                    }
                }
            }
        } catch (SocketException e) {
            System.err.println("Error when getting host ip address"+ e.getMessage());
        }

        return "";
    }
}

打开终端或命令行提示符,输入以下命令运行 TcpChatServer.java

javac TcpChatServer.java && java TcpChatServer

如果出现下面的文字则说明服务正常启动

服务器已经运行在 192.168.0.108:12345 ,等待客户端发送数据

你的 ip 可能不同

然后客户端就可以通过 192.168.0.108:12345 访问我们的 TcpChatServer 了

客户端

客户端的难点在于要另外开辟线程的问题,因为 Android 不允许直接在 主线程中做网络操作,而且不允许在主线程外的线程操作 UI,这里的做法是自己新建 一个线程,以及通过 Hanlder 来更新 UI,实际开发不建议直接这样做

  1. 创建一个 空的 Android 项目 cn.twle.android.TCPChat

  2. 修改 AndroidManifest.xml 添加网络权限

    <uses-permission android:name="android.permission.INTERNET"/>
    
  3. 修改布局文件 activity_main.xml

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical" >
    
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="简易聊天室" />
        <TextView
            android:id="@+id/ms_show_1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            />
        <EditText
            android:id="@+id/ms_editsend_1"
            android:inputType="number"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            />
    
            <Button
                android:id="@+id/btn_send_1"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="发送"
                />
    
        <TextView
            android:layout_width="match_parent"
            android:layout_height="2dp"
            android:background="#dddddd"/>
    
        <TextView
            android:id="@+id/ms_show_2"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            />
        <EditText
            android:id="@+id/ms_editsend_2"
            android:inputType="number"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            />
    
            <Button
                android:id="@+id/btn_send_2"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="发送"
                />
    </LinearLayout>
    
  4. 修改 MainActivity.java

    package cn.twle.android.tcpchat;
    
    import android.os.Bundle;
    import android.os.Handler;
    import android.os.Message;
    import android.support.v7.app.AppCompatActivity;
    import android.view.View;
    import android.widget.Button;
    import android.widget.EditText;
    import android.widget.TextView;
    
    import java.io.BufferedReader;
    import java.io.BufferedWriter;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.io.OutputStreamWriter;
    import java.io.PrintWriter;
    import java.net.Socket;
    
    public class MainActivity extends AppCompatActivity {
    
        //定义相关变量,完成初始化
        private TextView ms_show_1,ms_show_2;
        private EditText ms_editsend_1,ms_editsend_2;
        private Button btn_send_1, btn_send_2;
    
        private static final String HOST = "192.168.0.108";
        private static final int PORT = 12345;
    
        private Socket socket1 = null;
        private Socket socket2 = null;
    
        private BufferedReader in1 = null;
        private BufferedReader in2 = null;
    
        private PrintWriter out1 = null;
        private PrintWriter out2 = null;
    
        private String content1 = "";
        private String content2 = "";
    
        private StringBuilder sb1 = null;
        private StringBuilder sb2 = null;
    
        //定义一个handler对象,用来刷新界面
        public Handler handler = new Handler() {
            public void handleMessage(Message msg) {
                if (msg.what == 0x123) {
                    sb1.append(content1);
                    ms_show_1.setText(sb1.toString());
                }
    
                if (msg.what == 0x124) {
                    sb2.append(content2);
                    ms_show_2.setText(sb2.toString());
                }
            }
        };
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            sb1 = new StringBuilder();
            sb2 = new StringBuilder();
    
            ms_show_1 = (TextView) findViewById(R.id.ms_show_1);
            ms_show_2 = (TextView) findViewById(R.id.ms_show_2);
    
            ms_editsend_1 = (EditText) findViewById(R.id.ms_editsend_1);
            ms_editsend_2 = (EditText) findViewById(R.id.ms_editsend_2);
    
            btn_send_1 = (Button) findViewById(R.id.btn_send_1);
            btn_send_2 = (Button) findViewById(R.id.btn_send_2);
    
            //当程序一开始运行的时候就实例化Socket对象,与服务端进行连接,获取输入输出流
            //因为4.0以后不能再主线程中进行网络操作,所以需要另外开辟一个线程
            new Thread(new Runnable() {
                @Override
                public void run() {
    
                    try {
    
                        socket1 = new Socket(HOST, PORT);
                        in1 = new BufferedReader(new InputStreamReader(socket1.getInputStream(), "UTF-8"));
                        out1 = new PrintWriter(new BufferedWriter(new OutputStreamWriter(
                                socket1.getOutputStream())), true);
    
                        while (true) {
                            if (socket1.isConnected()) {
                                if (!socket1.isInputShutdown()) {
                                    if ((content1 = in1.readLine()) != null) {
                                        content1 += "\n";
                                        handler.sendEmptyMessage(0x123);
                                    }
                                }
                            }
                        }
    
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
    
            new Thread(new Runnable() {
                @Override
                public void run() {
    
                    try {
    
                        socket2 = new Socket(HOST, PORT);
                        in2 = new BufferedReader(new InputStreamReader(socket2.getInputStream(), "UTF-8"));
                        out2 = new PrintWriter(new BufferedWriter(new OutputStreamWriter(
                                socket1.getOutputStream())), true);
    
                        while (true) {
                            if (socket2.isConnected()) {
                                if (!socket2.isInputShutdown()) {
                                    if ((content2 = in2.readLine()) != null) {
                                        content2 += "\n";
                                        handler.sendEmptyMessage(0x124);
                                    }
                                }
                            }
                        }
    
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
    
            //为发送按钮设置点击事件
            btn_send_1.setOnClickListener(new View.OnClickListener() {
    
                @Override
                public void onClick(View v) {
                    sendMessage(socket1, out1,ms_editsend_1.getText().toString());
                    ms_editsend_1.setText("");
                }
            });
    
            //为发送按钮设置点击事件
            btn_send_2.setOnClickListener(new View.OnClickListener() {
    
                @Override
                public void onClick(View v) {
                    sendMessage(socket2, out2,ms_editsend_2.getText().toString());
                    ms_editsend_2.setText("");
                }
            });
    
        }
    
        public void sendMessage(Socket sock, PrintWriter pw, String m)
        {
            final String chat_msg = m;
            final PrintWriter w = pw;
            final Socket s = sock;
            new Thread(new Runnable() {
                @Override
                public void run() {
                    if (s.isConnected()) {
                        if (!s.isOutputShutdown()) {
                            w.println(chat_msg);
                        }
                    }
                }
            }).start();
        }
    }
    

Android 基础教程

关于   |   FAQ   |   我们的愿景   |   广告投放   |  博客

  简单教程,简单编程 - IT 入门首选站

Copyright © 2013-2022 简单教程 twle.cn All Rights Reserved.