Socket与JavaIO

前面的文章我们整理了JavaIO中文件相关的类,主要是File和RandomAccessFile。而实际的IO绝不仅仅只有和文件数据的交互。除了文件IO以外,我们在应用开发中还比较经常会用到的就是网络数据传输,即网络IO。而在网络IO中,我们最常用到的就是基于Socket的IO,这篇文章我们来简单看一下Socket。

0. Socket

Socket这个类不是java.io包中的,而在java.net包中。以至于Bruce Eckel在他的《Thinking in Java》中并没有怎么提及Socket,更不要说在IO章节中描述Socket。这位作者仅仅是在他的《Thinking in Enterprise Java》中非常简要地介绍了一下java.net.Socket这个类。

我们在本篇文章稍微详细一些地来看一下java.net.Socket这个类的情况。

先看一下类的声明:

public class Socket implements java.io.Closeable

可见Socket仅仅是实现了Closeable接口,给出了close()方法,并没有继承任何其他类(除Object)。这一点和SocketInputStream/SocketOutputStream继承于FileInputStream/FileOutputStream不同。

实际上,Socket这个类也仅仅是一层外在的包装。如果我们打开JDK中Socket的源码会看到,各类操作方法实际上都是对impl属性的一个封装。我们来看下,Socket类中有这样两个比较重要的属性:

  • SocketImpl impl;          注意,这个是包级别可见。
  • private static SocketImplFactory factory = null;   这个默认情况下是null。

除了属性相关的,Socket有connect()、bind()、getInputStream()、getOutputStream()等几个方法。而这样几个方法实际上又是调用了SocketImpl类的impl对象属性的同名方法,我们不得不看看SocketImpl和SocketImplFactory这两个类/接口。

首先,这俩都是java.net包中的。SocketImpl是一个public的抽象类,SocketImplFactory是一个接口。后者是前者的工厂类,即有构造前者对象的方法。

public abstract class SocketImpl implements SocketOptions

和SocketImpl这个名字有些不符,这个类实际上是一个抽象类(Impl一般是具体实现)。SocketOptions定义了各个选项ID常量和相关的方法。实际上在Socket类中的各个set和get属性的方法中,属性也都是在SocketImpl中定义的。而对于实际的具体的操作,还都是由SocketImpl的具体子类来完成。

如图,可见Windows下JDK中的SocketImpl类及子类层次结构。像connect()这种方法,最终还是通过native方式依靠底层来实现。

回过头来,我们再来看下Socket的构造方法。Socket有10个重载的构造方法,其中有2个是@Deprecated的,还有1个protected和1个private重载。而这些方法中大部分都最终调用了这个private的实现。通常情况下,我们构造Socket的时候回给出要连接的服务端地址和端口号。这最终也会调用private重载构造方法实现中。

    private Socket(SocketAddress address, SocketAddress localAddr,
                   boolean stream) throws IOException {
        setImpl();

        // backward compatibility
        if (address == null)
            throw new NullPointerException();

        try {
            createImpl(stream);
            if (localAddr != null)
                bind(localAddr);
            if (address != null)
                connect(address);
        } catch (IOException e) {
            close();
            throw e;
        }
    }

而SetImpl()方法就是构建SocketImpl类的impl属性的过程:

    void setImpl() {
        if (factory != null) {
            impl = factory.createSocketImpl();
            checkOldImpl();
        } else {
            // No need to do a checkOldImpl() here, we know it's an up to date
            // SocketImpl!
            impl = new SocksSocketImpl();
        }
        if (impl != null)
            impl.setSocket(this);
    }

在调用这个方法后,紧接着是调用createImpl(),根据stream布尔参数确定是否是流式的。

最后,执行bind()或者直接connect()操作(这个过程通常也会做到bind本地地址)。构建完成。

其实,Socket还有一个来源,就是ServerSocket类的accept()方法。

1. ServerSocket简介

通常在应用开发中,Socket充当的是网络连接中客户端的角色,而服务器的角色由java.net.ServerSocket这个类来扮演。而事实上,这个ServerSocket类,也是一个类似于File,具有迷惑性命名的情况。

我们需要知道的是,即使是服务端应用开发,监听获取到连接后,即ServerSocket的accept()调用,我们获取到的是一个Socket对象。而实际数据通信所用的,还是这个Socket对象。

以上说的大部分都是面向TCP的连接的。而若要使用基于UDP的通信,java.net.DatagramSocket类将是需要考虑的,这里就不细说了。

2. java.net这个包

看包的名称也能看出来,这个包是一个和网络相关的类的集合。

除了上面提到的这些,顺便再简要提一下其它的类:

  • Inet4Address、Inet6Address、InetAddress、InetSocketAddress、InterfaceAddress。这几个类都是在网络通信中代表地址的。
  • URLConnection和HttpURLConnection。前者是后者的父类,后面这个类可以用来实现HTTP连接请求过程,当然这个过程也可能会用到其它java.net中的类。
  • URI、URL和URLClassLoader。这些类是用来唯一标识网络资源并对其进行操作的。URLClassLoader是用来通过URL加载类和资源的。

除此之外,java.net包中还有很多有意思的类,需要大家去使用去挖掘。

作者原创,难免有错误,欢迎读者热心评论留言指出,以免误导他人,谢谢!

此条目发表在 IO, Java, Java语言, 开发, 计算机技术 分类目录,贴了 , , , , 标签。将固定链接加入收藏夹。

发表评论

电子邮件地址不会被公开。 必填项已用 * 标注

您可以使用这些 HTML 标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>