serversocket channel

ServerSocketChannel类
获得ServerSocketChannel与ServerSocket对象
ServerSocketChannel类是抽象的,并不能直接new实例化,但API中提供了public static ServerSocketChannel open()方法来创建ServerSocketChannel类的实例。open() 方法是静态的,作用是打开服务器套接.字通道。新通道的套接字最初是未绑定的;可以接受连接之前,必须通过它的某个套接字的bind()方法将其绑定到具体的地址。

通过调用open()方法创建ServerSocketChannel类的实例后,可以调用它的public abstract ServerSocket socket()方法来返回ServerSocket类的对象,然后与客户端套接字进行通信。socket()方法的作用是获取与此通道关联的服务器套接字ServerSocket类的对象。

public final void close()方法的作用是关闭此通道。如果已关闭该通道,则此方法立即返回。否则,它会将该通道标记为已关闭,然后调用implCloseChannel()方法以完成关闭操作。

public static void main(String[] args) {
try {
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
ServerSocket serverSocket = serverSocketChannel.socket();
serverSocket.bind(new InetSocketAddress(“localhost”, 8888));
Socket socket = serverSocket.accept();
InputStream inputStream = socket.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
char[] charArray = new char[1024];
int readLength = inputStreamReader.read(charArray);
while (readLength != -1) {
System.out.println(new String(charArray, 0, readLength));
readLength = inputStreamReader.read(charArray);
}
inputStreamReader.close();
inputStream.close();
socket.close();
serverSocket.close();
serverSocketChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
执行绑定操作
serverSocket.bind()方法将ServerSocket类绑定到指定的地址,而ServerSocketChannel类也有bind() 方法,该方法public final ServerSocketChannel bind(SocketAddress local)的作用是将通道的套接字绑定到本地地址并侦听连接。

public abstract ServerSocketChannel bind(SocketAddress local; int backlog)方法的作用是将通道的套接字绑定到本地地址并侦听连接,通过使用参数backlog来限制客户端连接的数量。

阻塞与非阻塞以及accept()方法的使用效果
public abstract Socket Channel accept()方法的作用是接受此通道套接字的连接。如果此通道处于非阻塞模式,那么在不存在挂起的连接时,此方法将直接返回nul否则,在新的连接可用或者发生I/O错误之前会无限期地阻塞它。无论此通道的阻塞模式如何,此方法返回的套接字通道(如果有)将处于阻塞模式。

调用 ServerSocketChannel的 public final Selectable Channel configure Blocking( boolean block)方法即可。 public final Selectable Channel configure Blocking( boolean block)方法的作用是调整此通道的阻塞模式,传入tue是阻塞模式,传人 false是非阻塞模式。

阻塞模式:

try {
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
System.out.println(“serverSocketChannel.isBlocking()=” + serverSocketChannel.isBlocking());
serverSocketChannel.bind(new InetSocketAddress(“localhost”, 8888));
System.out.println("begin " + System.currentTimeMillis());
SocketChannel socketChannel = serverSocketChannel.accept();
System.out.println("end " + System.currentTimeMillis());
socketChannel.close();
serverSocketChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
1
2
3
4
5
6
7
8
9
10
11
12
输出:

serverSocketChannel.isBlocking()=true
begin 1591676749339
1
2
非阻塞模式:

try {
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
System.out.println(“serverSocketChannel.isBlocking()=” + serverSocketChannel.isBlocking());
serverSocketChannel.configureBlocking(false);
System.out.println(“serverSocketChannel.isBlocking()=” + serverSocketChannel.isBlocking());
serverSocketChannel.bind(new InetSocketAddress(“localhost”, 8888));
System.out.println("begin " + System.currentTimeMillis());
SocketChannel socketChannel = serverSocketChannel.accept();
System.out.println("end " + System.currentTimeMillis());
socketChannel.close();
serverSocketChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

注意:非阻塞模式下,accept()方法在没有客户端连接时,返回null值。
使用 Server Socketchannel类的 accepto方法的优势是返回1个 Socketchannel通道,此通道是 Selectablechannel(可选择通道)的子类,可以把这个 Socketchannel通道注册到选择器中实现I/O多路复用,另外, Socketchannel通道使用缓冲区进行数据的读取操作。

获得Selector对象
由于Selector类是抽象类,需要调用 open方
法获得 Selector对象。

Selector类的public static Selector open方法的作用是打开1个选择器,使 Selectablechannel能将自身注册到这个选择器上。

执行注册操作与获得SelectionKey对象
Selectable Channel类的public final SelectionKey register( Selector sel, Int ops)方法的作用是向给定的选择器注册此通道,返回一个选择键( SelectionKey)。

参数sel代表要向其注册此通道的选择器,参数ops代表 register方法的返回值 SelectionKey的可用操作集,操作集是在 SelectionKey类中以常量的形式进行提供的:

OP_ACCEPT:用于套接字接受操作的操作集位
OP_CONNECT:用于套接字连接操作的操作集位
OP_READ:用于读取操作的操作集位
OP_WRITE:用于写入操作的操作集位
try {
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
//必须将ServerSocketChannel设置成非阻塞模式,否则抛出IllegalBlockingModeException
serverSocketChannel.configureBlocking(false);
ServerSocket serverSocket = serverSocketChannel.socket();
serverSocket.bind(new InetSocketAddress(“localhost”, 8888));

        //开始
        Selector selector = Selector.open();
        SelectionKey key = serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        //结束
        System.out.println("selector=" + selector);
        System.out.println("key= " + key);
        serverSocket.close();
        serverSocketChannel.close();
    } catch (IOException e) {
        e.printStackTrace();
    }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
selector=sun.nio.ch.KQueueSelectorImpl@511d50c0
key= sun.nio.ch.SelectionKeyImpl@610455d6
1
2
判断注册的状态
Selectablechannel类的 public final boolean isRegisteredo方法的作用是判断此通道当前是否已向任何选择器进行了注册。新创建的通道总是未注册的。由于对 SelectionKey执行取消操作和通道进行注销之间有延迟,因此在已取消某个通道的所有 SelectionKey后,该通道可能在定时间内还会保持已注册状态。关闭通道后,该通道可能在一定时间内还会保持已注册状态。

将通道设置成非阻塞模式再注册到选择器
try {
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.bind(new InetSocketAddress(“localhost”, 8888));

        Selector selector = Selector.open();
        System.out.println("Selector=" + selector);
        System.out.println("A serverSocketChannel1.isRegistered()=" + serverSocketChannel.isRegistered());
        SelectionKey selectionKey = serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        System.out.println("B serverSocketChannel1.isRegistered()=" + serverSocketChannel.isRegistered());
        serverSocketChannel.close();
    } catch (IOException e) {
        e.printStackTrace();
    }

1
2
3
4
5
6
7
8
9
10
11
12
13
register()方法的第二个参数OP_ACCEPT代表检测此通道套接字的连接。

出现异常的原因是没有将通道设置成非阻塞模式。如果想把通道注册到选择器中,就必须将通道设置成非阻塞模式。

使用configureBlocking(false)方法解决异常
public final Selectable Channel configure Blocking( boolean block()方法的作用是调整此通道的阻塞模式。如果向一个或多个选择器注册了此通道,则尝试将此通道置于阻塞模式将导致抛出 IllegalBlocking Mode Exception。可在任意时间调用此方法。

新的阻塞模式仅影响在此方法返回后发起的IO操作。对于某些实现,这可能需要阻塞,直到所有挂起的IO操作已完成。如果调用此方法的同时正在进行另一个此方法或register(方法的调用,则在另个操作完成前将首先阻塞该调用。
public final boolean is Blocking方法的作用是判断此通道上的每个I/O操作在完成前是否被阻塞。新创建的通道总是处于阻塞模式。如果此通道已关闭,则此方法返回的值是未指
定的。

判断打开的状态
public final boolean isOpen方法的作用是判断此通道是否处于打开状态。

获得阻塞对象
public final Object blocking Lock()方法的作用是获取其 configure Blocking和 register方法实现同步的对象,防止重复注册。

该方法的源代码如下:

private final Object regLock = new Obect();
public final Object blockingLock() {
return regLock;
}
1
2
3
4
获得支持的SocketOption列表
Set<SocketOption<?> supportedOptionso方法的作用是返回通道支持的 Socket Option。

Thread t = new Thread() {
@Override
public void run() {
try {
Thread.sleep(2000);
Socket socket = new Socket(“localhost”, 8888);
socket.close();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
};
t.start();
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.bind(new InetSocketAddress(“localhost”, 8888));
SocketChannel socketChannel = serverSocketChannel.accept();

    Set<SocketOption<?>> set1 = serverSocketChannel.supportedOptions();
    Set<SocketOption<?>> set2 = socketChannel.supportedOptions();
    Iterator<SocketOption<?>> it1 = set1.iterator();
    Iterator<SocketOption<?>> it2 = set2.iterator();

    System.out.println("ServerSocketChannel supportedOptions:");
    while (it1.hasNext()) {
        SocketOption<?> next = it1.next();
        System.out.println("name:" + next.getClass().getName());
    }

    System.out.println();
    System.out.println();

    System.out.println("SocketChannel supportedOptions:");
    while (it2.hasNext()) {
        SocketOption<?> next = it2.next();
        System.out.println("name:" + next.getClass().getName());
    }

    socketChannel.close();
    serverSocketChannel.close();
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
ServerSocketChannel supportedOptions:
name:java.net.StandardSocketOptionsStdSocketOptionname:java.net.StandardSocketOptionsStdSocketOption name:java.net.StandardSocketOptionsStdSocketOptionname:java.net.StandardSocketOptionsStdSocketOption
name:java.net.StandardSocketOptions$StdSocketOption

SocketChannel supportedOptions:
name:java.net.StandardSocketOptionsStdSocketOptionname:java.net.StandardSocketOptionsStdSocketOption name:java.net.StandardSocketOptionsStdSocketOptionname:java.net.StandardSocketOptionsStdSocketOption
name:java.net.StandardSocketOptionsStdSocketOptionname:java.net.StandardSocketOptionsStdSocketOption name:java.net.StandardSocketOptionsStdSocketOptionname:java.net.StandardSocketOptionsStdSocketOption
name:java.net.StandardSocketOptions$StdSocketOption
name:sun.nio.ch.ExtendedSocketOption1name:java.net.StandardSocketOptions1 name:java.net.StandardSocketOptions1name:java.net.StandardSocketOptionsStdSocketOption
name:java.net.StandardSocketOptions$StdSocketOption
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
获得与设置SocketOption
public abstract ServerSocket Channel setOption(SocketOption name, T value)的作用是设置 Socket Option值。

T getOption( SocketOptionname)方法的作用是获取 Socket Option值。

try {
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
//通道支持什么,Socket Option就只能设置什么,设置其他的Option就会出现异常
System.out.println(“A SO_REVBUF=” + serverSocketChannel.getOption(StandardSocketOptions.SO_RCVBUF));
serverSocketChannel.setOption(StandardSocketOptions.SO_RCVBUF, 5678);
System.out.println(“B SO_REVBUF=” + serverSocketChannel.getOption(StandardSocketOptions.SO_RCVBUF));
serverSocketChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
1
2
3
4
5
6
7
8
9
10
A SO_REVBUF=131072
B SO_REVBUF=5678
1
2
获得SocketAddress对象
public abstract SocketAddress getLocalAddresso方法的作用是获取绑定的 Socketaddress对象。

阻塞模式的判断
public final boolean is Blocking方法的作用是判断此通道上的每个IO操作在完成前是否被阻塞。新创建的通道总是处于阻塞模式。如果此通道已关闭,则此方法返回的值是未指
定的。返回值代表当且仅当此通道处于阻塞模式时才返回true。

根据Selector找到对应的SelectionKey
public final SelectionKey keyFor( Selector sel)方法的作用是获取通道向给定选择器注册的 SelectionKeyo同一个 Selectable channel通道可以注册到不同的选择器对象,然后返回新创建的 SelectionKey对象,可以使用 public final SelectionKey keyFor( Selector sel)方法来取得当前通道注册在指定选择器上的 SelectionKey对象。

try {
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.bind(new InetSocketAddress(“localhost”, 8888));
serverSocketChannel.configureBlocking(false);
Selector selector = Selector.open();
SelectionKey selectionKey = serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println(“A=” + selectionKey.hashCode());
SelectionKey selectionKey1 = serverSocketChannel.keyFor(selector);
System.out.println(“B=” + selectionKey1.hashCode());
serverSocketChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
A=1627674070
B=1627674070
1
2
获得SelectorProvider对象
public final Selector Provider provider()方法的作
用是返回创建此通道的 Selector Provider。

Selector Provider类的作用是用于选择器和可选择通道的服务提供者类。选择器提供者的实现类是 Selector Provider类的一个子类,它具有零参数的构造方法,并实现了抽象方法。给定的对Java虚拟机的调用维护了单个系统级的默认提供者实例,它由 provider方法返回。

在第一次调用该方法时,将查找以下指定的默认提供者。系统级的默认提供者由 DatagramChannel、 Pipe、Selector、 Server SocketChannel和 Socketchannel类的静态 open方法使用。

除了默认提供者之外,程序还可以使用其他提供者,方法是通过实例化一个提供者,然后直接调用此类中定义的 open方法。多个并发线程可安全地使用此类中的所有方法。

SelectorProvider provider = SelectorProvider.provider();
System.out.println(provider);

    ServerSocketChannel serverSocketChannel = null;
    serverSocketChannel = ServerSocketChannel.open();
    SelectorProvider provider1 = serverSocketChannel.provider();
    System.out.println(provider1);
    serverSocketChannel.close();

1
2
3
4
5
6
7
8
//同一个实例
sun.nio.ch.KQueueSelectorProvider@61bbe9ba
sun.nio.ch.KQueueSelectorProvider@61bbe9ba
1
2
3
通道注册与选择器

  1. 相同的通道可以注册到不同的选择器,返回的SelectionKey不是同一个对象:

//1. 相同的通道可以注册到不同的选择器,返回的SelectionKey不是同一个对象
try {
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.bind(new InetSocketAddress(“localhost”, 8888));

        serverSocketChannel.configureBlocking(false);

        Selector selector1 = Selector.open();
        Selector selector2 = Selector.open();

        SelectionKey selectionKey1 = serverSocketChannel.register(selector1, SelectionKey.OP_ACCEPT);
        SelectionKey selectionKey2 = serverSocketChannel.register(selector2, SelectionKey.OP_ACCEPT);
        System.out.println("selectionKey1=" + selectionKey1);
        System.out.println("selectionKey2=" + selectionKey2);
        serverSocketChannel.close();
    } catch (IOException e) {
        e.printStackTrace();
    }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//不同的实例
selectionKey1=sun.nio.ch.SelectionKeyImpl@610455d6
selectionKey2=sun.nio.ch.SelectionKeyImpl@511d50c0
1
2
3
2. 不同的通道注册到相同的选择器返回的SelectionKey不是同一个对象:

  1. 不同的通道注册到不同的选择器,返回的SelectionKey不是同一个对象

  2. 相同的通道重复注册相同的选择器,返回的SelectionKey是同一个对象

返回此通道所支持的操作
public final int validOps()方法的作用是返回一个操作集,标识此通道所支持的操作。因为服务器套接字通道仅支持接受新的连接,所以此方法返回 SelectionKey. OP ACCEPT。

try {
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
SocketChannel socketChannel = SocketChannel.open();

        int value1 = serverSocketChannel.validOps();
        int value2 = socketChannel.validOps();

        System.out.println("value1=" + value1);
        System.out.println("value2=" + value2);

        System.out.println();

        //ServerSocketChannel只支持OP_ACCEPT
        System.out.println(SelectionKey.OP_ACCEPT & ~serverSocketChannel.validOps());
        System.out.println(SelectionKey.OP_CONNECT & ~serverSocketChannel.validOps());
        System.out.println(SelectionKey.OP_READ & ~serverSocketChannel.validOps());
        System.out.println(SelectionKey.OP_WRITE & ~serverSocketChannel.validOps());
        System.out.println();
        //SocketChannel支持OP_CONNECT、OP_READ、OP_WRITE
        System.out.println(SelectionKey.OP_ACCEPT & ~socketChannel.validOps());
        System.out.println(SelectionKey.OP_CONNECT & ~socketChannel.validOps());
        System.out.println(SelectionKey.OP_READ & ~socketChannel.validOps());
        System.out.println(SelectionKey.OP_WRITE & ~socketChannel.validOps());

        socketChannel.close();
        serverSocketChannel.close();
    } catch (IOException e) {
        e.printStackTrace();
    }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//输出为0,就说明支持哪个选项:
value1=16
value2=13

0
8
1
4

16
0
0
0
————————————————
版权声明:本文为CSDN博主「吴声子夜歌」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/cold___play/article/details/106632505

### 如何在编程或网络配置中添加 Channel 通道 #### 在 Java 中使用 JDK NIO 添加 Channel Java 的 `java.nio.channels` 提供了多种类型的通道用于文件、套接字和其他 I/O 资源的操作。要创建一个基于 NIO 的 Socket 或 ServerSocket 类型的 Channel,可以按照以下方法: 对于客户端通道: ```java import java.nio.channels.SocketChannel; import java.net.InetSocketAddress; // 创建并连接到指定地址的 SocketChannel 实例 SocketChannel socketChannel = SocketChannel.open(); socketChannel.connect(new InetSocketAddress("localhost", 8080)); ``` 对于服务端监听通道: ```java import java.nio.channels.ServerSocketChannel; import java.io.IOException; // 打开一个新的 ServerSocketChannel 并绑定到本地端口 ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); serverSocketChannel.bind(new InetSocketAddress(8080)); ``` 上述代码展示了如何分别创建客户端和服务器端的 NIO 套接字通道[^1]。 #### 使用 Netty 框架自定义 Channel Netty 是一种高性能的异步事件驱动框架,广泛应用于网络应用开发。以下是创建 Netty 客户端和服务端 Channel 的基本流程: 对于客户端: ```java import io.netty.bootstrap.Bootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioSocketChannel; public class NettyClient { public static void main(String[] args) throws InterruptedException { EventLoopGroup group = new NioEventLoopGroup(); try { Bootstrap bootstrap = new Bootstrap() .group(group) .channel(NioSocketChannel.class) .remoteAddress("localhost", 8080); ChannelFuture future = bootstrap.connect().sync(); future.channel().closeFuture().sync(); // 阻塞直到关闭 } finally { group.shutdownGracefully(); } } } ``` 对于服务端: ```java import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; public class NettyServer { public static void main(String[] args) throws Exception { EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap() .group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .childHandler(null); // 设置处理器链路 ChannelFuture f = b.bind(8080).sync(); f.channel().closeFuture().sync(); } finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } } ``` 以上代码片段说明了如何利用 Netty 来初始化客户端和服务端的 NIO 通道。 #### Go 编程语言中的 Goroutines 和 Channels Go 语言提供了轻量级线程 goroutine 及其通信机制 channel。下面是如何声明和使用 channel 进行数据交换的例子: 创建无缓冲区 channel: ```go ch := make(chan int) func sendData(ch chan<- int) { ch <- 42 // 发送整数至 channel } func receiveData(ch <-chan int) { data := <-ch // 接收来自 channel 的数据 fmt.Println(data) } go sendData(ch) receiveData(ch) ``` 带缓冲区的 channel 则可以通过参数设置大小: ```go bufferedCh := make(chan string, 3) // 创建容量为3的字符串类型 channel bufferedCh <- "hello" fmt.Println(<- bufferedCh) // 输出 hello ``` 这些示例演示了如何在 Go 中通过 channels 协调不同 goroutines 的执行流[^2]。 #### Alpha 通道 (Alpha Channel) 处理 在图形渲染领域,alpha 通道通常用来表示图像像素的透明度级别。如果要在 DirectX 渲染管线中启用 alpha 测试或者混合功能,则需调整相关状态对象设置如下所示: ```cpp ID3D11DeviceContext* context; // 设备上下文指针假设已获取 float blendFactor[4] = { 0.f, 0.f, 0.f, 0.f }; UINT sampleMask = 0xffffffff; context->OMSetBlendState(alphaBlendState.Get(), blendFactor, sampleMask); ``` 这里假定 `alphaBlendState` 已经被正确构建好以支持 alpha 合成效果[^4]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值