编程回忆之Android回忆(Android Socket编程)
2014-03-17 11:21
351 查看
Android Socket编程
一、什么是Socket
什么是socket呢?socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,都可以用“打开open –> 读写write/read –> 关闭close”模式来操作。我的理解就是Socket就是该模式的一个实现,socket即是一种特殊的文件,一些socket函数就是对其进行的操作(读/写IO、打开、关闭)。二、Socket的连接过程
根据连接启动的方式以及本地Socket要连接的目标,Socket之间的连接过程可以分为三个步骤:服务器监听,客户端请求,连接确认。1、服务器监听:是服务器端Socket并不定位具体的客户端Socket,而是处于等待连接的状态,实时监控网络状态。
2、客户端请求:是指由客户端的Socket提出连接请求,要连接的目标是服务器端的Socket。为此,客户端的Socket必须首先描述它要连接的服务器的Socket,指出服务器端Socket的地址和端口号,然后就向服务器端Socket提出连接请求。
3、连接确认:是指当服务器端Socket监听到或者说接收到客户端Socket的连接请求,它就响应客户端Socket的请求,建立一个新的线程,把服务器端套接字的描述发给客户端,一旦客户端确认了此描述,连接就建立好了。而服务器端Socket继续处于监听状态,继续接收其他客户端Socket的连接请求。
三、Android socket的基础知识
1.1 Accept TimeoutAccept timeout 仅对ServerSocket有用。ServerSocket 使用accept()方法来监听客户端Socket的连接。默认,ServerSocket.accept() 方法会一直阻塞直到有客户端来连接。通常,我们不需要设置accept timeout.
但有时候特殊情况,还是要考虑设置accept timeout.
比如: 程序A给程序B发了一个JMS消息,然后程序A启动一个Socket Server,想通过socket等待接收程序B的返回消息。如果不设置accept timeout, 并且程序B因为某些原因一直不能连接Socket Server,最终会导致程序A挂起。
Accept Timeout可以这样设置:
ServerSocket serverSocket = new ServerSocket(5555);
serverSocket.setSoTimeout(5000); // in milliseconds
while (true) {
Socket socket = serverSocket.accept();
…
}
1.2 Connect Timeout
当Client端连接Server端的时候,可以指定Connect Timeout
如果没有指定,会使用操作系统的默认值:
OS | Default TCP timeout |
BSD | 75 seconds |
Linux | 189 seconds |
Solaris | 225 seconds |
Windows XP | 21 seconds |
Connect Timeout可以这样设置:
SocketAddress socketAddress = new InetSocketAddress(host, port);
socket = new Socket();
socket.connect(socketAddress, connectTimeout);
1.3 Receive Timeout
当socket从另一方接收数据时,可以设置Receive Timeout
默认没有timeout,socket会一直阻塞直到有数据可读取。
Receive Timeout可以这样设置:
Socket socket = new Socket(host, port);
socket.setSoTimeout(timeout);
1.4 Send Timeout
Send Timeout是socket给另一方发送数据时使用的。
不过Java里没有办法设置Send Timeout.
当然,socket发送数据的时候,会首先发送到本机OS的一个buffer内。一般只要一次发送的数据不是很大,即使对方挂起或暂时不能接收数据,也不会导致发送方挂起。
2.1 Socket ack (acknowledgement)
Socket ack是指当socket接收到数据之后,发送一个ack字符串(比如$ACK)给socket发送方。这样,socket发送方可以根据是否收到了ack判断对方是否收到了数据。
Socket ack是显示的在应用程序中加入的一种通讯协议。如果不使用ack,在socket通讯中,可能会丢失数据。
比如,socket client要连续的给socket server发送100条消息。如果我们在server收到第50条消息的时候,强行kill了server。那么查询client端发送的log,可能client端成功发送了51条。只有当client端发送第52条消息的时候才遇到异常。这样第51条消息就丢失了。
所以为了确保数据传输的准确性,我们可以引入ack协议。有时我们不仅要确保server不但收到了数据,而且还要保证server成功处理了数据。这时,可以等server成功处理完数据之后,再给client发ack。
2.2 Socket Keep Alive
Socket连接像数据库连接一样,属于重量型资源。如果我们频繁的创建socket、发送/接收数据、关闭socket,那么会有很大一部分时间浪费在socket的创建和关闭上。
所以,如果我们经常需要与同一个socket地址发送/接收数据时,应该考虑只创建一次socket,然后一直使用这个socket对象发送/接收数据。
2.3 Heartbeat
通常,我们会设置socket的receive timeout。这样,如果我们一直打开着socket (keep alive), 而很长时间又没有数据通讯,socket接收方就会timeout,最终导致打开的连接坏掉。
如果很长时间没有数据通讯,防火墙或代理服务器也可能会关闭打开的socket连接。
所以为了保证打开的socket连接一直可用,如果一段时间没有数据进行通讯(或指定一个时间间隔),我们可以显示的发送一个heartbeat消息(比如: $HRT)给对方,从而保证连接不会被异常关闭。
2.4 Socket Close
每一个socket对象会持有一个socket descriptor (其实就是file descriptor),操作系统对于socket descriptor有一个最大限制。因此当socket不再使用时,一定要记得关闭,即使socket连接失败或出现异常,只要socket对象不为null,一定要记得关闭。
四、socket服务端的编写
服务器端编程步骤:1: 创建服务器端套接字并绑定到一个端口上(0-1023是系统预留的,最好大约1024以上)
2: 套接字设置监听模式等待连接请求
3: 接受连接请求后进行通信
4: 返回,等待下一个连接请求
import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class SckServer { private static final int PORT = 9999;// 端口 private List<Socket> mList = new ArrayList<Socket>(); private ServerSocket server = null; private ExecutorService mExecutorService = null; // thread pool public static void main(String[] args) { new SckServer(); } public SckServer() { try { // 创建服务器端socket并绑定到一个端口上 server = new ServerSocket(PORT); // 使用连接池 mExecutorService = Executors.newCachedThreadPool(); System.out.print("server start ..."); Socket client = null; // 接字设置监听模式等待连接请求 while (true) { client = server.accept(); mList.add(client); // 接受连接请求后进行通信 mExecutorService.execute(new Service(client)); } } catch (Exception e) { e.printStackTrace(); } } class Service implements Runnable { private Socket socket; private BufferedReader in = null; private String msg = ""; public Service(Socket socket) { this.socket = socket; try { in = new BufferedReader(new InputStreamReader( socket.getInputStream())); msg = "user" + this.socket.getInetAddress() + "come toal:" + mList.size(); this.sendmsg(); } catch (IOException e) { e.printStackTrace(); } } @Override public void run() { // TODO Auto-generated method stub try { while (true) { if ((msg = in.readLine()) != null) { if (msg.equals("exit")) { System.out.println("ssssssss"); mList.remove(socket); in.close(); msg = "user:" + socket.getInetAddress() + "exit total:" + mList.size(); socket.close(); this.sendmsg(); break; } else { msg = socket.getInetAddress() + ":" + msg; this.sendmsg(); } } } } catch (Exception e) { e.printStackTrace(); } } public void sendmsg() { System.out.println(msg); int num = mList.size(); for (int index = 0; index < num; index++) { Socket mSocket = mList.get(index); PrintWriter pout = null; try { pout = new PrintWriter(new BufferedWriter( new OutputStreamWriter(mSocket.getOutputStream())), true); pout.println(msg); } catch (IOException e) { e.printStackTrace(); } } } } }
五、Android客户端的编写
客户端编程步骤: 1: 创建客户端套接字(指定服务器端IP地址与端口号)
2: 连接(Android 创建Sockett时会自动连接)
3: 与服务器端进行通信
4: 关闭套接字
package com.lyh.sck; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.net.Socket; import android.app.Activity; import android.app.AlertDialog; import android.content.DialogInterface; 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; public class MainActivity extends Activity { private TextView tv_msg = null; private EditText ed_msg = null; private Button btn_send = null; private static final String HOST = "192.168.3.121"; private static final int PORT = 9999; private Socket socket = null; private BufferedReader in = null; private PrintWriter out = null; private String content = ""; public Handler scHandler; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initData(); findViews(); initSocket(); } /** * 初始化控件 * */ private void findViews() { tv_msg = (TextView) findViewById(R.id.TextView); ed_msg = (EditText) findViewById(R.id.EditText01); btn_send = (Button) findViewById(R.id.Button02); } /** * 初始化数据 * */ private void initData() { scHandler = new Handler() { @Override public void handleMessage(Message msg) { // TODO Auto-generated method stub super.handleMessage(msg); if (msg.what == 0) { btn_send.setOnClickListener(new Button.OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub String msg = ed_msg.getText().toString(); if (socket.isConnected()) { if (!socket.isOutputShutdown()) { out.println(msg); } } } }); new Thread(runnable).start(); } else if (msg.what == 1) { tv_msg.setText(tv_msg.getText().toString() + content); } else if (msg.what == 2) { } } }; } /** * 初始化Socket * */ public void initSocket() { new Thread() { @Override public void run() { // TODO Auto-generated method stub try { socket = new Socket(HOST, PORT);//初始化socket对象 in = new BufferedReader(new InputStreamReader( socket.getInputStream())); out = new PrintWriter(new BufferedWriter( new OutputStreamWriter(socket.getOutputStream())), true); //初始化玩socket,将结果告知scHandler Message msg=scHandler.obtainMessage(); msg.what=0; scHandler.sendMessage(msg); } catch (IOException ex) { ex.printStackTrace(); ShowDialog("login exception" + ex.getMessage()); } } }.start(); } /*** * 获取数据线程 * */ public Runnable runnable = new Runnable() { @Override public void run() { // TODO Auto-generated method stub try { while (true) { if (socket.isConnected()) { if (!socket.isInputShutdown()) { if ((content = in.readLine()) != null) { content += "\n"; Message msg=scHandler.obtainMessage(); msg.what=1; scHandler.sendMessage(msg); } else { } } } } } catch (Exception e) { e.printStackTrace(); } } }; public void ShowDialog(String msg) { new AlertDialog.Builder(this).setTitle("notification").setMessage(msg) .setPositiveButton("ok", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { // TODO Auto-generated method stub } }).show(); } }
备注:Android网络编程的时候,切记不能将耗时的上传下载线程放在UI线程中。
源码下载地址:http://download.csdn.net/detail/stop_pig/7052629
相关文章推荐
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- 解決Linux下Android开发真机调试设备不被识别问题
- [Android]在代码里运行另一个程序的方法
- [软件咨询]WPS2012正式版已发布 金山Office移动版4.0发布
- Android笔记-Linux Kernel Ftrace (Function Trace)解析
- 拨号网络的简单知识
- 菜鸟必看网络名词
- 通晓网络测试常用命令
- 网络路由技术及运用2
- IP网络路由技术
- 网络防火墙的设置技巧
- 网络管理之IP地址篇
- 网络路由技术及运用3
- 网络路由技术及运用4
- 网络路由技术及运用1
- 无线局域网络简介一
- 无线局域网络简介二