Android上JAVA Socket通信解析(二)多线程通信

来源:互联网 发布:格雷格.门罗数据 编辑:程序博客网 时间:2024/05/21 05:40

在上一篇文章中,我们初步明白了一个socket如何在客户端和服务器端进行通信的过程,客户端和服务器端能够进行简单的通信,这一篇文章,我们将加入多线程通信的部分,这部分在实际工作中是很有用的;上一篇内容,请参考:Android上JAVA Socket通信解析(一)初识socket通信


1. 简介

前面服务器和客户端只是进行了简单的通信操作:服务器端接受客户端的连接请求后,返回一个字符串给客户端;但是在实际的使用过程中,客户端会合服务器端进行长时间的通信,服务器端不断的读取客户端发来的数据,并向客户端写入数据;客户端不断的从服务器读取数据,兵饷服务器端写入数据;

当服务器使用inputstream读取数据的时候,在该方法返回之前,线程被阻塞,程序无法继续执行,故服务器端应该为每个socket单独启动一个线程,每条线程负责与一个客户端进行通信;

同理,客户端读取服务器的数据的线程也会被阻塞,所以客户端应该单独启动一个线程,这个线程专门负责读取服务器数据;

下面模拟一个简单的C/S架构的聊天室应用:服务器端每接收到一个socket请求,就开一个线程与客户端通信,并保存线程到list中,对每个socket请求发送来的数据,都转发输出不到同的socket客户端;

2. 服务器端代码

下面是服务器端代码:一个serversocket监听的类,负责监听客户端的请求,一个负责处理客户端通信的线程类ServerThread,每个线程类ServerThread都有一个回调监听;

服务器端循环监听接受客户端的请求,接收到客户端的请求后,开启一个线程与客户端通信,并将新的线程添加到list中,对于回调,当客户端有消息数据到达服务器端,监听回调方法onMsgArrive,循环线程列表,每个线程发送消息内容给客户端,进行转发,对于不存在的消息从列表list中删除;


