socketchannel在客户端链接断开时的问题

本文介绍了一个使用Java NIO实现的服务端程序,该程序能够处理客户端的连接请求,并通过Selector进行高效的非阻塞I/O操作。文章详细展示了如何创建ServerSocketChannel、配置非阻塞模式、绑定端口、获取Selector、注册SelectionKey以及处理读就绪和接受新连接等关键步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

public class ReaderLookup { private static final int PORT = 8888; // private Map<Reader,SocketChannel> readerCache = new Hashtable<Reader, SocketChannel>(); private List<SelectionKey> keyCache = new ArrayList<SelectionKey>(); public void lookupAllReaders(){ try { // Create a new server socket channel and set to non blocking mode ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); serverSocketChannel.configureBlocking(false); // Bind the server socket to local host ServerSocket serverSocket = serverSocketChannel.socket(); serverSocket.bind(new InetSocketAddress(InetAddress.getLocalHost(),PORT)); // Get a selector Selector selector = Selector.open(); // Register accepts on the server socket with the selector. This // step tells the selector that the socket wants to be put on the // ready list when accept operations occur, so allowing multiplexed // non-blocking I/O to take place. SelectionKey key_ServerSocketChannel = serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); int keys = 0 ; while( (keys = selector.select()) > 0 ){ // Get selected keys Set selectedKeys = selector.selectedKeys(); Iterator iterator = selectedKeys.iterator(); while (iterator.hasNext()) { SelectionKey key = (SelectionKey) iterator.next(); iterator.remove(); if(key.isAcceptable()){ ServerSocketChannel tmpServerSocketChannel = (ServerSocketChannel) key.channel(); // The accept() returned immediately because the ServerSocketChannel was working on non-blocking mode SocketChannel client = tmpServerSocketChannel.accept(); // 最好不要注册写状态SelectionKey.OP_WRITE,因为写状态在任何时候都是ready的,都会被select(),影响性能 client.configureBlocking(false); SelectionKey clientKey = client.register(selector, SelectionKey.OP_READ); // Save the client channel's selectionkey for write databuffer keyCache.add(clientKey); }else if(key.isReadable()){ System.out.println(key.readyOps()); int dataLength = 4; ByteBuffer dst = ByteBuffer.allocate(dataLength); SocketChannel socketChannel = (SocketChannel) key.channel(); socketChannel.read(dst); // dst.flip(); // // Do something with the bytebuffer dst.flip(); Charset charset = Charset.forName("us-ascii"); CharsetDecoder decoder = charset.newDecoder(); CharBuffer charBuffer = decoder.decode(dst); if(charBuffer.toString().equals("w")){ writeMsg(socketChannel.keyFor(selector)); }else{ dst.flip(); socketChannel.write(dst); } // System.out.println(charBuffer.toString()); } } } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } private void writeMsg(SelectionKey selectionKey) throws IOException { // TODO Auto-generated method stub ByteBuffer bf = ByteBuffer.wrap("It isn't through selector!".getBytes()); ((SocketChannel)selectionKey.channel()).write(bf); } public static void main(String[] args) { ReaderLookup main = new ReaderLookup(); main.lookupAllReaders(); } }

当客户端的链接异常断开,此时代表这个链接的channel一直处于readable的状态,如何检查链接已断开呢?

一段时间的试验发现,链接断开后,虽然该channel的ready operation是OP_READ,但是此时channel.read(buffer)返回-1,此时可以增加一个判断

while( (keys = selector.select()) > 0 ){ // Get selected keys Set selectedKeys = selector.selectedKeys(); Iterator iterator = selectedKeys.iterator(); while (iterator.hasNext()) { SelectionKey key = (SelectionKey) iterator.next(); iterator.remove(); if(key.isAcceptable()){ ServerSocketChannel tmpServerSocketChannel = (ServerSocketChannel) key.channel(); // The accept() returned immediately because the ServerSocketChannel was working on non-blocking mode SocketChannel client = tmpServerSocketChannel.accept(); // 最好不要注册写状态SelectionKey.OP_WRITE,因为写状态在任何时候都是ready的,都会被select(),影响性能 client.configureBlocking(false); SelectionKey clientKey = client.register(selector, SelectionKey.OP_READ ); // Save the client channel's selectionkey for write databuffer keyCache.add(clientKey); }else if(key.isReadable()){ SocketChannel socketChannel = (SocketChannel) key.channel(); ByteBuffer dstBuffer = ByteBuffer.allocate(BUFFER_SIZE); int count ; while( (count = socketChannel.read(dstBuffer)) > 0){ dstBuffer.flip(); // Do something with the bytebuffer Charset charset = Charset.forName("us-ascii"); CharsetDecoder decoder = charset.newDecoder(); CharBuffer charBuffer = decoder.decode(dstBuffer); if(charBuffer.toString().equals("w")){ writeMsg(socketChannel.keyFor(selector)); }else{ dstBuffer.flip(); socketChannel.write(dstBuffer); } dstBuffer.clear(); }//end while( (count = socketChannel.read(dst)) > 0){ //链接异常中断,关闭channel if(count < 0){ socketChannel.close(); } }//end else if(key.isReadable()){ }//end while (iterator.hasNext()) }//end while( (keys = selector.select()) > 0 ){ }

//链接异常中断,关闭channel
if(count < 0){
socketChannel.close();
}

### 客户端与服务端通信流程详解 #### 基于Socket的TCP通信过程 在基于Python Socket库实现的服务端和客户端之间的通信过程中,双方通过一系列预定义的操作完成数据交换。当客户端启动,会调用`socket()`函数创建套接字对象并指定协议族(AF_INET表示IPv4),随后设置地址重用选项以提高灵活性[^1]。 ```python import socket client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) ``` 为了建立连接,客户端需调用`connect()`方法并向目标服务器发送SYN同步报文,其中包含了目的主机名或IP地址及其监听端口作为参数。一旦三次握手顺利完成,则意味着一条可靠的双向传输通道已经形成,允许后续的数据交互操作得以执行。 ```python server_address = ('localhost', 8080) client_socket.connect(server_address) ``` 对于服务端而言,在接收到来自任意客户的首次接触尝试之前,必须先绑定本地接口并通过listen()进入侦听状态等待潜在来访者的接入请求到达;每当有新的链接被接受下来之后就会触发accept()回调事件从而返回一个新的专用子线程专门负责处理该特定用户的事务往来直至断开为止。 ```python server_socket.bind(('localhost', 8080)) server_socket.listen(5) while True: connection, client_address = server_socket.accept() try: while True: data = connection.recv(16) if not data: break # Process the received message here... finally: connection.close() ``` #### HTTPS的安全协商阶段 除了常规的HTTP GET/POST方式之外,现代Web应用程序更多采用的是经过SSL/TLS加密保护后的HTTPS协议来进行敏感资料传递。在此期间,浏览器(即扮演着客户角色的一方)将主动发出带有特殊标志位的通知给网站后台告知对方自己期望开启一次受信任的身份验证对话框,并附带一份清单列明所支持的各种算法组合方案供后者挑选最合适的配对策略使用[^2]。 具体来说就是: - 浏览器向站点提交Client Hello消息; - Web Server回应Server Hello连同选定密钥材料一起打包回传; - 双方各自独立计算出会话秘钥用于接下来的应用层负载编码工作; - 终止前导部分转而正式步入稳定运行期继续正常业务逻辑运作直到结束整个生命周期。 #### Netty框架下的高效网络通讯模式 不同于上述两种较为基础的传统做法,Netty提供了一种更为先进且灵活的消息收发解决方案——它不仅能够很好地兼容多种主流互联网标准规范而且还具备出色的性能表现力。借助于此工具包可以轻松搭建起一套完整的分布式系统架构体系结构,无论是即聊天室还是大型在线游戏平台都能胜任愉快[^3]。 首先是在项目初始化环节要引入必要的依赖项,接着按照官方文档指导编写相应的Handler处理器类去响应各类IO事件的发生变化情况。最后利用Bootstrap辅助类简化繁琐的配置步骤快速部署上线测试环境准备就绪即可投入使用了。 ```java // Java code snippet demonstrating a simple Netty-based TCP echo server setup. EventLoopGroup group = new NioEventLoopGroup(); try { Bootstrap b = new Bootstrap(); // (1) b.group(group); b.channel(NioSocketChannel.class); // (2) b.remoteAddress(new InetSocketAddress(host, port)); b.handler(new ChannelInitializer<SocketChannel>() { // (3) @Override public void initChannel(SocketChannel ch) throws Exception { ChannelPipeline p = ch.pipeline(); p.addLast(new EchoClientHandler()); } }); // Make the connection attempt. ChannelFuture f = b.connect().sync(); // (5) f.channel().closeFuture().sync(); } finally { group.shutdownGracefully(); } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值