一、Socket 的使用
1、单线程Socket的使用
/**
* 单线程版本
* 问题描述:只能服务单个客户端
* 解决方案:多线程版本
*/
public class Socket_V1 {
public static void main(String[] args) throws Exception {
ServerSocket serverSocket = new ServerSocket(1234);
System.out.println("启动服务器:");
while(true) {
System.out.println("等待客户端链接(阻塞)……");
Socket socket = serverSocket.accept();
System.out.println("有链接进来了……");
handle(socket);
}
}
private static void handle(Socket socket) {
try {
InputStream inputStream = socket.getInputStream();
byte[] b = new byte[1024];
String str = null;
while(true) {
str = null;
System.out.println("等待输入……");
int read = inputStream.read(b);
if(read == -1) {
System.out.println("客户端关闭。");
break;
}
str = new String(b);
System.out.println(str);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
2、多线程版本
/**
* 多线程版本
* 问题描述:多线程开销大
* 解决方案:NIO
*/
public class Socket_V2 {
public static void main(String[] args) throws Exception {
ServerSocket serverSocket = new ServerSocket(1234);
// 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
ExecutorService threadPool = Executors.newFixedThreadPool(10);
System.out.println("启动服务器:");
while(true) {
System.out.println("等待客户端链接(阻塞)……");
final Socket socket = serverSocket.accept();
System.out.println("有链接进来了……");
//使用线程池来,支持并发
threadPool.execute(new Runnable() {
public void run() {
handle(socket);
}
});
}
}
private static void handle(Socket socket) {
try {
InputStream inputStream = socket.getInputStream();
byte[] b = new byte[1024];
String str = null;
while(true) {
str = null;
System.out.println("等待输入……");
int read = inputStream.read(b);
if(read == -1) {
System.out.println("客户端关闭。");
break;
}
str = new String(b);
System.out.println(str);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
3、NIO
/**
* NIO版本
* 原理:IO多路复用
* NIO与旧Socket对比:
* ServerSocketChannel <-等价于-> ServerSocket
* SocketChannel <-等价于-> Socket
* Selector 选择器
* SelectionKey 事件类型
*/
public class Socket_NIO {
public static void main(String[] args) throws Exception {
//获得Socket通道
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
//将通道设置为非阻塞模式
serverSocketChannel.configureBlocking(false);
//将通道绑定到指定的端口
serverSocketChannel.socket().bind(new InetSocketAddress(1234));
//获取通道选择器
Selector selector = Selector.open();
//将连接事件,注册到选择器内(步骤1)
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("服务器启动成功。端口已经监听……");
while(true) {
selector.select();//阻塞等待已经注册的事件
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while(iterator.hasNext()) {
SelectionKey next = iterator.next();
iterator.remove();
if(next.isAcceptable()) { //如果是连接事件,注册读写事件
System.out.println("获取到连接事件。");
ServerSocketChannel sschannel = (ServerSocketChannel) next.channel(); //该强转是步骤1内存放的值
SocketChannel channel = sschannel.accept(); //获取客户端连接的通道
channel.configureBlocking(false);//设置为非阻塞
channel.register(selector, SelectionKey.OP_READ); //步骤2
}else if(next.isReadable()) { //如果是可读事件,则执行读操作
System.out.println("获取到可读事件。");
SocketChannel channel = (SocketChannel) next.channel(); //获取socket通道,该强转是步骤2内存放的值
ByteBuffer dst = ByteBuffer.allocate(1024); //缓冲区
int read = channel.read(dst);
if(read < 0 ) {
System.out.println("客户端关闭。");
next.cancel();
break;
}
System.out.println("客户端输入:"+new String(dst.array()));
} //还有其他事件……略
}
}
}
}