Iwfu-安卓网络应用---Socket
2016-04-03 23:52
369 查看
Socket
socket套接字,和java网络编程部分类似,通过socket可以实现客户端与服务器的链接(c/s),使用ServerSocket可以创建TCP服务器端,与客户端建立三次握手连接。
通过一个例子熟悉:
简单的c/s聊天室
服务器端
(使用MyEclipse编写的):public static void main(String args[]){
try { ServerSocket serverSocket = new ServerSocket(SERVER_PORT); System.out.println("启动了服务器"); while(true){ Socket socket = serverSocket.accept(); socketList.add(socket); System.out.println("成功连接客户端"); //为每一个客户端socket单独开启一个线程通信 new Thread(new MyServerThread(socket)).start(); } } catch (IOException e) { e.printStackTrace(); } }
主要通过 new ServerSocket(port)启动服务器Socket,再调用accepet(),接收来自客户端的链接请求,因为是聊天室,一个客户端发送信息要同时让其他客户端收到,所以给每个客户端socket开启一个线程用于发送和读取消息。
客户端自定义线程类,用于处理服务器和客户端的交互数据:
@Override public void run() { String content = null; //采用循环不断的从Socket中读取客户端发送过来的数据 while ((content = readFromClient()) != null) { for (Socket socket : MyServer.socketList) { //遍历每个Socket,把读到的内容向每一个Socket发送一遍 try { OutputStream outputStream = socket.getOutputStream(); outputStream.write((content+"\n").getBytes("utf-8")); } catch (IOException e) { e.printStackTrace(); } } } }
读数据的方法:
/* * 从客户端读取数据到服务器 */ private String readFromClient() { String content = null; try { content = bufferedReader.readLine(); return content; } catch (IOException e) { e.printStackTrace(); // 捕捉到异常,说明该Socket对应的客户端已关闭 MyServer.socketList.remove(socket); } return null; }
然后运行,服务器端就准备好了 : )
客户端(用AndroidStudio编写):
客户端界面:简单聊天界面,一个EditText,一个Button发送信息,一个TextView显示从服务器读取的信息. 重点在于如何与服务器进行连接和从子线程修改UI线程(主线程)组件。
关键代码(MainActivity):
public static String URL_PATH = "192.168.2.1"; //访问服务器地址 public static int SERVER_PORT = 50000; //服务器端口号 public static int TIMEOUT = 5000; //超时限定 public static final int MESSAGE_APPENDTEXT = 0x111; public static final int MESSAGE_EDITTEXT = 0x111;
private Handler myHandler = new Handler () { @Override public void handleMessage (Message msg) { super.handleMessage (msg); if (msg.what == MESSAGE_APPENDTEXT) { textView.append (msg.obj.toString () + "\n"); } else if (msg.what == 0x222) { Toast.makeText (MainActivity.this, "连接服务器失败", Toast.LENGTH_SHORT).show (); } } };
private void initViews () { textView = (TextView) findViewById (R.id.textView); button = (Button) findViewById (R.id.button); editText = (EditText) findViewById (R.id.editText); button.setOnClickListener (new View.OnClickListener () { @Override public void onClick (View v) { Message message = new Message (); message.what = MainActivity.MESSAGE_EDITTEXT; message.obj = editText.getText ().toString (); myClientThread.revHandler.sendMessage (message); editText.setText (""); } }); }
@Override protected void onCreate (Bundle savedInstanceState) { super.onCreate (savedInstanceState); setContentView (R.layout.activity_main); initViews (); myClientThread = new MyClientThread (myHandler); new Thread (myClientThread).start (); }
客户端子线程的实现类MyClientThread :
public class MyClientThread implements Runnable{ private Handler handler; //向ui发送消息的Handler对象 public Handler revHandler; //接受ui发来的消息的Handler对象 private BufferedReader bufferedReader; private OutputStream outputStream; public MyClientThread(Handler handler){ this.handler = handler; } @Override public void run () { try { //建立Socket连接服务器 Socket socket = new Socket (MainActivity.URL_PATH,MainActivity.SERVER_PORT); socket.setSoTimeout (MainActivity.TIMEOUT); bufferedReader = new BufferedReader (new InputStreamReader (socket.getInputStream ())); outputStream = socket.getOutputStream (); //启动一条子线程来读取服务器的数据 new Thread (new Runnable () { String content; @Override public void run () { try { while((content = bufferedReader.readLine ())!=null){ Message message = new Message (); message.what = MainActivity.MESSAGE_APPENDTEXT; message.obj = content; handler.sendMessage(message); } } catch (IOException e) { e.printStackTrace (); } } }).start (); Looper.prepare (); revHandler = new Handler (){ @Override public void handleMessage (Message msg) { //接受用户输入的数据 if (msg.what==MainActivity.MESSAGE_EDITTEXT){ try { outputStream.write ((msg.obj.toString ()+"\r\n").getBytes ("utf-8")); } catch (IOException e) { e.printStackTrace (); } } } }; Looper.loop (); }catch (SocketTimeoutException e){ Log.e ("TimeOut", "连接服务器超时"); } catch (SocketException e) { e.printStackTrace (); } catch (UnknownHostException e) { e.printStackTrace (); } catch (IOException e) { e.printStackTrace (); } } }
为什么要在子线程中还开一条子线程用于读取服务器数据呢?我想是因为怕子线程发送数据给服务器的同时,其他客户端向服务器也发送了数据,对于我自己这个客户端就要从服务器接收数据(就是聊天室别人发信息我同时也要从服务器接收到信息),收发同时为了避免线程拥塞,所以在子线程中再开一个子线程单独用于发送数据给服务器。
注意的地方:
网络通信在安卓中一般放在子线程中进行,主线程一般用于UI操作。
安卓网络通信加权限: <uses-permission android:name="android.permission.INTERNET"/>
截图:
启动服务器:
客户端:
完结撒花,如有不足,望批评指正。
附:源码下载-
Rose.Chan—Socket简易聊天室
相关文章推荐
- Linux下的网络环境配置
- TCP/IP协议:最大传输单元MTU 路径MTU
- Http状态码
- 自考那些事儿(六):计算机网络原理(总述篇)
- 【HTTP 2】启用 HTTP 2(Starting HTTP/2)
- 【HTTP 2】启用 HTTP 2(Starting HTTP/2)
- 【HTTP 2】启用 HTTP 2(Starting HTTP/2)
- 【HTTP 2】简介(Introduction)
- 【HTTP 2】简介(Introduction)
- 【HTTP 2】简介(Introduction)
- 【HTTP 2】 序言
- 【HTTP 2】 序言
- 【HTTP 2】 序言
- 【HTTP 2】HTTP/2 协议概述(HTTP/2 Protocol Overview)
- 【HTTP 2】HTTP/2 协议概述(HTTP/2 Protocol Overview)
- 大师带你了解TCP基本功之滑动窗口(2)
- 大师带你了解TCP基本功之滑动窗口(1)
- 深入理解HTTP协议、HTTP协议原理分析
- TCP客户/服务器程序概述
- 为什么UDP有时比TCP更有优势