Java NIO套接字【源码笔记】

目录
一、TCP/IP套接字函数交互图示
二、交互示例
三、本地函数释义
四、本文总结
五、参考书籍
一、TCP/IP套接字函数交互图示

在这里插入图片描述

二、交互示例

本文以代码示例跟踪调用Native函数,看下原型函数的具体释义。例子中“客户端”从文件test02.tmp读取内容后,通过socket发送到“服务端”后写入test01.tmp文件中。

服务端示例

ServerSocketChannel ssc = ServerSocketChannel.open(); // @1
ssc.socket().bind (new InetSocketAddress(8121)); // @2
ssc.configureBlocking (false); // @3
while (true) {
    SocketChannel sc = ssc.accept( ); // @4
    if (sc == null) {
        Thread.sleep (2000);
    } else {
        Path path = Paths.get("/mytest/test01.tmp");
        FileChannel fileChannel = FileChannel.open(path,
                EnumSet.of(StandardOpenOption.CREATE,
                        StandardOpenOption.TRUNCATE_EXISTING,
                        StandardOpenOption.WRITE)
        );
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        while(sc.read(buffer) > 0) {
            buffer.flip();
            fileChannel.write(buffer);
            buffer.clear();
        }
        fileChannel.close();
        System.out.println("Receive finish.");
        sc.close( ); // @5
    }
}

客户端示例

SocketChannel server = SocketChannel.open();
SocketAddress socketAddr = new InetSocketAddress("localhost", 8121);
server.connect(socketAddr); // @6
Path path = Paths.get("/mytest/test02.tmp");
FileChannel fileChannel = FileChannel.open(path);
ByteBuffer buffer = ByteBuffer.allocate(1024);
while(fileChannel.read(buffer) > 0) {
    buffer.flip();
    server.write(buffer);
    buffer.clear();
}
fileChannel.close();
System.out.println("Sent finish");
server.close();
三、本地函数释义

代码@1
ServerSocketChannel.open()调用本地函数Net.c#Java_sun_nio_ch_Net_socket0()

fd = socket(domain, type, 0);

原型函数

int type = (stream ? SOCK_STREAM : SOCK_DGRAM);
int socket(int domain, int type, int protocol);

函数释义

socket()为通讯创建一个端点,为套接字返回一个文件描述符。 
socket()有三个参数:
domain 为创建的套接字指定协议集(或称做地址族 address family)。 例如:
    AF_INET 表示IPv4网络协议
    AF_INET6 表示IPv6
    AF_UNIX 表示本地套接字(使用一个文件)
type(socket类型)如下:
    SOCK_STREAM (可靠的面向流服务或流套接字)
    SOCK_DGRAM (数据报文服务或者数据报文套接字)
    SOCK_SEQPACKET (可靠的连续数据包服务)
    SOCK_RAW (在网络层之上自行指定运输层协议头,即原始套接字)
protocol 指定实际使用的传输协议。 
    最常见的就是IPPROTO_TCP、IPPROTO_SCTP、IPPROTO_UDP、IPPROTO_DCCP

小结:通过Native函数释义看出TCP/IP封装类SocketChannel;UDP/IP的封装类DatagramChannel通过传入socket()函数的类型不同来创建套接字通信端点。

代码@2
bind()调用本地PlainSocketImpl.c#Java_java_net_PlainSocketImpl_socketBind().

NET_Bind(int fd, struct sockaddr *him, int len)
rv = bind(fd, him, len);

原型函数

int bind(int sockfd, const struct sockaddr *my_addr, socklen_t addrlen);

函数释义

bind() 为一个套接字分配地址
bind()有三个参数
    sockfd, 表示使用bind函数的套接字描述符
    my_addr, 指向sockaddr结构(用于表示所分配地址)的指针
    addrlen, 用socklen_t字段指定了sockaddr结构的长度
如果发生错误,函数返回值为-1,否则为0

