Socket通道

Socket通道和文件通道有着不一样的特征:

  • Socket通道类可以运行于非阻塞模式,并且是可选的。这两个特征可以激活大程序(如网络服务和中间件组件)巨大的可伸缩性和灵活性,因此再也没有为每个Socket连接添加一个线程的必要。这一特性避免了管理大量线程所需的上下文交换总开销,借助NIO,一个或几个线程就可以管理成百上千个Socket连接,并且没有或只有很少的性能损失
  • 全部的Socket通道类(SocketChannel,ServerSocketChannel,DatagramChannel)在被实例化时都会创建一个对应的Socket对象,如Socket,ServerSocket,DatagramSocket,这些Socket可以通过调用对应通道类的socket()方法来获取。此外,这三个Socket都有getChannel()方法
  • 每个Socket通道都有一个关联的java.net.socket对象,反之却不是这样。如果使用传统方式(直接实例化)创建了一个Socket对象,它不会关联有Socket通道,且它的getChannel()方法总是返回null

打开SocketChannel

SocketChannel channel = SocketChannel.open();
channel.connect(new InetSocketAddress("http://xxx,com", 80));

关闭SocketChannel

channel.close();

从SocketChannel读数据

ByteBuffer buf = ByteBuffer.allocate(100);
int bytr = channel.read(buf);

该方法将数据从SocketChannel读到Buffer中,read()方法的返回值表示读了多少字节到Buffer中,当返回值为-1时,表示已经读到了流的末尾

写入SocketChannel

 String str = "some thing";
ByteBuffer buf = ByteBuffer.allocate(100);
buf.put(str.getBytes());
buf.flip();
while(buf.hasRemaining){
socketChannel.write(buf);
}
socketChannel.close();

注意: SocketChannel.write()方法的调用是在一个while循环中的。Write()方法无法保证能写多少字节到SocketChannel。所以,我们重复调用write()直到Buffer没有要写的字节为止。

非阻塞模式

要把一个Socket通道置于非阻塞模式,要依赖父类的父类SelectableChannel:

 public abstract class SelectableChannel extends AbstractInterruptibleChannel implements Channel {
...
public abstract void configureBlocking(boolean block) throws IOException;
public abstract boolean isBlocking();
public abstract Object blockngLock();
...
}

从SelectableChannel的API可以看出,设置或重新设置一个通道的阻塞模式是很简单的,只要调用configureBlocking()方法即可,传递参数值为true则设为阻塞模式。参数值为false,则为非阻塞模式。同时,通过调用isBlocking()方法来判断是否为被阻塞。blockingLock()方法会返回一个不透明的对象引用,返回的对象是通道实现修改阻塞模式时内部使用的,只有拥有此对象的锁的线程才能更改通道的阻塞模式,对于确保在执行代码的关键部分时Socket通道的阻塞模式不会改变以及在不影响其他线程的前提下暂时改变阻塞模式来说,这个方法是非常方便的。

connect()方法

非阻塞模式下调用connect()方法,该方法可能在连接建立完之前就返回,为了确定连接是否建立,调用finishConnect()方法

socketChannel.configureBlocking(false);
socketChannel.connect(new InetSocketAddress("http://xxx.com", ));
while(!socketChannel.finishConnect()){
//some codes 如果没有连接成功则...
}

write()方法

非阻塞模式下,write()方法在尚未写出任何内容时可能就返回了。所以需要在循环中调用write()

read()方法

非阻塞模式下,read()方法在尚未读取到任何数据时可能就返回了。所以需要关注它的int返回值,它会告诉你读取了多少字节

补充: 非阻塞模式与选择器搭配会工作的更好,通过将一或多个SocketChannel注册到Selector,可以询问选择器哪个通道已经准备好了读取,写入等

ServerSocketChannel

Java NIO中的 ServerSocketChannel 是一个可以监听新进来的TCP连接的通道, 就像标准IO中的ServerSocket一样。ServerSocketChannel类在 java.nio.channels包中

ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.socket().bind(new InetSocketAddress(9999));
while(true){
SocketChannel socketChannel = serverChannel.accept();
}

打开ServerSocketChannel

ServerSocketChannel serverChannel = ServerSocketChannel.open();

关闭ServerSocketChannel

serverChannel.close();

监听新进来的连接

通过ServerSocketchannel.accept()方法监听新进来的连接。accept()方法返回一个包含新进来的连接的SocketChannel,因此,accept()方法会一直阻塞到有新连接到达

