您的位置:首页 > 移动开发 > Android开发

编程回忆之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 Timeout
Accept 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
 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息