小结:bind主要将套接字与套接字地址关联。

代码@3
configureBlocking()调用本地函数IOUtil.c#Java_sun_nio_ch_IOUtil_configureBlocking().

int flags = fcntl(fd, F_GETFL);
int newflags = blocking ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK);
return (flags == newflags) ? 0 : fcntl(fd, F_SETFL, newflags);

原型函数

int fcntl(int descriptor,int command,...) 

函数释义

fcntl函数可执行各种描述符控制的操作。
* fcntl函数关于I/O的特性
  非阻塞式I/O。通过使用F_SETFL命令设置O_NONBLOCK文件状态标志,可以把一个套接字设置成非阻塞型。例如:
  flags = flags | O_NONBLOCK;
  fcntl(fd, F_SETFL, flags);
* 信号驱动式I/O。通过使用F_SETFL命令设置O_ASYNC文件状态标志,可以把套接字设置成一旦其状态发生变化,内核就产生一个SIGIO信号。

小结:Java NIO的非阻塞通过本地函数fcntl中F_SETFL来设置。

代码@4
accept()调用本地函数ServerSocketChannelImpl.c#Java_sun_nio_ch_ServerSocketChannelImpl_accept0。

// Java层
accept0(FileDescriptor ssfd, FileDescriptor newfd,
                               InetSocketAddress[] isaa)
// C层                              
newfd = (jint)accept(ssfd, (struct sockaddr *)&sa, &addrlen);

原型函数

int accept(int socket, struct sockaddr *address, int *address_len);

函数释义

accept用于从已完成连接队列头返回下一个已完成连接。如果accept成功,那么其返回值是由内核自动生成的一个全新描述符,代表与返回客户的TCP连接。
第一个参数:“监听套接字描述符”(由socket创建,随后用做bind和listen的第一个参数描述符),accept的返回值为“已连接套接字描述符”。一个服务器通常仅仅创建一个“监听套接字”(由socket创建,随后用做bind和listen的第一个参数的描述符)。内核为每个由服务器进程接受的客户端连接创建一个“已连接套接字”(TCP三路握手已经完成),当服务器完成对某个给定客户端的服务时,相应的已连接套接字就关闭。
第二个参数:address为sockaddr_in结构体变量
第三个参数:address的长度

代码@5
close通过close0(FileDescriptor fd)调用本地函数FileDispatcherImpl.c#Java_sun_nio_ch_FileDispatcherImpl_close0

int result = close(fd);

原型函数

int close(int sockfd)

函数释义

通常的Unix close函数也用来关闭套接字,并终止TCP连接。

小结:close一个TCP套接字的默认行为是把该套接字标记成已关闭,然后立即返回到调用进程。该套接字描述符不能再由调用进程使用。即不能作为write/read的第一个参数

代码@6
close函数通过connect0(boolean preferIPv6,FileDescriptor fd,InetAddress remote,int remotePort)调用本地函数Net.c#Java_sun_nio_ch_Net_connect0

rv = connect(fdval(env, fdo), (struct sockaddr *)&sa, sa_len);

原型函数

int connect(int sockfd, const struct sockaddr *servaddr, socklen_t addrlen)

函数释义

TCP用户用connect函数建立与TCP服务器的连接。
第一个参数:sockfd是由socket函数返回的套接字描述符
第二个参数:套接字地址结构的指针
第三个参数:地址结构大小
四、本文总结

主要跟了下Java NIO套接字中函数的本地原型函数及其含义。Java NIO Socket通道可以运行非阻塞模式以及可选择的,不必为每个socket连接新建一个线程,避免管理大量线程上下文切换的总开销;借助NIO类,一个或者几个线程就可以管理成百上千的活动socket连接并且只有很少甚至没有性能损失。

五、参考书籍

《Java NIO》、《UNIX网络编程 卷1》


「瓜农老梁 学习同行」
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值