通常不会仅仅只监听一个连接,在while循环中调用 accept()方法

while(true){
SocketChannel socketChannel = serverSocketChannel.accept();
}

非阻塞模式

ServerSocketChannel可以设置成非阻塞模式。在非阻塞模式下,accept() 方法会立刻返回,如果还没有新进来的连接,返回的将是null。 因此,需要检查返回的SocketChannel是否是null

ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.socket().bind(new InetSocketAddress(9999));
while(true){
SocketChannel socketChannel = serverChannel.accept();
if(socketChannel != null){
//some codes
}
}

Socket通道的服务端程序

public class SocketServer
{
public static void main(String[] args) throws Exception
{
int port = 1234;
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.configureBlocking(false); //非阻塞模式
ServerSocket ss = ssc.socket();
ss.bind(new InetSocketAddress(port));
while (true)
{
SocketChannel sc = ssc.accept();
if (sc == null)
{
// 如果当前没有数据,等待1秒钟再次轮询是否有数据,在学习了Selector之后此处可以使用Selector
Thread.sleep(1000);
}
else {
ByteBuffer bb = ByteBuffer.allocate(100);
sc.read(bb);
bb.flip();
while (bb.hasRemaining()){
System.out.print((char)bb.get());
}
sc.close();
System.exit(0);
}
}
}
}

Socket通道的客戶端程序

public class NonBlockingSocketClient
{
private static final String STR = "Hello World!";
private static final String REMOTE_IP= "127.0.0.1"; public static void main(String[] args) throws Exception {
int port = 1234;
SocketChannel sc = SocketChannel.open();
sc.configureBlocking(false); //非阻塞模式
sc.connect(new InetSocketAddress(REMOTE_IP, port));
while (!sc.finishConnect()){
System.out.println("同" + REMOTE_IP+ "的连接正在建立,请稍等!");
Thread.sleep(10);
}
System.out.println("连接已建立,待写入内容至指定ip+端口!时间为" + System.currentTimeMillis());
ByteBuffer bb = ByteBuffer.allocate(STR.length());
bb.put(STR.getBytes());
bb.flip(); // 写缓冲区的数据之前一定要先反转(flip)
sc.write(bb);
bb.clear();
sc.close();
}
}

DatagramChannel

Java NIO中的DatagramChannel是一个能收发UDP包的通道。因为UDP是无连接的网络协议,所以不能像其它通道那样读取和写入。它发送和接收的是数据包

打开DatagramChannel

DatagramChannel datagramChannel = DatagramChannel.open();
datagramChannel.socket().bind(new InetSocketAddress(8089));

receive()方法

ByteBuffer buf = ByteBuffer.allocate(100);
datagramChannel.receive(buf);

receive()方法会将接收到的数据包内容复制到指定的Buffer. 如果Buffer容不下收到的数据,多出的数据将被丢弃

send()方法

String str = "some codes";
ByteBuffer buf = ByteBuffer.allocate(100);
buf.put(str.getBytes());
buf.flip();
int byt = datagramChannel.send(buf, new InetSocketAddress("xxx.com", 80));

这个例子发送一串字符到”xxx.com”服务器的UDP端口80。 因为服务端并没有监控这个端口,所以什么也不会发生。也不会通知你发出的数据包是否已收到,因为UDP在数据传送方面没有任何保证

连接到特点地址

datagramChannel.connect("xxx.com", 80);

可以将DatagramChannel“连接”到网络中的特定地址的。由于UDP是无连接的,连接到特定地址并不会像TCP通道那样创建一个真正的连接。而是锁住DatagramChannel ,让其只能从特定地址收发数据

当连接后,也可以使用read()和write()方法,就像在用传统的通道一样。只是在数据传送方面没有任何保证

int bytesRead = datagramChannel.read(buf);
int bytesWritten = datagramChannel.write(but);

