今天来学习一下android中通信方式中的socket。上两次我们分别使用了HttpURLConnection , 和 HttpClient来实现通信,他们都是在使用HTTP协议,Socket被称为套接字,使用的协议有TCP和UDP,TCP和UDP的区别在于TCP是可靠稳定的,自带容错处理等优点,所以效率要低一点。然后UDP就不那么稳定了,当使用UDP发送数据的时候,每次send,那么socket只管send,不会管send之后对方是否收到,是否顺序正确,所以UDP被称为不稳定的传输,但是其效率却很高,因为做的事情很简单。
今天我们学习socket是使用tcp协议,在android中使用socket其实就是使用java中的socket,看应用的包 java.net.*; 就知道。
我们来完成一个小的局域网聊天的程序,包含一个服务器,android应用作为客户端,一台手机发送信息后,服务器会转发给其他手机,很简单。代码比较多,后面附上下载地址,我们先来看怎么使用socket。
客户端:
客户端Socket使用步骤:
(1)Socket mSocket = new Socket( SERVER_IP, SERVER_PORT); // 两个参数分别为服务器ip地址,和服务器监听的端口号
(2)BufferedReader mBufferReader = new BufferedReader(new InputStreamReader( mSocket.getInputStream())); // 通过socket.getInputStream() 可以得到输入流,可从socket获取服务器发送的数据。为了能一行一行读取,我们将其转化为 BufferReader,可以使用BufferReader.readLine() 一行一行得去取等待在socket上的数据。
(3)PrintWriter mPrintWriter = new PrintWriter(mSocket.getOutputStream(), true); // 通过socket.getOutputStream() 可以得到输入流,就可以通过socket向服务器发送数据, 这里需要套上 PrintWriter 标准的接口,就可以使用 mPrintWriter.println(“ ..." ),直接发送数据。
(4)使用完后断开连接。
我们来看一看实例:
mSocket = new Socket(SERVER_IP, SERVER_POART); mBufferReader = new BufferedReader(new InputStreamReader( mSocket.getInputStream())); mPrintWriter = new PrintWriter(mSocket.getOutputStream(), true);
如果没有报 Exception那就表示链接服务器已经成功,这时就可以使用 mBufferReader和 mPrintWrinter 来实现数据的收发。
读取数据:
while (true) { if (null != (mStrMsg = mBufferReader.readLine())) { mStrMsg += "\n"; if(mStrMsg.trim().equals("exit")){ mHandler.sendMessage(mHandler.obtainMessage(2)); return ; } mHandler.sendMessage(mHandler.obtainMessage(0)); } }
发送数据:
private View.OnClickListener sendListener = new View.OnClickListener() { @Override public void onClick(View v) { String str = mEtMessage.getText().toString().trim(); if(mPrintWriter == null){ showToast("请先连接服务器!"); return ; } mPrintWriter.println(str); mPrintWriter.flush(); mEtMessage.setText(""); } };
断开连接:
showToast("断开连接"); try { mBufferReader.close(); mPrintWriter.close(); mSocket.close(); mSocket = null; if (mGetMessageThread != null) { mGetMessageThread = null; } } catch (IOException e) { e.printStackTrace(); }
服务端:
在服务端我们需要有一个ServerSocket,这是用来接收连接请求的,当每接收到一个一个请求后就会返回一个Socket套接字,就可以使用这个套接字与客户端通信。注意:服务器端应该是一直运行,且可以接收很多连接的程序,服务器程序一定不能被关闭或者崩掉,所以在设计服务器的时候要考虑很多很多事情,我们这里只是简单的处理收到客户端请求连接,然后将其加入到list当中,如果收到消息,就将收到的消息转发给列表中其他所有socket,很简单的处理。
使用步骤:
(1)ServerSocket mServerSocket = new ServerSocket(SERVERPORT); // 创建监听连接ServerSocket
(2)不停监听 mServerSocket, 使用accept() 接收连接,如果侦测到连接,就启动一个线程,将返回值socket传入,那么每个线程就对应一个客户端,可以与客户端通信了。
(3)接下来就是 每个线程如果收到数据,就需要将消息转发给 客户单列表中所有其他用户。
(4)客户端断开链接后,清除客户socket信息。
启动监听连接,并启动一个线程池,当监听到连接请求时创建启动线程,并放入到线程池中运行:
try { mServerSocket = new ServerSocket(SERVERPORT); mExecutorService = Executors.newCachedThreadPool(); System.out.println("Server Start..."); Socket client = null; while (true) { client = mServerSocket.accept(); mClientList.add(client); mExecutorService.execute(new ThreadServer(client)); } } catch (IOException e) { e.printStackTrace(); }
线程中接收消息,以及转发消息:
private class ThreadServer implements Runnable { private Socket mClient; private BufferedReader mBufferedReader; private PrintWriter mPrintWriter; private String mStrMsg; public ThreadServer(Socket client) throws IOException { this.mClient = client; mBufferedReader = new BufferedReader(new InputStreamReader( mClient.getInputStream())); mStrMsg = "usr:" + this.mClient.getInetAddress() + " connected" + ",total:" + mClientList.size(); sendMessage(mStrMsg); } private void sendMessage(String msg) throws IOException { System.out.println(msg); for (Socket client : mClientList) { mPrintWriter = new PrintWriter(client.getOutputStream(), true); mPrintWriter.println(msg); } } @Override public void run() { try { while (null != (mStrMsg = mBufferedReader.readLine())) { if (mStrMsg.trim().equals("exit")) { mStrMsg = "user:" + mClient.getInetAddress() + " exit!" + "total:" + (mClientList.size() - 1); sendMessage(mStrMsg); mPrintWriter = new PrintWriter( mClient.getOutputStream(), true); mPrintWriter.println("exit"); mBufferedReader.close(); mPrintWriter.close(); mClientList.remove(mClient); mClient.close(); break; } else { mStrMsg = mClient.getInetAddress() + ":" + mStrMsg; sendMessage(mStrMsg); } } } catch (SocketException e) { // e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }
如果没有网络基础,这个理解起来还是有点复杂的,下面贴了项目代码,大家自己去体会一下吧,使用socket其实很简单,主要是逻辑处理。
这里有几点需要注意的:
(1)android2.3之后不能再UI主线程中调用网络请求,会直接崩溃,所以需要使用handler来发送消息处理
(2)实例代码放在你的机子上不一定能使用,SERVER_IP为计算机在局域网的本机地址,需要自己修改。 android客户端需要连上计算机,这样客户端就能找到服务器了。
代码下载地址:
http://download.csdn.net/detail/u013647382/8273861