zoukankan      html  css  js  c++  java
  • tcp nio 远程主机强迫关闭了一个现有的连接

    import java.io.IOException;
    import java.net.InetSocketAddress;
    

    import java.net.ServerSocket;
    import java.net.SocketAddress;
    import java.nio.ByteBuffer;
    import java.nio.channels.SelectionKey;
    import java.nio.channels.Selector;
    import java.nio.channels.ServerSocketChannel;
    import java.nio.channels.SocketChannel;
    import java.util.Iterator;
    import java.util.Set;

    public class NIOServer implements Runnable {

    <span class="hljs-comment">/*标识数字*/</span>
    <span class="hljs-keyword">private</span>  <span class="hljs-keyword">int</span> flag = <span class="hljs-number">0</span>;
    <span class="hljs-comment">/*缓冲区大小*/</span>
    <span class="hljs-keyword">private</span>  <span class="hljs-keyword">int</span> BLOCK = <span class="hljs-number">4096</span>;
    <span class="hljs-comment">/*接受数据缓冲区*/</span>
    <span class="hljs-keyword">private</span>  ByteBuffer sendbuffer = ByteBuffer.allocate(BLOCK);
    <span class="hljs-comment">/*发送数据缓冲区*/</span>
    <span class="hljs-keyword">private</span>  ByteBuffer receivebuffer = ByteBuffer.allocate(BLOCK);
    <span class="hljs-keyword">private</span>  Selector selector;
    
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">NIOServer</span><span class="hljs-params">(<span class="hljs-keyword">int</span> port)</span></span>{
    	<span class="hljs-keyword">try</span>{
    	<span class="hljs-comment">// 打开服务器套接字通道</span>
    		ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
    	<span class="hljs-comment">// 服务器配置为非阻塞</span>
    		serverSocketChannel.configureBlocking(<span class="hljs-keyword">false</span>);
    	<span class="hljs-comment">// 检索与此通道关联的服务器套接字</span>
    		ServerSocket serverSocket = serverSocketChannel.socket();
    	<span class="hljs-comment">// 进行服务的绑定</span>
    		serverSocket.bind(<span class="hljs-keyword">new</span> InetSocketAddress(port));
    	<span class="hljs-comment">// 通过open()方法找到Selector</span>
    		selector = Selector.open();
    	<span class="hljs-comment">// 注册到selector,等待连接</span>
    		serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
    		System.out.println(<span class="hljs-string">"Server Start----8888:"</span>);
    	}<span class="hljs-keyword">catch</span>(Exception e){
    		e.printStackTrace();
    	}
    }
    
    
    <span class="hljs-comment">// 监听</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">run</span><span class="hljs-params">()</span> </span>{
    	<span class="hljs-keyword">try</span> {
    		<span class="hljs-keyword">while</span> (<span class="hljs-keyword">true</span>) {
    			<span class="hljs-comment">// 选择一组键,并且相应的通道已经打开</span>
    			selector.select();
    			<span class="hljs-comment">// 返回此选择器的已选择键集。</span>
    			Set&lt;SelectionKey&gt; selectionKeys = selector.selectedKeys();
    			Iterator&lt;SelectionKey&gt; iterator = selectionKeys.iterator();
    			<span class="hljs-keyword">while</span> (iterator.hasNext()) {
    				SelectionKey selectionKey = iterator.next();
    				iterator.remove();
    				<span class="hljs-keyword">try</span>{
    					handleKey(selectionKey);
    			
    				}<span class="hljs-keyword">catch</span>(IOException e){
    					e.printStackTrace();
    					<span class="hljs-comment">//selectionKey.cancel();</span>
    					<span class="hljs-keyword">break</span>;
    				}
    			}
    		}
    	} <span class="hljs-keyword">catch</span> (Exception e) {
    		e.printStackTrace();
    	}
    }
    
    <span class="hljs-comment">// 处理请求</span>
    <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">handleKey</span><span class="hljs-params">(SelectionKey selectionKey)</span> <span class="hljs-keyword">throws</span> IOException </span>{
    	<span class="hljs-comment">// 接受请求</span>
    	ServerSocketChannel server = <span class="hljs-keyword">null</span>;
    	SocketChannel client = <span class="hljs-keyword">null</span>;
    	SocketAddress clientaddr = <span class="hljs-keyword">null</span>;
    	String receiveText;
    	String sendText;
    	<span class="hljs-keyword">int</span> count=<span class="hljs-number">0</span>;
    
    	<span class="hljs-comment">// 测试此键的通道是否已准备好接受新的套接字连接。</span>
    	
    	<span class="hljs-keyword">if</span> (selectionKey.isAcceptable()) {
    		<span class="hljs-comment">// 返回为之创建此键的通道。</span>
    		server = (ServerSocketChannel) selectionKey.channel();
    		<span class="hljs-comment">// 接受到此通道套接字的连接。</span>
    		<span class="hljs-comment">// 此方法返回的套接字通道(如果有)将处于阻塞模式。</span>
    		client = server.accept();
    		clientaddr=client.socket().getRemoteSocketAddress();
    		System.out.printf(<span class="hljs-string">"+++++++++++服务器端接受客户端[%s]连接!+++++++++++ 
    "</span>,clientaddr);
    		<span class="hljs-comment">// 配置为非阻塞</span>
    		client.configureBlocking(<span class="hljs-keyword">false</span>);
    		<span class="hljs-comment">// 注册到selector,等待连接</span>
    		client.register(selector, SelectionKey.OP_READ);
    	} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (selectionKey.isReadable()) {
    		
    			<span class="hljs-comment">// 返回为之创建此键的通道。</span>
    			client = (SocketChannel) selectionKey.channel();
    			clientaddr=client.socket().getRemoteSocketAddress();
    			<span class="hljs-comment">//将缓冲区清空以备下次读取</span>
    			receivebuffer.clear();
    			
    			<span class="hljs-comment">//读取服务器发送来的数据到缓冲区中</span>
    			count = client.read(receivebuffer);	
    			<span class="hljs-keyword">if</span> (count &gt; <span class="hljs-number">0</span>) {
    				receiveText = <span class="hljs-keyword">new</span> String( receivebuffer.array(),<span class="hljs-number">0</span>,count);
    				System.out.printf(<span class="hljs-string">"服务器端接受客户端[%s]数据:
    %s"</span>,clientaddr,receiveText);
    				client.register(selector, SelectionKey.OP_WRITE);
    			}
    		
    	} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (selectionKey.isWritable()) {
    		<span class="hljs-comment">//将缓冲区清空以备下次写入</span>
    		sendbuffer.clear();
    		<span class="hljs-comment">// 返回为之创建此键的通道。</span>
    		client = (SocketChannel) selectionKey.channel();
    		sendText=<span class="hljs-string">"message from server--"</span> + flag++;
    		<span class="hljs-comment">//向缓冲区中输入数据</span>
    		sendbuffer.put(sendText.getBytes());
    		 <span class="hljs-comment">//将缓冲区各标志复位,因为向里面put了数据标志被改变要想从中读取数据发向服务器,就要复位</span>
    		sendbuffer.flip();
    		<span class="hljs-comment">//输出到通道</span>
    		client.write(sendbuffer);
    		System.out.println(<span class="hljs-string">"服务器端向客户端发送数据--:"</span>+sendText);
    		client.register(selector, SelectionKey.OP_READ);
    	}
    }
    
    <span class="hljs-comment">/**
     * <span class="hljs-doctag">@param</span> args
     * <span class="hljs-doctag">@throws</span> IOException
     */</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> <span class="hljs-keyword">throws</span> IOException </span>{
    	<span class="hljs-comment">// TODO Auto-generated method stub</span>
    	<span class="hljs-keyword">int</span> port = <span class="hljs-number">8888</span>;
    	NIOServer server = <span class="hljs-keyword">new</span> NIOServer(port);
    	server.run();
    }
    

    }


    上面是从网上摘录的Java NIO TCP 的一个server程序,用客户端的NIO TCP与之连接的时候,只要客户端断开连接,服务器端就会报出“远程主机强迫关闭了一个现有的连接”的错误,并且中断服务器端程序。经过查阅之后发现,得知OP_READ 事件不仅仅只有可读时才触发,当channel中数据读完远程的另一端被关闭有一个错误的pending都会触发OP_READ事件"!

    解决办法:在selectionKey.isReadable()中加入错误捕捉机制,即:

     if (selectionKey.isReadable()) {
    try {
    // 返回为之创建此键的通道。
    client = (SocketChannel) selectionKey.channel();
    clientaddr=client.socket().getRemoteSocketAddress();
    //将缓冲区清空以备下次读取
    receivebuffer.clear();
    //读取服务器发送来的数据到缓冲区中
    count = client.read(receivebuffer);
    if (count > 0) {
    receiveText = new String( receivebuffer.array(),0,count);
    System.out.printf("服务器端接受客户端[%s]数据: %s",clientaddr,receiveText);
    client.register(selector, SelectionKey.OP_WRITE);
    }
    } catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    selectionKey.cancel();  //取消selectionKey
    }

     就可以捕获异常,使服务器程序不会因为某一个客户端的断开而中断.


  • 相关阅读:
    Hello iOS
    钝化程序、逻辑冻结、冻结程序的延续、瞬间转移
    对字符串操作的各种笔试题
    .NET各大平台数据列表控件绑定原理及比较(WebForm、Winform、WPF)
    多线程计算----pthread
    Xcode的Hello World(简单易懂)
    two sets of Qt binaries into the same process的解决办法
    微软新一代输入法框架 TSF
    Starting the application on Mac does not work(拷贝platforms到不同的位置,才能解决问题),还可设置DYLD_PRINT_LIBRARIES=1 观察动态库
    dddd
  • 原文地址:https://www.cnblogs.com/jpfss/p/10195902.html
Copyright © 2011-2022 走看看