基于BIO的Java Socket通信详解
|
BIO,即阻塞IO,在基于Socket的消息通信过程中,Socket服务端向外部提供服务,而Socket客户端可以建立到Socket服务端的连接,进而发送请求数据,然后等待Socket服务端处理,并返回处理结果(响应)。 BIO通信实现 下面基于BIO模式,来实现一个简单的Socket服务端与Socket客户端进行通信的逻辑,对这种通信方式有一个感性的认识。具体逻辑描述如下: Socket服务端实现,代码如下所示:
package org.shirdrn.java.communications.bio;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
/**
* 基于BIO的Socket服务器端
*
* @author shirdrn
*/
public class SimpleBioTcpServer extends Thread {
/** 服务端口号 */
private int port = 8888;
/** 为客户端分配编号 */
private static int sequence = 0;
public SimpleBioTcpServer(int port) {
this.port = port;
}
@Override
public void run() {
Socket socket = null;
try {
ServerSocket serverSocket = new ServerSocket(this.port);
while(true) {
socket = serverSocket.accept(); // 监听
this.handleMessage(socket); // 处理一个连接过来的客户端请求
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 处理一个客户端socket连接
* @param socket 客户端socket
* @throws IOException
*/
private void handleMessage(Socket socket) throws IOException {
InputStream in = socket.getInputStream(); // 流:客户端->服务端(读)
OutputStream out = socket.getOutputStream(); // 流:服务端->客户端(写)
int receiveBytes;
byte[] receiveBuffer = new byte[128];
String clientMessage = "";
if((receiveBytes=in.read(receiveBuffer))!=-1) {
clientMessage = new String(receiveBuffer,receiveBytes);
if(clientMessage.startsWith("I am the client")) {
String serverResponseWords =
"I am the server,and you are the " + (++sequence) + "th client.";
out.write(serverResponseWords.getBytes());
}
}
out.flush();
System.out.println("Server: receives clientMessage->" + clientMessage);
}
public static void main(String[] args) {
SimpleBioTcpServer server = new SimpleBioTcpServer(1983);
server.start();
}
}
上述实现,没有进行复杂的异常处理。 Socket客户端实现,代码如下所示:
package org.shirdrn.java.communications.bio;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Date;
/**
* 基于BIO的Socket客户端
*
* @author shirdrn
*/
public class SimpleBioTcpClient {
private String ipAddress;
private int port;
private static int pos = 0;
public SimpleBioTcpClient() {}
public SimpleBioTcpClient(String ipAddress,int port) {
this.ipAddress = ipAddress;
this.port = port;
}
/**
* 连接Socket服务端,并模拟发送请求数据
* @param data 请求数据
*/
public void send(byte[] data) {
Socket socket = null;
OutputStream out = null;
InputStream in = null;
try {
socket = new Socket(this.ipAddress,this.port); // 连接
// 发送请求
out = socket.getOutputStream();
out.write(data);
out.flush();
// 接收响应
in = socket.getInputStream();
int totalBytes = 0;
int receiveBytes = 0;
byte[] receiveBuffer = new byte[128];
if((receiveBytes=in.read(receiveBuffer))!=-1) {
totalBytes += receiveBytes;
}
String serverMessage = new String(receiveBuffer,receiveBytes);
System.out.println("Client: receives serverMessage->" + serverMessage);
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
// 发送请求并接收到响应,通信完成,关闭连接
out.close();
in.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
int n = 1;
StringBuffer data = new StringBuffer();
Date start = new Date();
for(int i=0; i<n; i++) {
data.delete(0,data.length());
data.append("I am the client ").append(++pos).append(".");
SimpleBioTcpClient client = new SimpleBioTcpClient("localhost",1983);
client.send(data.toString().getBytes());
}
Date end = new Date();
long cost = end.getTime() - start.getTime();
System.out.println(n + " requests cost " + cost + " ms.");
}
}
首先启动Socket服务端进程SimpleBioTcpServer,然后再运行Socket客户端SimpleBioTcpClient。可以看到,服务端接收到请求数据,然后响应客户端,客户端接收到了服务端的响应数据。 上述实现中,对于Socket客户端和服务端都是一次写入,并一次读出,而在实际中如果每次通信过程中数据量特别大的话,服务器端是不可能接受的,可以在确定客户端请求数据字节数的情况,循环来读取并进行处理。 另外,对于上述实现中流没有进行装饰(Wrapped)处理,在实际中会有性能的损失,如不能缓冲等。 对于Socket服务端接收数据,如果可以使多次循环读取到的字节数据通过一个可变长的字节缓冲区来存储,就能方便多了,可是使用ByteArrayOutputStream,例如: ByteArrayOutputStream data = new ByteArrayOutputStream(); data.write(receiveBuffer,totalBytes,totalBytes + receiveBytes); BIO通信测试 下面测试一下大量请求的场景下,Socket服务端处理的效率。 第一种方式:通过for循环来启动5000个Socket客户端,发送请求,代码如下所示:
public static void main(String[] args) {
int n = 5000;
StringBuffer data = new StringBuffer();
Date start = new Date();
for(int i=0; i<n; i++) {
data.delete(0,data.length());
data.append("I am the client ").append(++pos).append(".");
SimpleBioTcpClient client = new SimpleBioTcpClient("localhost",1983);
client.send(data.toString().getBytes());
}
Date end = new Date();
long cost = end.getTime() - start.getTime();
System.out.println(n + " requests cost " + cost + " ms.");
}
经过测试,大约需要9864ms,大概接近10s。 第二种方式:通过启动5000个独立的客户端线程,同时请求,服务端进行计数:
package org.shirdrn.java.communications.bio;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Date;
/**
* 基于BIO的Socket通信测试
*
* @author shirdrn
*/
public class SimpleBioTcpTest {
static int threadCount = 5000;
/**
* 基于BIO的Socket服务端进程
*
* @author shirdrn
*/
static class SocketServer extends Thread {
/** 服务端口号 */
private int port = 8888;
/** 为客户端分配编号 */
private static int sequence = 0;
public SocketServer(int port) {
this.port = port;
}
@Override
public void run() {
Socket socket = null;
int counter = 0;
try {
ServerSocket serverSocket = new ServerSocket(this.port);
boolean flag = false;
Date start = null;
while(true) {
socket = serverSocket.accept(); // 监听
// 有请求到来才开始计时
if(!flag) {
start = new Date();
flag = true;
}
this.handleMessage(socket); // 处理一个连接过来的客户端请求
if(++counter==threadCount) {
Date end = new Date();
long last = end.getTime() - start.getTime();
System.out.println(threadCount + " requests cost " + last + " ms.");
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 处理一个客户端socket连接
* @param socket 客户端socket
* @throws IOException
*/
private void handleMessage(Socket socket) throws IOException {
InputStream in = socket.getInputStream(); // 流:客户端->服务端(读)
OutputStream out = socket.getOutputStream(); // 流:服务端->客户端(写)
int receiveBytes;
byte[] receiveBuffer = new byte[128];
String clientMessage = "";
if((receiveBytes=in.read(receiveBuffer))!=-1) {
clientMessage = new String(receiveBuffer,receiveBytes);
if(clientMessage.startsWith("I am the client")) {
String serverResponseWords =
"I am the server,and you are the " + (++sequence) + "th client.";
out.write(serverResponseWords.getBytes());
}
}
out.flush();
System.out.println("Server: receives clientMessage->" + clientMessage);
}
}
/**
* 基于BIO的Socket客户端线程
*
* @author shirdrn
*/
static class SocketClient implements Runnable {
private String ipAddress;
private int port;
/** 待发送的请求数据 */
private String data;
public SocketClient(String ipAddress,int port) {
this.ipAddress = ipAddress;
this.port = port;
}
@Override
public void run() {
this.send();
}
/**
* 连接Socket服务端,并模拟发送请求数据
*/
public void send() {
Socket socket = null;
OutputStream out = null;
InputStream in = null;
try {
socket = new Socket(this.ipAddress,this.port); // 连接
// 发送请求
out = socket.getOutputStream();
out.write(data.getBytes());
out.flush();
// 接收响应
in = socket.getInputStream();
int totalBytes = 0;
int receiveBytes = 0;
byte[] receiveBuffer = new byte[128];
if((receiveBytes=in.read(receiveBuffer))!=-1) {
totalBytes += receiveBytes;
}
String serverMessage = new String(receiveBuffer,receiveBytes);
System.out.println("Client: receives serverMessage->" + serverMessage);
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
// 发送请求并接收到响应,通信完成,关闭连接
out.close();
in.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public void setData(String data) {
this.data = data;
}
}
public static void main(String[] args) throws Exception {
SocketServer server = new SocketServer(1983);
server.start();
Thread.sleep(3000);
for(int i=0; i<threadCount; i++) {
SocketClient client = new SocketClient("localhost",1983);
client.setData("I am the client " + (i+1) + ".");
new Thread(client).start();
Thread.sleep(0,1);
}
}
}
经过测试,大约需要7110ms,大概接近7s,没有太大提高。 BIO通信改进 (编辑:安卓应用网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