Java NIO(五)套接字通道的更多相关文章

  1. Java NIO之套接字通道

    1.简介 前面一篇文章讲了文件通道,本文继续来说说另一种类型的通道 -- 套接字通道.在展开说明之前,咱们先来聊聊套接字的由来.套接字即 socket,最早由伯克利大学的研究人员开发,所以经常被称为B ...

  2. java输入输出 -- Java NIO之套接字通道

    一.简介 前面一篇文章讲了文件通道,本文继续来说说另一种类型的通道 – 套接字通道.在展开说明之前,咱们先来聊聊套接字的由来.套接字即 socket,最早由伯克利大学的研究人员开发,所以经常被称为Be ...

  3. Java NIO SocketChannel套接字通道

    原文链接:http://tutorials.jenkov.com/java-nio/socketchannel.html 在Java NIO体系中,SocketChannel是用于TCP网络连接的套接 ...

  4. Java NIO(四)文件通道

    文件通道 通道是访问I/O服务的导管,I/O可以分为广义的两大类:File I/O和Stream I/O.那么相应的,通道也有两种类型,它们是文件(File)通道和套接字(Socket)通道.文件通道 ...

  5. Java网络编程--套接字Socket

    一.套接字Socket IP地址标志Internet上的计算机,端口号标志正在计算机上运行的进程(程序). 端口号被规定为一个16位的0--65535之间的整数,其中,0--1023被预先定义的服务通 ...

  6. Java如何使套接字向单个客户端显示消息?

    在Java编程中,如何使用套接字向单个客户端显示消息? 以下示例演示了如何使用Socket类的ssock.accept()方法向单个套接字客户端上显示消息. package com.yiibai; i ...

  7. Java链接db2套接字出错

    ### Error querying database. Cause: org.springframework.jdbc.CannotGetJdbcConnectionException: Could ...

  8. 网络协议学习笔记(五)套接字Socket

    概述 前面学习网络知识的时候写过一篇关于套接字的随笔见<JAVA SOCKET 详解>,现在本人正在系统的学习网络知识,现在除了温故知新之外,在详细的学习记录一下套接字的知识. Socke ...

  9. Java NIO Channel to Channel Transfers通道传输接口

    原文链接:http://tutorials.jenkov.com/java-nio/channel-to-channel-transfers.html 在Java NIO中如果一个channel是Fi ...

随机推荐

  1. wp8路线跟踪应用源码详细说明

    目录 介绍 应用程序界面 定时执行 定位追踪 设置地图Pitch和Heading属性 后台位置追踪 Live Tile 总结 介绍 我从Windows Phone7 还在测试阶段的时候就开始开发了,所 ...

  2. redis BIO详解

    BIO即background I/O service,后台I/O服务,是redis的aof持久化后台服务. redis把阻塞的同步I/O操作交给后台I/O服务来完成:close和fsync. clos ...

  3. JS 猴子

    公园里有一只猴子和一堆桃子,猴子每天吃掉桃子总数的一半,把剩下一半中扔掉一个坏的. 到第七天的时候,猴子睁开眼发现只剩下一个桃子.问公园里刚开始有多少个桃子? <!DOCTYPE html> ...

  4. 很详尽KMP算法(厉害)

    作者:July时间:最初写于2011年12月,2014年7月21日晚10点 全部删除重写成此文,随后的半个多月不断反复改进.后收录于新书<编程之法:面试和算法心得>第4.4节中. 1. 引 ...

  5. 何为DOM对象

    <html> <head> <title>DOM对象</title> <link href="/Content/Site.css&quo ...

  6. 【UOJ139】【UER #4】被删除的黑白树

    题意: 很久很久以前,有一棵树加入了 UOJ 群. 这天,在它讨论“一棵树应该怎么旋转”的时候一不小心被删除了,变成了被删除的树. 突然间,它突然发现它失去了颜色,变成了一棵纯白的树.这让它感觉很焦躁 ...

  7. javascript事件列表解说

    javascript事件列表解说 事件 浏览器支持 解说 一般事件 onclick IE3.N2 鼠标点击时触发此事件 ondblclick IE4.N4 鼠标双击时触发此事件 onmousedown ...

  8. Android开发人员必备的10 个开发工具

      工欲善其事,必先利其器,Android SDK 本身包含很多帮助开发人员设计.开发.测试和发布 Android 应用的工具,在本文中,我们将讨论 10 个最常用的工具. 1.Eclipse ADT ...

  9. nyoj92-图像有用区域【BFS】

    "ACKing"同学以前做一个图像处理的项目时,遇到了一个问题,他需要摘取出图片中某个黑色线圏成的区域以内的图片,现在请你来帮助他完成第一步,把黑色线圏外的区域全部变为黑色.    ...

  10. Tensorflow 之物体检测

    1)安装Protobuf TensorFlow内部使用Protocol Buffers,物体检测需要特别安装一下. # yum info protobuf protobuf-compiler 2.5. ...