`

Socket实战之六 使用NIO包实现Socket通信

    博客分类:
  • J2SE
阅读更多
本文地址:http://blog.csdn.net/kongxx/article/details/7288896
前面几篇文章介绍了使用java.io和java.net类库实现的Socket通信,下面介绍一下使用java.nio类库实现的Socket。
java.nio包是Java在1.4之后增加的,用来提高I/O操作的效率。在nio包中主要包括以下几个类或接口:

* Buffer:缓冲区,用来临时存放输入或输出数据。

* Charset:用来把Unicode字符编码和其它字符编码互转。

* Channel:数据传输通道,用来把Buffer中的数据写入到数据源,或者把数据源中的数据读入到Buffer。

* Selector:用来支持异步I/O操作,也叫非阻塞I/O操作。

nio包中主要通过下面两个方面来提高I/O操作效率:

* 通过Buffer和Channel来提高I/O操作的速度。

* 通过Selector来支持非阻塞I/O操作。


下面来看一下程序中是怎么通过这些类库实现Socket功能。

首先介绍一下几个辅助类
辅助类SerializableUtil,这个类用来把java对象序列化成字节数组,或者把字节数组反序列化成java对象。
[java] view plaincopyprint?
package com.googlecode.garbagecan.test.socket; 
 
import java.io.ByteArrayInputStream; 
import java.io.ByteArrayOutputStream; 
import java.io.IOException; 
import java.io.ObjectInputStream; 
import java.io.ObjectOutputStream; 
 
public class SerializableUtil { 
     
    public static byte[] toBytes(Object object) { 
        ByteArrayOutputStream baos = new ByteArrayOutputStream(); 
        ObjectOutputStream oos = null; 
        try { 
            oos = new ObjectOutputStream(baos); 
            oos.writeObject(object); 
            byte[] bytes = baos.toByteArray(); 
            return bytes; 
        } catch(IOException ex) { 
            throw new RuntimeException(ex.getMessage(), ex); 
        } finally { 
            try { 
                oos.close(); 
            } catch (Exception e) {} 
        } 
    } 
     
    public static Object toObject(byte[] bytes) { 
        ByteArrayInputStream bais = new ByteArrayInputStream(bytes); 
        ObjectInputStream ois = null; 
        try { 
            ois = new ObjectInputStream(bais); 
            Object object = ois.readObject(); 
            return object; 
        } catch(IOException ex) { 
            throw new RuntimeException(ex.getMessage(), ex); 
        } catch(ClassNotFoundException ex) { 
            throw new RuntimeException(ex.getMessage(), ex); 
        } finally { 
            try { 
                ois.close(); 
            } catch (Exception e) {} 
        } 
    } 

辅助类MyRequestObject和MyResponseObject,这两个类是普通的java对象,实现了Serializable接口。MyRequestObject类是Client发出的请求,MyResponseObject是Server端作出的响应。
[java] view plaincopyprint?
package com.googlecode.garbagecan.test.socket.nio; 
 
import java.io.Serializable; 
 
public class MyRequestObject implements Serializable { 
 
    private static final long serialVersionUID = 1L; 
 
    private String name; 
     
    private String value; 
 
    private byte[] bytes; 
     
    public MyRequestObject(String name, String value) { 
        this.name = name; 
        this.value = value; 
        this.bytes = new byte[1024]; 
    } 
     
    public String getName() { 
        return name; 
    } 
 
    public void setName(String name) { 
        this.name = name; 
    } 
 
    public String getValue() { 
        return value; 
    } 
 
    public void setValue(String value) { 
        this.value = value; 
    } 
     
    @Override 
    public String toString() { 
        StringBuffer sb = new StringBuffer(); 
        sb.append("Request [name: " + name  + ", value: " + value + ", bytes: " + bytes.length+ "]"); 
        return sb.toString(); 
    } 

 
package com.googlecode.garbagecan.test.socket.nio; 
 
import java.io.Serializable; 
 
public class MyResponseObject implements Serializable { 
 
    private static final long serialVersionUID = 1L; 
 
    private String name; 
     
    private String value; 
 
    private byte[] bytes; 
     
    public MyResponseObject(String name, String value) { 
        this.name = name; 
        this.value = value; 
        this.bytes = new byte[1024]; 
    } 
     
    public String getName() { 
        return name; 
    } 
 
    public void setName(String name) { 
        this.name = name; 
    } 
 
    public String getValue() { 
        return value; 
    } 
 
    public void setValue(String value) { 
        this.value = value; 
    } 
     
    @Override 
    public String toString() { 
        StringBuffer sb = new StringBuffer(); 
        sb.append("Response [name: " + name  + ", value: " + value + ", bytes: " + bytes.length+ "]"); 
        return sb.toString(); 
    } 


下面主要看一下Server端的代码,其中有一些英文注释对理解代码很有帮助,注释主要是来源jdk的文档和例子,这里就没有再翻译
[java] view plaincopyprint?
package com.googlecode.garbagecan.test.socket.nio; 
 
import java.io.ByteArrayOutputStream; 
import java.io.IOException; 
import java.net.InetSocketAddress; 
import java.nio.ByteBuffer; 
import java.nio.channels.ClosedChannelException; 
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.logging.Level; 
import java.util.logging.Logger; 
 
import com.googlecode.garbagecan.test.socket.SerializableUtil; 
 
public class MyServer3 { 
 
    private final static Logger logger = Logger.getLogger(MyServer3.class.getName()); 
     
    public static void main(String[] args) { 
        Selector selector = null; 
        ServerSocketChannel serverSocketChannel = null; 
         
        try { 
            // Selector for incoming time requests 
            selector = Selector.open(); 
 
            // Create a new server socket and set to non blocking mode 
            serverSocketChannel = ServerSocketChannel.open(); 
            serverSocketChannel.configureBlocking(false); 
             
            // Bind the server socket to the local host and port 
            serverSocketChannel.socket().setReuseAddress(true); 
            serverSocketChannel.socket().bind(new InetSocketAddress(10000)); 
             
            // 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. 
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); 
     
            // Here's where everything happens. The select method will 
            // return when any operations registered above have occurred, the 
            // thread has been interrupted, etc. 
            while (selector.select() > 0) { 
                // Someone is ready for I/O, get the ready keys 
                Iterator<SelectionKey> it = selector.selectedKeys().iterator(); 
     
                // Walk through the ready keys collection and process date requests. 
                while (it.hasNext()) { 
                    SelectionKey readyKey = it.next(); 
                    it.remove(); 
                     
                    // The key indexes into the selector so you 
                    // can retrieve the socket that's ready for I/O 
                    execute((ServerSocketChannel) readyKey.channel()); 
                } 
            } 
        } catch (ClosedChannelException ex) { 
            logger.log(Level.SEVERE, null, ex); 
        } catch (IOException ex) { 
            logger.log(Level.SEVERE, null, ex); 
        } finally { 
            try { 
                selector.close(); 
            } catch(Exception ex) {} 
            try { 
                serverSocketChannel.close(); 
            } catch(Exception ex) {} 
        } 
    } 
 
    private static void execute(ServerSocketChannel serverSocketChannel) throws IOException { 
        SocketChannel socketChannel = null; 
        try { 
            socketChannel = serverSocketChannel.accept(); 
            MyRequestObject myRequestObject = receiveData(socketChannel); 
            logger.log(Level.INFO, myRequestObject.toString()); 
             
            MyResponseObject myResponseObject = new MyResponseObject( 
                    "response for " + myRequestObject.getName(),  
                    "response for " + myRequestObject.getValue()); 
            sendData(socketChannel, myResponseObject); 
            logger.log(Level.INFO, myResponseObject.toString()); 
        } finally { 
            try { 
                socketChannel.close(); 
            } catch(Exception ex) {} 
        } 
    } 
     
    private static MyRequestObject receiveData(SocketChannel socketChannel) throws IOException { 
        MyRequestObject myRequestObject = null; 
        ByteArrayOutputStream baos = new ByteArrayOutputStream(); 
        ByteBuffer buffer = ByteBuffer.allocate(1024); 
         
        try { 
            byte[] bytes; 
            int size = 0; 
            while ((size = socketChannel.read(buffer)) >= 0) { 
                buffer.flip(); 
                bytes = new byte[size]; 
                buffer.get(bytes); 
                baos.write(bytes); 
                buffer.clear(); 
            } 
            bytes = baos.toByteArray(); 
            Object obj = SerializableUtil.toObject(bytes); 
            myRequestObject = (MyRequestObject)obj; 
        } finally { 
            try { 
                baos.close(); 
            } catch(Exception ex) {} 
        } 
        return myRequestObject; 
    } 
 
    private static void sendData(SocketChannel socketChannel, MyResponseObject myResponseObject) throws IOException { 
        byte[] bytes = SerializableUtil.toBytes(myResponseObject); 
        ByteBuffer buffer = ByteBuffer.wrap(bytes); 
        socketChannel.write(buffer); 
    } 

下面是Client的代码,代码比较简单就是启动了100个线程来访问Server
[java] view plaincopyprint?
package com.googlecode.garbagecan.test.socket.nio; 
 
import java.io.ByteArrayOutputStream; 
import java.io.IOException; 
import java.net.InetSocketAddress; 
import java.net.SocketAddress; 
import java.nio.ByteBuffer; 
import java.nio.channels.SocketChannel; 
import java.util.logging.Level; 
import java.util.logging.Logger; 
 
import com.googlecode.garbagecan.test.socket.SerializableUtil; 
 
public class MyClient3 { 
 
    private final static Logger logger = Logger.getLogger(MyClient3.class.getName()); 
     
    public static void main(String[] args) throws Exception { 
        for (int i = 0; i < 100; i++) { 
            final int idx = i; 
            new Thread(new MyRunnable(idx)).start(); 
        } 
    } 
     
    private static final class MyRunnable implements Runnable { 
         
        private final int idx; 
 
        private MyRunnable(int idx) { 
            this.idx = idx; 
        } 
 
        public void run() { 
            SocketChannel socketChannel = null; 
            try { 
                socketChannel = SocketChannel.open(); 
                SocketAddress socketAddress = new InetSocketAddress("localhost", 10000); 
                socketChannel.connect(socketAddress); 
 
                MyRequestObject myRequestObject = new MyRequestObject("request_" + idx, "request_" + idx); 
                logger.log(Level.INFO, myRequestObject.toString()); 
                sendData(socketChannel, myRequestObject); 
                 
                MyResponseObject myResponseObject = receiveData(socketChannel); 
                logger.log(Level.INFO, myResponseObject.toString()); 
            } catch (Exception ex) { 
                logger.log(Level.SEVERE, null, ex); 
            } finally { 
                try { 
                    socketChannel.close(); 
                } catch(Exception ex) {} 
            } 
        } 
 
        private void sendData(SocketChannel socketChannel, MyRequestObject myRequestObject) throws IOException { 
            byte[] bytes = SerializableUtil.toBytes(myRequestObject); 
            ByteBuffer buffer = ByteBuffer.wrap(bytes); 
            socketChannel.write(buffer); 
            socketChannel.socket().shutdownOutput(); 
        } 
 
        private MyResponseObject receiveData(SocketChannel socketChannel) throws IOException { 
            MyResponseObject myResponseObject = null; 
            ByteArrayOutputStream baos = new ByteArrayOutputStream(); 
             
            try { 
                ByteBuffer buffer = ByteBuffer.allocateDirect(1024); 
                byte[] bytes; 
                int count = 0; 
                while ((count = socketChannel.read(buffer)) >= 0) { 
                    buffer.flip(); 
                    bytes = new byte[count]; 
                    buffer.get(bytes); 
                    baos.write(bytes); 
                    buffer.clear(); 
                } 
                bytes = baos.toByteArray(); 
                Object obj = SerializableUtil.toObject(bytes); 
                myResponseObject = (MyResponseObject) obj; 
                socketChannel.socket().shutdownInput(); 
            } finally { 
                try { 
                    baos.close(); 
                } catch(Exception ex) {} 
            } 
            return myResponseObject; 
        } 
    } 


最后测试上面的代码,首先运行Server类,然后运行Client类,就可以分别在Server端和Client端控制台看到发送或接收到的MyRequestObject或MyResponseObject对象了。

关于NIO和IO的比较,下面的两篇文章对理解很有帮助,可以参考一下。
http://tutorials.jenkov.com/java-nio/nio-vs-io.html

https://blogs.oracle.com/slc/entry/javanio_vs_javaio
分享到:
评论

相关推荐

    《NIO与Socket编程技术指南》_高洪岩

    Socket通信基于TCP/IP协议,确保数据的可靠传输,通过输入流和输出流进行数据交换。在实际应用中,Socket常用于实现分布式服务、聊天应用、文件传输等场景。 本书可能涵盖了以下主题: 1. NIO基础:介绍NIO的基本...

    基于Java的源码-Java Socket通信实现.zip

    10. **实战应用**:Java Socket通信不仅限于命令行程序,还可以嵌入到Web应用、桌面应用中,例如,WebSocket协议就是基于TCP的Socket实现的,用于在Web浏览器和服务器之间进行全双工通信。 以上是对"基于Java的源码...

    java socket通信h

    Java Socket通信是网络编程中的重要组成部分,主要用于实现客户端与服务器之间的双向通信。Socket在Java中提供了TCP(传输控制协议)的编程接口,使得开发者能够构建可靠的、基于连接的数据传输通道。下面将详细介绍...

    socket通信成熟案例SanNiuSignal源码

    通过对SanNiuSignal源码的学习,开发者可以深入了解Socket通信的实战应用,掌握如何在实际项目中实现高效、可靠的网络通信。同时,源码中的设计思路和最佳实践也有助于提升程序员在网络编程领域的专业技能。

    30分钟小Demo,基于Socket套接字实现即时通信IM

    在这个Demo中,我们使用了Socket通信,这是一种基于TCP/IP协议的应用层通信方式,确保了数据的可靠传输。Socket提供了一种进程间通信(IPC)机制,使得Android客户端可以与服务器进行双向通信。 1. **Socket基础...

    java socket教程java socket教程

    除了基础的Socket通信,Java还提供了NIO(Non-blocking I/O)Socket,支持多路复用,提高并发性能。此外,还有SSLSocket,用于加密通信,保障数据安全。 总结,Java Socket教程涵盖了从基础的Socket通信机制到高级...

    详细介绍 NIO与Netty编程-实战讲义详细pdf.7z

    NIO库包括`java.nio`包及其子包,如`java.nio.channels`和`java.nio.charset`。 ### 三、NIO网络聊天案例 NIO在网络聊天应用中可以实现高效的通信。例如,服务器通过一个ServerSocketChannel监听客户端连接,接收到...

    Java TCP IP Socket编程 包含源码

    `java.nio.channels`包下的`ServerSocketChannel`和`SocketChannel`可以替代传统的`ServerSocket`和`Socket`。 9. **套接字选项**:`Socket`对象有多种配置选项,如设置超时时间、禁用Nagle算法等,可以通过`Socket...

    JAVA Socket 网络编程教程

    Java Socket网络编程是Java开发中一个重要的组成部分,它允许应用程序通过网络进行通信,实现客户端与服务器之间的数据交换。本教程将深入探讨Java Socket编程的基本概念、原理和实践应用。 一、Socket基本概念 ...

    java socket tcpip多线程网络通信服务器客户端

    在“java socket tcpip多线程网络通信服务器客户端”这个主题中,我们将深入探讨如何使用Java Socket实现基于TCP/IP协议的多线程服务器和客户端通信。 TCP(传输控制协议)是一种面向连接的、可靠的、基于字节流的...

    java socket 学习资料

    Java Socket是Java编程语言中用于实现网络通信的核心API,它基于TCP/IP协议栈,提供了低级别的网络连接功能。Socket在Java中被广泛用于构建客户端-服务器应用,例如创建Web服务器、聊天应用程序、文件传输等。以下是...

    Java 套接字(socket)101

    Java套接字(Socket)是网络编程中的基本概念,它为两台计算机之间的通信提供了标准接口。...在实际开发中,可以根据需求进一步封装和优化,例如使用NIO(非阻塞I/O)提高效率,或者使用SSL/TLS实现安全的网络通信。

    java socket 编程文档

    Java套接字编程是网络通信的核心技术之一,它允许Java应用程序之间或应用程序与远程服务器之间的双向数据传输。本文将深入探讨Java Socket编程的基础知识、关键概念以及如何在实践中应用。 一、Socket概述 Socket,...

    Android应用源码之Android应用源码安卓与PC的Socket通信项目C#版+Java版_应用.zip

    本项目涉及的是Android应用与PC之间的Socket通信技术,主要涵盖了两个方面:一是Android客户端的实现,二是PC端的C#和Java服务器端的构建。在实际应用中,这种通信方式常用于设备间的数据交换、远程控制或者游戏同步...

    java的socket编程课件~~~

    它们共同构建了Socket通信的基本框架。 1. **创建Socket连接**: - 客户端通过`Socket(String host, int port)`构造函数建立到指定服务器的连接。`host`是服务器的IP地址或域名,`port`是服务器监听的端口号。 - ...

    java网络编程socket

    六、实战应用 1. 文件传输:Socket可用于实现简单的文件传输,客户端发送文件,服务器端接收并保存。 2. 即时通讯:聊天软件、游戏等实时通信应用,利用Socket实现数据的即时交互。 3. 数据同步:分布式系统中的...

    java socket 中文教程

    这将加深对Socket通信机制的理解,提高实际编程能力。 本Java Socket中文教程旨在全面讲解Socket编程的核心概念和实践技巧,帮助开发者构建自己的网络应用程序。阅读HTML文档,你可以找到更多关于Socket编程的实例...

    Java TCPIP Socket 编程中文版

    在本教程中,我们将深入探讨这个主题,理解TCP/IP协议的基础,以及如何在Java中使用Socket进行实现。 TCP(传输控制协议)和IP(互联网协议)是互联网通信的基石。TCP是一种面向连接的、可靠的传输协议,确保数据包...

    java基于socket手写协议的在线考试系统

    在Java中,`java.net.Socket`类和`ServerSocket`类是实现Socket通信的核心。开发者需要定义客户端和服务端的交互协议,包括数据格式、请求类型和响应格式等,这正是“手写协议”的含义。 在这个系统中,协议设计至...

Global site tag (gtag.js) - Google Analytics