Socket通信(Android)

最近一个Android项目中用到了Socket编程,主要是实现UDP通信。这里,用一个demo示例Socket编程过程。 ## 一、什么是Socket?

日常生活中,浏览器的进程与web服务器通信、QQ进程与服务器或你好友所在的QQ进程通信,这些都得靠socket。Socket又称"套接字",应用程序通常通过"套接字"向网络发出请求或者应答网络请求。具体关于Socket,网上有很多相关的内容,可自行查找学习。

二、Socket编程流程

可以把Socket看作一种特殊的文件,如同操作文件一样我们去操作Socket。过程大致是:open—write/read—close。

三、Android中使用Socket编程实现TCP、UDP通信

因为TCP是可靠连接,而UDP属于不可靠连接,所以从速度上将UDP要快于TCP,但是TCP较UDP更稳定可靠。具体TCP、UDP的实现过程以及区别,也有很多可查阅的资料。

在Android中,Socket类位于java.net.Socket包中,UDP通信所需要使用的两个类DatagramSocket、DatagramPacket分别位于java.net.DatagramSocket和java.net.DatagramPacket包中。接下来,我们编写服务端和客户端程序来体验Android中的Socket编程。

TCP/UDP通信需要声明INTERNET权限,代码如下:

<uses-permission android:name="android.permission.INTERNET" />

客户端开启分别开启两个线程(TCP、UDP)用于与服务端不间断通信,服务端同样开启两个线程(绑定不同的端口号)用于接收客户端(TCP、UDP)发送的数据并返回相应的数据。步骤(以TCP为例)分为以下几步:

Android中TCP通信主要使用InputStream和OutputStream实现。

客户端: 1、 创建客户端套接字(指定服务器端IP地址与端口号) 2、 连接(Android 创建Socket时会自动连接) 3、 与服务器端进行通信 4、 关闭套接字

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
try {
char[] buffer = new char[24];
Socket mSocket = new Socket("10.0.2.12", 8891);//创建Socket示例,系统会自动连接
BufferedWriter mBufferedWriter = new BufferedWriter(new OutputStreamWriter(mSocket.getOutputStream()));
mBufferedWriter.write("TCP call request " + (++call_no) + "_");//向服务端发送数据
mBufferedWriter.flush();
BufferedReader mBufferedReader = new BufferedReader(new InputStreamReader(mSocket.getInputStream()));
String tmpString = null;
if(mBufferedReader.read(buffer) &gt; 0){//接收来自服务端的数据
tmpString = new String(buffer);
}
Message msg = mHandler.obtainMessage();
msg.what = 0;
msg.obj = tmpString;
mHandler.sendMessage(msg);
mBufferedReader.close();
mBufferedWriter.close();
mSocket.close();//关闭
Thread.sleep(1000);
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}).start();

服务器端: 1、 创建服务器端套接字并绑定到一个端口上 2、 套接字设置监听模式等待连接请求(阻塞) 3、 接受连接请求后进行通信 4、 返回

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
new Thread(new Runnable() {//监听TCP报文
public void run() {
try {
ServerSocket mServerSocket = new ServerSocket(8891);//绑定8891端口
while(true){
char[] buffer = new char[1024];
Socket client = mServerSocket.accept();//阻塞直至收到报文
BufferedReader mBufferedReader = new BufferedReader(new InputStreamReader(client.getInputStream()));
if(mBufferedReader.read(buffer) &gt; 0){//使用readLine()会阻塞,并保证数据的正确性
System.out.println(new String(buffer));
}
// System.out.println(mBufferedReader.readLine());//
BufferedWriter mBufferedWriter = new BufferedWriter(new OutputStreamWriter(client.getOutputStream()));
mBufferedWriter.write("(TCP)server return " + (++call_no) + "_");
mBufferedWriter.flush();
mBufferedReader.close();
mBufferedWriter.close();
client.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();

UDP通信是把数据打包成数据包发送的。主要使用DatagramSocket和DatagramSocket对象来实现,DatagramSocket是使用UDP方式通信时的Socket类,DatagramPacket是用来将数据打包的类,打包完成后由DatagramSocket对象发送数据包到指定的主机的端口上。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
DatagramSocket mDatagramSocket = null;
DatagramPacket mDatagramPacket = null;
String sendData = null;
String receiveData = null;
try {
mDatagramSocket = new DatagramSocket(8893);
while(true){
sendData = "UDP call request " + (++udp_call_no) + "_";
byte[] receiveMsg = new byte[1024];// 接收的字节大小
mDatagramPacket = new DatagramPacket(sendData.getBytes(), sendData == null ? 0 : sendData.length(), InetAddress.getByName("10.0.2.12"), 8892);
mDatagramSocket.send(mDatagramPacket);//发送
mDatagramPacket = new DatagramPacket(receiveMsg, receiveMsg.length);//阻塞直至收到返回报文
mDatagramSocket.receive(mDatagramPacket);//接收
receiveData = new String(mDatagramPacket.getData(), mDatagramPacket.getOffset(), mDatagramPacket.getLength());
Message msg = mHandler.obtainMessage();
msg.what = 1;
msg.obj = receiveData;
mHandler.sendMessage(msg);
// mDatagramSocket.close();
Thread.sleep(1000);
}
} catch (SocketException e) {
e.printStackTrace();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}).start();

服务端通过DatagramSocket类的receive(DatagramPacket packet)方法接收发送过来的数据包,运行到这个方法时,会发生阻塞,直到接收到数据包。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
new Thread(new Runnable() {//监听UDP报文
public void run() {
DatagramSocket mDatagramSocket = null;
DatagramPacket mDatagramPacket = null;
String receiveData = null;
String sendData = null;
try {
mDatagramSocket = new DatagramSocket(8892);
while(true){
sendData = "(UDP)server return " + (++udp_call_no) + "_";
byte[] receiveMsg = new byte[1024];// 接收的字节大小
mDatagramPacket = new DatagramPacket(receiveMsg, receiveMsg.length);
mDatagramSocket.receive(mDatagramPacket);////阻塞直至收到报文
receiveData = new String(mDatagramPacket.getData(), mDatagramPacket.getOffset(), mDatagramPacket.getLength());
System.out.println(receiveData);
mDatagramPacket = new DatagramPacket(sendData.getBytes(), sendData == null ? 0 : sendData.length(), InetAddress.getByName("10.0.2.16"), 8893);
mDatagramSocket.send(mDatagramPacket);//发送
// mDatagramSocket.close();
}
} catch (SocketException e) {
e.printStackTrace();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();

四、完整代码

https://github.com/whilu/AndroidUtils/tree/master/SocketDemo