View Javadoc
1   /*
2    * Copyright 2014 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package io.netty.channel.epoll;
17  
18  import io.netty.buffer.ByteBuf;
19  import io.netty.channel.ChannelOutboundBuffer;
20  import io.netty.channel.socket.DatagramPacket;
21  import io.netty.channel.unix.IovArray;
22  import java.net.Inet6Address;
23  import java.net.InetAddress;
24  import java.net.InetSocketAddress;
25  
26  import static io.netty.channel.unix.Limits.UIO_MAX_IOV;
27  import static io.netty.channel.unix.NativeInetAddress.ipv4MappedIpv6Address;
28  
29  /**
30   * Support <a href="http://linux.die.net/man/2/sendmmsg">sendmmsg(...)</a> on linux with GLIBC 2.14+
31   */
32  final class NativeDatagramPacketArray implements ChannelOutboundBuffer.MessageProcessor {
33  
34      // Use UIO_MAX_IOV as this is the maximum number we can write with one sendmmsg(...) call.
35      private final NativeDatagramPacket[] packets = new NativeDatagramPacket[UIO_MAX_IOV];
36      private int count;
37  
38      NativeDatagramPacketArray() {
39          for (int i = 0; i < packets.length; i++) {
40              packets[i] = new NativeDatagramPacket();
41          }
42      }
43  
44      /**
45       * Try to add the given {@link DatagramPacket}. Returns {@code true} on success,
46       * {@code false} otherwise.
47       */
48      boolean add(DatagramPacket packet) {
49          if (count == packets.length) {
50              return false;
51          }
52          ByteBuf content = packet.content();
53          int len = content.readableBytes();
54          if (len == 0) {
55              return true;
56          }
57          NativeDatagramPacket p = packets[count];
58          InetSocketAddress recipient = packet.recipient();
59          if (!p.init(content, recipient)) {
60              return false;
61          }
62  
63          count++;
64          return true;
65      }
66  
67      @Override
68      public boolean processMessage(Object msg) {
69          return msg instanceof DatagramPacket && add((DatagramPacket) msg);
70      }
71  
72      /**
73       * Returns the count
74       */
75      int count() {
76          return count;
77      }
78  
79      /**
80       * Returns an array with {@link #count()} {@link NativeDatagramPacket}s filled.
81       */
82      NativeDatagramPacket[] packets() {
83          return packets;
84      }
85  
86      void clear() {
87          this.count = 0;
88      }
89  
90      void release() {
91          // Release all packets
92          for (NativeDatagramPacket datagramPacket : packets) {
93              datagramPacket.release();
94          }
95      }
96  
97      /**
98       * Used to pass needed data to JNI.
99       */
100     @SuppressWarnings("unused")
101     static final class NativeDatagramPacket {
102         // Each NativeDatagramPackets holds a IovArray which is used for gathering writes.
103         // This is ok as NativeDatagramPacketArray is always obtained from an EpollEventLoop
104         // field so the memory needed is quite small anyway.
105         private final IovArray array = new IovArray();
106 
107         // This is the actual struct iovec*
108         private long memoryAddress;
109         private int count;
110 
111         private byte[] addr;
112         private int scopeId;
113         private int port;
114 
115         private void release() {
116             array.release();
117         }
118 
119         /**
120          * Init this instance and return {@code true} if the init was successful.
121          */
122         private boolean init(ByteBuf buf, InetSocketAddress recipient) {
123             array.clear();
124             if (!array.add(buf)) {
125                 return false;
126             }
127             // always start from offset 0
128             memoryAddress = array.memoryAddress(0);
129             count = array.count();
130 
131             InetAddress address = recipient.getAddress();
132             if (address instanceof Inet6Address) {
133                 addr = address.getAddress();
134                 scopeId = ((Inet6Address) address).getScopeId();
135             } else {
136                 addr = ipv4MappedIpv6Address(address.getAddress());
137                 scopeId = 0;
138             }
139             port = recipient.getPort();
140             return true;
141         }
142     }
143 }