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]連接!+++++++++++ \n"</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]數據:\n%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]數據:\n%s",clientaddr,receiveText);
client.register(selector, SelectionKey.OP_WRITE);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
selectionKey.cancel();  //取消selectionKey
}

 就可以捕獲異常,使服務器程序不會因為某一個客戶端的斷開而中斷.



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM