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    *   https://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.Channel;
20  import io.netty.channel.ChannelException;
21  import io.netty.channel.ChannelOutboundBuffer;
22  import io.netty.channel.socket.InternetProtocolFamily;
23  import io.netty.channel.socket.ServerSocketChannel;
24  import io.netty.channel.socket.SocketChannel;
25  import io.netty.channel.socket.SocketProtocolFamily;
26  import io.netty.util.concurrent.GlobalEventExecutor;
27  
28  import java.io.IOException;
29  import java.net.InetAddress;
30  import java.net.InetSocketAddress;
31  import java.net.SocketAddress;
32  import java.util.Collection;
33  import java.util.Collections;
34  import java.util.Map;
35  import java.util.concurrent.Executor;
36  
37  import static io.netty.channel.epoll.LinuxSocket.newSocketStream;
38  import static io.netty.channel.epoll.Native.IS_SUPPORTING_TCP_FASTOPEN_CLIENT;
39  
40  /**
41   * {@link SocketChannel} implementation that uses linux EPOLL Edge-Triggered Mode for
42   * maximal performance.
43   */
44  public final class EpollSocketChannel extends AbstractEpollStreamChannel implements SocketChannel {
45  
46      private final EpollSocketChannelConfig config;
47  
48      private volatile Collection<InetAddress> tcpMd5SigAddresses = Collections.emptyList();
49  
50      public EpollSocketChannel() {
51          super(newSocketStream(), false);
52          config = new EpollSocketChannelConfig(this);
53      }
54  
55      /**
56       *
57       * @deprecated use {@link EpollServerSocketChannel#EpollServerSocketChannel(SocketProtocolFamily)}.
58       */
59      @Deprecated
60      public EpollSocketChannel(InternetProtocolFamily protocol) {
61          super(newSocketStream(protocol), false);
62          config = new EpollSocketChannelConfig(this);
63      }
64  
65      public EpollSocketChannel(SocketProtocolFamily protocol) {
66          super(newSocketStream(protocol), false);
67          config = new EpollSocketChannelConfig(this);
68      }
69  
70      public EpollSocketChannel(int fd) {
71          super(fd);
72          config = new EpollSocketChannelConfig(this);
73      }
74  
75      EpollSocketChannel(LinuxSocket fd, boolean active) {
76          super(fd, active);
77          config = new EpollSocketChannelConfig(this);
78      }
79  
80      EpollSocketChannel(Channel parent, LinuxSocket fd, InetSocketAddress remoteAddress) {
81          super(parent, fd, remoteAddress);
82          config = new EpollSocketChannelConfig(this);
83  
84          if (parent instanceof EpollServerSocketChannel) {
85              tcpMd5SigAddresses = ((EpollServerSocketChannel) parent).tcpMd5SigAddresses();
86          }
87      }
88  
89      /**
90       * Returns the {@code TCP_INFO} for the current socket.
91       * See <a href="https://linux.die.net//man/7/tcp">man 7 tcp</a>.
92       */
93      public EpollTcpInfo tcpInfo() {
94          return tcpInfo(new EpollTcpInfo());
95      }
96  
97      /**
98       * Updates and returns the {@code TCP_INFO} for the current socket.
99       * See <a href="https://linux.die.net//man/7/tcp">man 7 tcp</a>.
100      */
101     public EpollTcpInfo tcpInfo(EpollTcpInfo info) {
102         try {
103             socket.getTcpInfo(info);
104             return info;
105         } catch (IOException e) {
106             throw new ChannelException(e);
107         }
108     }
109 
110     @Override
111     public InetSocketAddress remoteAddress() {
112         return (InetSocketAddress) super.remoteAddress();
113     }
114 
115     @Override
116     public InetSocketAddress localAddress() {
117         return (InetSocketAddress) super.localAddress();
118     }
119 
120     @Override
121     public EpollSocketChannelConfig config() {
122         return config;
123     }
124 
125     @Override
126     public ServerSocketChannel parent() {
127         return (ServerSocketChannel) super.parent();
128     }
129 
130     @Override
131     protected AbstractEpollUnsafe newUnsafe() {
132         return new EpollSocketChannelUnsafe();
133     }
134 
135     @Override
136     boolean doConnect0(SocketAddress remote) throws Exception {
137         if (IS_SUPPORTING_TCP_FASTOPEN_CLIENT && config.isTcpFastOpenConnect()) {
138             ChannelOutboundBuffer outbound = unsafe().outboundBuffer();
139             outbound.addFlush();
140             Object curr;
141             if ((curr = outbound.current()) instanceof ByteBuf) {
142                 ByteBuf initialData = (ByteBuf) curr;
143                 // If no cookie is present, the write fails with EINPROGRESS and this call basically
144                 // becomes a normal async connect. All writes will be sent normally afterwards.
145                 long localFlushedAmount = doWriteOrSendBytes(
146                         initialData, (InetSocketAddress) remote, true);
147                 if (localFlushedAmount > 0) {
148                     // We had a cookie and our fast-open proceeded. Remove written data
149                     // then continue with normal TCP operation.
150                     outbound.removeBytes(localFlushedAmount);
151                     return true;
152                 }
153             }
154         }
155         return super.doConnect0(remote);
156     }
157 
158     private final class EpollSocketChannelUnsafe extends EpollStreamUnsafe {
159         @Override
160         protected Executor prepareToClose() {
161             try {
162                 // Check isOpen() first as otherwise it will throw a RuntimeException
163                 // when call getSoLinger() as the fd is not valid anymore.
164                 if (isOpen() && config().getSoLinger() > 0) {
165                     // We need to cancel this key of the channel so we may not end up in a eventloop spin
166                     // because we try to read or write until the actual close happens which may be later due
167                     // SO_LINGER handling.
168                     // See https://github.com/netty/netty/issues/4449
169                     registration().cancel();
170                     return GlobalEventExecutor.INSTANCE;
171                 }
172             } catch (Throwable ignore) {
173                 // Ignore the error as the underlying channel may be closed in the meantime and so
174                 // getSoLinger() may produce an exception. In this case we just return null.
175                 // See https://github.com/netty/netty/issues/4449
176             }
177             return null;
178         }
179     }
180 
181     void setTcpMd5Sig(Map<InetAddress, byte[]> keys) throws IOException {
182         // Add synchronized as newTcpMp5Sigs might do multiple operations on the socket itself.
183         synchronized (this) {
184             tcpMd5SigAddresses = TcpMd5Util.newTcpMd5Sigs(this, tcpMd5SigAddresses, keys);
185         }
186     }
187 }