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; }}
客户端使用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(); }}
- Android上JAVA Socket通信解析(二)多线程通信
- Android上JAVA Socket通信解析(一)初识socket通信
- Java Socket通信(多线程)
- Java Socket实战之二 多线程通信
- Java Socket实战之二 多线程通信
- Java Socket实战之二 多线程通信 .
- Java Socket实战之二 多线程通信
- Java Socket实战之二 多线程通信
- Java Socket实战之二:多线程通信
- Java Socket实战之二 多线程通信
- Java Socket实战之二 多线程通信
- Java Socket实战之二 多线程通信
- Java Socket实战之二 多线程通信
- Java Socket实战之二:多线程通信
- Java Socket实战之二 多线程通信
- Java Socket实战之二 多线程通信
- Java Socket实战之二 多线程通信
- Java Socket实战之二 多线程通信
- C# 一些概念
- Leetcode-N-Queens II
- 微信多客户端导致重复调用服务接口问题
- 研究一下repartitionAndSortWithinPartitions算子
- SQL基本命令
- Android上JAVA Socket通信解析(二)多线程通信
- windows10中的Ubuntu
- 错误总结之no appropriate constructor in class
- Java + FlexPaper 实现 pdf 的预览功能
- jsp 简易用户自动登录(无数据库)
- 通俗易懂的解释numpy中的广播
- 237. Delete Node in a Linked List
- 除了技术重构,你更应该在乎知识体系重构
- JQuery插件DataTables的使用