本节主要介绍Socket编程,发现Java里面的socket编程和C语言的还是有一些不一样,比如TCP socket ,在Java中区分了serverSocket。不过原理都一样,在流程处理上也非常相似,所以,理解起来并不难。我们会先从基础说起,从如何建立socket连接,到如何实现一个合理的设计例如在android中,我们发送一条消息,然后监听一个回复,如何做到不卡死UI,本文将会由浅入深的为大家呈现一个相对完整的android socket编程。
在进入Java 的socket编程之前,我们从原理上切入,然后提及相应的代码说明。
Socket可以说是一种针对网络的抽象,应用通过它可以来针对网络读写数据。就像通过一个文件的file handler就可以都写数据到存储设备上一样。根据TCP协议和UDP协议的不同,在网络编程方面就有面向两个协议的不同socket,一个是面向字节流的一个是面向报文的。
TCP主要是面向连接的协议,它包含有建立和拆除连接,保证数据流的顺序和正确性等功能。每次对TCP中间的数据操作相当于对一个数据流进行访问。它最典型的特征就是那三次握手的建立连接过程。TCP的连接建立和撤销过程如下图:
这个流程的典型示例代码如下:
//1. 构造ServerSocket实例,指定服务端口。 ServerSocket servSock = new ServerSocket(servPort); while(true) { // 2.调用accept方法,建立和客户端的连接 Socket clntSock = servSock.accept(); SocketAddress clientAddress = clntSock.getRemoteSocketAddress(); System.out.println("Handling client at " + clientAddress); // 3. 获取连接的InputStream,OutputStream来进行数据读写 InputStream in = clntSock.getInputStream(); OutputStream out = clntSock.getOutputStream(); while((recvMsgSize = in.read(receiveBuf)) != -1) { out.write(receiveBuf, 0, recvMsgSize); } // 4.操作结束,关闭socket. clntSock.close(); }
try { file://建立服务器 ServerSocket server = new ServerSocket(9998); int i=1; for(;;) { <span style="white-space:pre"> </span>Socket incoming = server.accept(); <span style="white-space:pre"> </span>new ServerThread(incoming,i).start(); //开启线程来处理客户端的请求 <span style="white-space:pre"> </span>i++; } }catch (IOException ex){ ex.printStackTrace(); }
// 1.根据指定的server地址和端口,建立socket连接。 Socket socket = new Socket(server, servPort); // 2. 根据socket实例获取InputStream, OutputStream进行数据读写。 InputStream in = socket.getInputStream(); OutputStream out = socket.getOutputStream(); out.write(data); //3.操作结束,关闭socket. socket.close();
package com.nan.client; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.net.Socket; import java.net.UnknownHostException; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; import android.widget.Toast; public class MyClientActivity extends Activity { private EditText mEditText = null; private Button connectButton = null; private Button sendButton = null; private TextView mTextView = null; private Socket clientSocket = null; private OutputStream outStream = null; private Handler mHandler = null; private ReceiveThread mReceiveThread = null; private boolean stop = true; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); mEditText = (EditText)this.findViewById(R.id.edittext); mTextView = (TextView)this.findViewById(R.id.retextview); connectButton = (Button)this.findViewById(R.id.connectbutton); sendButton = (Button)this.findViewById(R.id.sendbutton); sendButton.setEnabled(false); //连接按钮监听 connectButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub try { //实例化对象并连接到服务器 clientSocket = new Socket("113.114.170.246",8888); } catch (UnknownHostException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } displayToast("连接成功!"); //连接按钮使能 connectButton.setEnabled(false); //发送按钮使能 sendButton.setEnabled(true); mReceiveThread = new ReceiveThread(clientSocket); stop = false; //开启线程 mReceiveThread.start(); } }); //发送数据按钮监听 sendButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub byte[] msgBuffer = null; //获得EditTex的内容 String text = mEditText.getText().toString(); try { //字符编码转换 msgBuffer = text.getBytes("GB2312"); } catch (UnsupportedEncodingException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } try { //获得Socket的输出流 outStream = clientSocket.getOutputStream(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } try { //发送数据 outStream.write(msgBuffer); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } //清空内容 mEditText.setText(""); displayToast("发送成功!"); } }); //消息处理 mHandler = new Handler() { @Override public void handleMessage(Message msg) { //显示接收到的内容 mTextView.setText((msg.obj).toString()); } }; } //显示Toast函数 private void displayToast(String s) { Toast.makeText(this, s, Toast.LENGTH_SHORT).show(); } private class ReceiveThread extends Thread { private InputStream inStream = null; private byte[] buf; private String str = null; ReceiveThread(Socket s) { try { //获得输入流 this.inStream = s.getInputStream(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } @Override public void run() { while(!stop) { this.buf = new byte[512]; try { //读取输入数据(阻塞) this.inStream.read(this.buf); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } //字符编码转换 try { this.str = new String(this.buf, "GB2312").trim(); } catch (UnsupportedEncodingException e) { // TODO Auto-generated catch block e.printStackTrace(); } Message msg = new Message(); msg.obj = this.str; //发送消息 mHandler.sendMessage(msg); } } } @Override public void onDestroy() { super.onDestroy(); if(mReceiveThread != null) { stop = true; mReceiveThread.interrupt(); } } }
package com.nan.server; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.net.ServerSocket; import java.net.Socket; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; import android.widget.Toast; public class MyServerActivity extends Activity { private TextView ipTextView = null; private EditText mEditText = null; private Button sendButton = null; private TextView mTextView = null; private OutputStream outStream = null; private Socket clientSocket = null; private ServerSocket mServerSocket = null; private Handler mHandler = null; private AcceptThread mAcceptThread = null; private ReceiveThread mReceiveThread = null; private boolean stop = true; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); ipTextView = (TextView)this.findViewById(R.id.iptextview); mEditText = (EditText)this.findViewById(R.id.sedittext); sendButton = (Button)this.findViewById(R.id.sendbutton); sendButton.setEnabled(false); mTextView = (TextView)this.findViewById(R.id.textview); //发送数据按钮监听 sendButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub byte[] msgBuffer = null; //获得EditTex的内容 String text = mEditText.getText().toString(); try { //字符编码转换 msgBuffer = text.getBytes("GB2312"); } catch (UnsupportedEncodingException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } try { //获得Socket的输出流 outStream = clientSocket.getOutputStream(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } try { //发送数据 outStream.write(msgBuffer); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } //清空内容 mEditText.setText(""); displayToast("发送成功!"); } }); //消息处理 mHandler = new Handler() { @Override public void handleMessage(Message msg) { switch(msg.what) { case 0: { //显示客户端IP ipTextView.setText((msg.obj).toString()); //使能发送按钮 sendButton.setEnabled(true); break; } case 1: { //显示接收到的数据 mTextView.setText((msg.obj).toString()); break; } } } }; mAcceptThread = new AcceptThread(); //开启监听线程 mAcceptThread.start(); } //显示Toast函数 private void displayToast(String s) { Toast.makeText(this, s, Toast.LENGTH_SHORT).show(); } private class AcceptThread extends Thread { @Override public void run() { try { //实例化ServerSocket对象并设置端口号为8888 mServerSocket = new ServerSocket(8888); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); }
while(1)
{
try { //等待客户端的连接(阻塞) clientSocket = mServerSocket.accept(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } mReceiveThread = new ReceiveThread(clientSocket); stop = false; //开启接收线程 mReceiveThread.start(); Message msg = new Message(); msg.what = 0; //获取客户端IP msg.obj = clientSocket.getInetAddress().getHostAddress(); //发送消息 mHandler.sendMessage(msg); } } } private class ReceiveThread extends Thread { private InputStream mInputStream = null; private byte[] buf ; private String str = null; ReceiveThread(Socket s) { try { //获得输入流 this.mInputStream = s.getInputStream(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } @Override public void run() { while(!stop) { this.buf = new byte[512]; //读取输入的数据(阻塞读) try { this.mInputStream.read(buf); } catch (IOException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } //字符编码转换 try { this.str = new String(this.buf, "GB2312").trim(); } catch (UnsupportedEncodingException e) { // TODO Auto-generated catch block e.printStackTrace(); } Message msg = new Message(); msg.what = 1; msg.obj = this.str; //发送消息 mHandler.sendMessage(msg); } } } @Override public void onDestroy() { super.onDestroy(); if(mReceiveThread != null) { stop = true; mReceiveThread.interrupt(); } } }
UDP和TCP有两个典型的区别,一个就是它不需要建立连接,另外就是它在每次收发的报文都保留了消息的边界。
// 1. 构建DatagramSocket实例,指定本地端口。 DatagramSocket socket = new DatagramSocket(servPort); // 2. 构建需要收发的DatagramPacket报文 DatagramPacket packet = new DatagramPacket(new byte[ECHOMAX], ECHOMAX); while(true) { // 3. 收报文 socket.receive(packet); System.out.println("Handling client at " + packet.getAddress().getHostAddress() + " on port " + packet.getPort()); // 4. 发报文 socket.send(packet); packet.setLength(ECHOMAX); }
// 1. 构造UDP DatagramSocket对象 DatagramSocket socket = new DatagramSocket(); // 2。指定timeout时间,防止进入无限等待状态 socket.setSoTimeout(TIMEOUT); // 3. 构造收发的报文对象 DatagramPacket sendPacket = new DatagramPacket(bytesToSend, bytesToSend.length, serverAddress, servPort); DatagramPacket receivePacket = new DatagramPacket(new byte[bytesToSend.length], bytesToSend.length); // 4.指定尝试的次数 int tries = 0; boolean receivedResponse = false; do { socket.send(sendPacket); try { socket.receive(receivePacket); if(!receivePacket.getAddress().equals(serverAddress)) { throw new IOException("Received packet from an unknown source"); } receivedResponse = true; } catch(InterruptedIOException e) { tries += 1; System.out.println("Timed out, " + (MAXTRIES - tries) + ""); } }while((!receivedResponse) && (tries < MAXTRIES)); // 根据是否接收到报文进行反馈 if(receivedResponse) { System.out.println("Received: " + new String(receivePacket.getData())); } else { System.out.println("No response -- giving up."); } // 5. 关闭socket socket.close();
Reference:
http://shmilyaw-hotmail-com.iteye.com/blog/1556187
http://www.cnblogs.com/lknlfy/archive/2012/03/04/2379628.html