public class MyServer{    private static final String CODE_TYPE = "utf-8";    private static ArrayList<Socket> sockets = new ArrayList<Socket>();    /**     * @Description TODO(描述这个方法的作用)<br/>     * @param args     *            void     * @date 2017-8-26     */    public static void main(String[] args)    {        // TODO Auto-generated method stub        try        {            ServerSocket ss = new ServerSocket(4000);            while (true)            {                Socket s = ss.accept();                System.out.println("new socket receive ");                sockets.add(s);                new Thread(new ServerThread(s, clientStatuListner)).start();            }        } catch (IOException e)        {            // TODO Auto-generated catch block            e.printStackTrace();        }    }    private static OnClientStatuListner clientStatuListner = new OnClientStatuListner()    {        @Override        public void onMsgArrive(String msg)        {            System.out.println("onMsgArrive, msg:" + msg);            if (sockets == null || sockets.isEmpty())            {                return;            }            for (Socket socket : sockets)            {                try                {                    System.out.println("send msg:" + msg+",size:"+sockets.size());                    OutputStream os = socket.getOutputStream();                    os.write(("服务:"+msg).getBytes(CODE_TYPE));                } catch (IOException e)                {                    e.printStackTrace();                    sockets.remove(socket);                }            }        }        @Override        public void onClientFail(Socket s)        {            if (s != null)            {                System.out.println("onClientFail");                sockets.remove(s);            }        }    };}
服务器端的线程通信类ServerThread;读取客户端的数据,并调用收到消息的回调方法clientStatuListner.onMsgArrive(content);

public class ServerThread implements Runnable{    private Socket socket;    private BufferedReader bReader = null;    private static final String CODE_TYPE = "utf-8";    private OnClientStatuListner clientStatuListner;    public ServerThread(Socket s, OnClientStatuListner clientStatuListner)    {        socket = s;        this.clientStatuListner = clientStatuListner;        try        {            bReader = new BufferedReader(new InputStreamReader(socket.getInputStream(),                    CODE_TYPE));        } catch (UnsupportedEncodingException e)        {            // TODO Auto-generated catch block            e.printStackTrace();        } catch (IOException e)        {            // TODO Auto-generated catch block            e.printStackTrace();        }    }    @Override    public void run()    {        System.out.println("socket run");        String content = null;        while ((content = readfromClent()) != null)        {            System.out.println("接受消息:content:" + content);            if (clientStatuListner != null)            {                clientStatuListner.onMsgArrive(content);            }        }    }    private String readfromClent()    {        try        {                        System.out.println("socket read input");            return bReader.readLine();        } catch (IOException e)        {            e.printStackTrace();            clientStatuListner.onClientFail(socket);        }        return null;    }}


3,客户端代码

客户端使用Android应用的activity,Android界面上包含两个线程:一个线程负责读取需要发送给服务器端的数据信息,并写入到对应的socket输出流,发送给服务器端;一个线程负责读取输入流,读取服务器端发送过来的数据,并将数据在activity界面上显示;

如下是activity的布局文件:

<?xml version="1.0" encoding="utf-8"?><LinearLayout    xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical"    tools:context="com.anana.sockettest.MainActivity">    <LinearLayout        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:orientation="vertical">        <EditText            android:id="@+id/input"            android:layout_width="368dp"            android:layout_height="wrap_content"            android:text="input"            />        <Button            android:id="@+id/send"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:text="发送"            />        <TextView            android:id="@+id/show"            android:layout_width="368dp"            android:layout_height="wrap_content"            android:text="展示内容:"            />    </LinearLayout></LinearLayout>

acitivity客户端代码:

public class MainActivity extends AppCompatActivity{    private static final String TAG = "socket";    Button send;    EditText input;    TextView show;    private Handler handler;    ClientThread clientThread;    public static final int SHOW = 1;    @Override    protected void onCreate(Bundle savedInstanceState)    {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        handler = new Handler()        {            @Override            public void handleMessage(Message msg)            {                switch (msg.what)                {                    case SHOW:                        show.append("\n" + msg.obj.toString());                        break;                    default:                        break;                }                super.handleMessage(msg);            }        };        clientThread = new ClientThread(handler);        new Thread(clientThread).start();        input = (EditText) findViewById(R.id.input);        send = (Button) findViewById(R.id.send);        show = (TextView) findViewById(R.id.show);        send.setOnClickListener(new View.OnClickListener()        {            @Override            public void onClick(View view)            {                String content = input.getText().toString();                Message msg = new Message();                msg.what = ClientThread.SEND;                msg.obj = content;                clientThread.pushHandler.sendMessage(msg);//                clientThread.send(content);                input.setText("");            }        });    }}
客户端的线程处理类ClientThread:不断的读取socket的输入流,当读到收远程服务器的消息数据后,通知主界面显示,同时,发送数据到远程服务器端;

public class ClientThread implements Runnable{    Handler handler;    public Handler pushHandler;    private Socket s;    BufferedReader br;    private OutputStream os;    public ClientThread(Handler handler)    {        this.handler = handler;    }    private static final String SERVER_IP = "192.168.23.104";    private static final int PORT = 4000;    @Override    public void run()    {        try        {//            s = new Socket();            s = new Socket(SERVER_IP, PORT);//            s.connect(new InetSocketAddress(SERVER_IP, PORT), 5000);//            s.setSoTimeout(5000);            br = new BufferedReader(new InputStreamReader(s.getInputStream                    ()));//            scan = new Scanner(s.getInputStream());            os = s.getOutputStream();            new Thread()            {                @Override                public void run()                {                    String line = null;                    try                    {                        while ((line = br.readLine()) != null)                        {                            System.out.println("reveive:msg:" + line);                            Message msg = new Message();                            msg.what = MainActivity.SHOW;                            msg.obj = line;                            handler.sendMessage(msg);                        }                    }                    catch (IOException e)                    {                        e.printStackTrace();                    }                }            }.start();            startpush();        }        catch (ConnectException e)        {            e.printStackTrace();        }        catch (SocketTimeoutException e)        {            e.printStackTrace();        }        catch (IOException e)        {            e.printStackTrace();        }    }    public static final int SEND = 33;    public void send(String msg)    {        try        {            System.out.println("send :msg:" + msg);            os.write(msg.getBytes("utf-8"));        }        catch (Exception e)        {            e.printStackTrace();        }    }    private void startpush()    {        Looper.prepare();        pushHandler = new Handler()        {            @Override            public void handleMessage(Message msg)            {                switch (msg.what)                {                    case SEND:                        try                        {                            String content = msg.obj.toString()+"\r\n";                            System.out.println("send :msg:" + content);                            os.write(content.getBytes("utf-8"));                        }                        catch (Exception e)                        {                            e.printStackTrace();                        }                        break;                    default:                        break;                }                super.handleMessage(msg);            }        };        Looper.loop();    }}