Socket编程(六)---TCP/NIO实例讲解
来源:互联网 发布:mac ruby安装 编辑:程序博客网 时间:2024/05/29 13:17
本文将通过Channel(通道)、Buffer(缓冲区)以及Selector(选择器)来实现TCP下NIO的实例。
主要通过ServerSocketChannel与SocketChannel信道来完成本次实例。
1.服务端
public class ChannelServer2 { public static void main(String[] args) { ServerSocketChannel serverChannel; Selector selector; try { // 打开监听信道,ServerSocketChannel最终实现的是channel接口 serverChannel = ServerSocketChannel.open(); ServerSocket serverSocket = serverChannel.socket(); // 创建一个服务端socket InetSocketAddress address = new InetSocketAddress(7456); serverSocket.bind(address); // 绑定IP及端口 serverChannel.configureBlocking(false);// 设置为非阻塞方式,如果为true 那么就为传统的阻塞方式 selector = Selector.open();// 静态方法 实例化selector 创建选择器 /*如果你注册不止一种事件,那么可以用“位或”操作符将常量连接起来 SelectionKey.OP_READ |SelectionKey.OP_WRITE;*/ serverChannel.register(selector, SelectionKey.OP_ACCEPT); System.out.println("服务端启动......"); while (true) { // 等待某信道就绪(或超时) 监听注册通道,当其中有注册的 IO操作可以进行时,该函数返回 // 并将对应的SelectionKey加入selected-key if (selector.select(3000) == 0) { System.out.println("重新等待"); continue; } // selectedKeys()中包含了每个准备好某一I/O操作的信道的SelectionKey Set<SelectionKey> readyKeys = selector.selectedKeys(); // Selected-key代表了所有通过select()方法监测到可以进行IO操作的channel Iterator<SelectionKey> iterator = readyKeys.iterator(); while (iterator.hasNext()) {// 对每个信道进行一次循环,查看各个信道是否有事件需要处理 SelectionKey key = iterator.next(); iterator.remove(); if (key.isAcceptable()) { // 有客户端连接请求时 ServerSocketChannel server = (ServerSocketChannel) key.channel(); SocketChannel client = server.accept(); System.out.println("接收到连接:" + client); client.configureBlocking(false); SelectionKey clientKey = client.register(selector, SelectionKey.OP_WRITE | SelectionKey.OP_READ); ByteBuffer buffer = ByteBuffer.allocate(1024);// 分配一个新的1024字节的缓冲区 clientKey.attach(buffer);// 将给定的缓冲区附加到此键 } if (key.isReadable()) {// 判断是否有数据发送过来 SocketChannel client = (SocketChannel) key.channel(); ByteBuffer buffer = (ByteBuffer) key.attachment(); buffer.clear(); // 读取信息获得读取的字节数 long bytesRead = client.read(buffer); if (bytesRead == -1) { // 没有读取到内容的情况 client.close(); } else { // 将缓冲区准备为数据传出状态 buffer.flip(); // 将字节转化为为UTF-8的字符串 String receivedString = Charset.forName("UTF-8").newDecoder() .decode(buffer).toString(); // 控制台打印出来 System.out.println(client.socket().getRemoteSocketAddress()); System.out.println("信息内容:" + receivedString); // 准备发送的文本 String sendString = "你好,已经收到你的信息" + receivedString; // 将byte数组包装到缓冲区中 buffer = ByteBuffer.wrap(sendString.getBytes("UTF-8")); // 将字节序列从给定的缓冲区中写入此通道,并返回给客户端 client.write(buffer); } } } } } catch (IOException ex) { ex.printStackTrace(); } }}
2.客户端
客户端信道连接类
public class ChannelClient { // 信道选择器 Selector selector; // 与服务器通信的信道 SocketChannel socketChannel; // 要连接的服务器Ip地址 String hostIp; // 要连接的远程服务器在监听的端口 int hostListenningPort; public ChannelClient(String HostIp, int HostListenningPort) throws IOException { this.hostIp = HostIp; this.hostListenningPort = HostListenningPort; // 打开监听信道并设置为非阻塞模式 socketChannel = SocketChannel.open(new InetSocketAddress(hostIp, hostListenningPort)); socketChannel.configureBlocking(false); // 打开并注册选择器(监听读)到信道 selector = Selector.open(); socketChannel.register(selector, SelectionKey.OP_READ); // 启动读取线程 ChannelClientReadThread ccrt = new ChannelClientReadThread(selector); ccrt.start(); } // 发送字符串到服务器 public void sendMsg(String message) throws IOException { ByteBuffer writeBuffer = ByteBuffer.wrap(message.getBytes("UTF-8")); socketChannel.write(writeBuffer); } public static void main(String[] args) throws IOException { ChannelClient2 client = new ChannelClient2("localhost", 7456); try { client.sendMsg("我是客户端"); while (true) { Scanner scan = new Scanner(System.in);// 等待键盘输入数据 String string = scan.nextLine(); client.sendMsg(string); } } catch (IOException e) { e.printStackTrace(); } }}
客户端处理读的线程
public class ChannelClientReadThread extends Thread { private Selector selector; public ChannelClientReadThread(Selector selector) { this.selector = selector; } @Override public void run() { try { while (selector.select() > 0) { // 遍历每个有可用IO操作Channel对应的SelectionKey for (SelectionKey sk : selector.selectedKeys()) { // 如果该SelectionKey对应的Channel中有可读的数据 if (sk.isReadable()) { // 使用NIO读取Channel中的数据 SocketChannel sc = (SocketChannel) sk.channel();// 获取通道信息 ByteBuffer buffer = ByteBuffer.allocate(1024);// 分配缓冲区大小 sc.read(buffer);// 读取通道里面的数据放在缓冲区内 buffer.flip();// 调用此方法为一系列通道写入或相对获取 操作做好准备 // 将字节转化为为UTF-16的字符串 String receivedString = Charset.forName("UTF-8").newDecoder().decode(buffer) .toString(); // 控制台打印返回信息 System.out.println("服务器:" + sc.socket().getRemoteSocketAddress()); System.out.println("信息内容:" + receivedString); // 为下一次读取作准备 sk.interestOps(SelectionKey.OP_READ); } // 删除正在处理的SelectionKey selector.selectedKeys().remove(sk); } } } catch (IOException ex) { ex.printStackTrace(); } }}
目前TCP部分大致完结,之后会继续研究UDP部分内容
源码位置:https://github.com/oDevilo/Java-Network
1 0
- Socket编程(六)---TCP/NIO实例讲解
- socket编程实例讲解
- Java NIO socket编程实例
- Java NIO socket编程实例
- Tcp Socket编程实例
- socket编程实例TCP
- PHP socket通信(tcp/udp)实例讲解
- windows socket 编程实例--TCP
- Java Socket--TCP编程实例
- Java NIO socket编程实例 (转)
- java TCP/IP Socket编程-----NIO--TCP信道-----笔记11
- java socket编程实例代码讲解
- 初学java socket编程实例代码讲解
- java TCP/IP Socket编程-----NIO--初识-----笔记8
- java TCP/IP Socket编程-----NIO--Buffer-----笔记10
- 基于TCP协议的socket编程实例
- TCP Socket编程实例1---Sever端
- TCP Socket编程实例1---Client端
- Python创建和连接SqLite数据库
- Java加密和解密
- Redis 集合(Set)
- java 获取域名
- C#做窗体应用程序时,对excel涉及的背景色设置,边框设置,数据如何导入等的实现
- Socket编程(六)---TCP/NIO实例讲解
- 一句话介绍Hadoop家族产品
- Android学习系列(15)--App列表之圆角ListView(续)
- Java异常
- 最长回文子串(C/C++)
- JSP之——表单信息和图片一起提交
- 66. Plus One
- ssh 用法归纳
- maven命令