# 04-netty-socketaddress

## 11. netty系列之:netty中SocketAddress详解

## 简介

Socket是一种建立客户端和服务器端连接的方式，这种方式在netty中尤其常见，在socket中客户端和服务器端需要一种机制来确定如何连接到对方，这种机制就是SocketAddress。

今天我们来详细讲解一下SocketAddress在netty中的实现。

## SocketAddress

SocketAddress听名字就知道它是一个地址，事实上它是在JDK中定义的，我们来看下它的具体定义：

```
public abstract class SocketAddress implements java.io.Serializable {

    static final long serialVersionUID = 5215720748342549866L;

}
```

SocketAddress在java.net包中，表示的是一个网络地址。

但是我们检查代码可以看到，SocketAddress中并没有任何内容，它的所有实现都在它的子类中，那么SocketAddress到底有那些实现类呢？

我们以一个图来观察一下，SocketAddress和它的子类实现：

![](https://img-blog.csdnimg.cn/cf8b4415dfeb4d25a1c4249abf84c828.png)

可以看到，SocketAddress继承自Serializable，为什么要继承自Serializable呢？很明显SocketAddress要在网络中进行传输，所以必须要进行序列化。

SocketAddress有4个直接实现的子类分别是LocalAddress,EmbeddedSocketAddress,DomainSocketAddress,InetSocketAddress，还有两个间接实现的子类,分别是DatagramSocketAddress和DomainDatagramSocketAddress。

我们一起来看一下他们到底是什么用的。

## LocalAddress

首先是LocalAddress，LocalAddress表示的是本地的数据传输地址。localAddress中有两个属性：

```
    private final String id;
    private final String strVal;
```

这两个属性是从netty的channel中得到的，我们看下他们的实现逻辑：

```
    LocalAddress(Channel channel) {
        StringBuilder buf = new StringBuilder(16);
        buf.append("local:E");
        buf.append(Long.toHexString(channel.hashCode() & 0xFFFFFFFFL | 0x100000000L));
        buf.setCharAt(7, ':');
        id = buf.substring(6);
        strVal = buf.toString();
    }
```

可以看出，LocalAddress的id是依赖于channel的hashCode来生成，然后进行拼接而成的，拼接后的StringBuilder中的前6个字符就是id，整个StringBuilder的值就是strVal。

而这个ID的作用就是用判断不同的LocalAddress是否相同：

```
    public int compareTo(LocalAddress o) {
        return id.compareTo(o.id);
    }
```

可以看到因为LocalAddress并没有涉及到IP地址，所以它只能在本地使用。

## EmbeddedSocketAddress

先来看下这个类的定义：

```
final class EmbeddedSocketAddress extends SocketAddress {
    private static final long serialVersionUID = 1400788804624980619L;

    @Override
    public String toString() {
        return "embedded";
    }
}
```

可以看到它自定义了一个toString方法，所以这个类的用处不对，主要使用在EmbeddedChannel中,表示嵌套的channel中的地址。

因为channel是嵌套的，所以它的LOCAL\_ADDRESS和REMOTE\_ADDRESS实际上是不存在的。

## InetSocketAddress

这个类应该是我们最常用到的socketAddress，因为它代表的是一个IP Socket Address,也就是IP address + port number, 这个IP地址也可以用hostname来替代。

InetSocketAddress中只有一个InetSocketAddressHolder类型的属性holder,而这个holder事实上是一个内部类，它定义了三个属性，分别是：

```
        private String hostname;

        private InetAddress addr;

        private int port;
```

这三个属性正好对于了我们之前降到的IP socket address的几个特征。

其中InetAddress表示的是一个ip地址。

对于域名来说，有个解析的过程，所以InetSocketAddress提供了一个isUnresolved的方法判断。

## DatagramSocketAddress

DatagramSocketAddress是InetSocketAddress的子类，表示的是使用UDP协议的地址。

UDP Socket在JDK中有个专门的类叫做DatagramSocket，DatagramSocketAddress就是对应DatagramSocket的地址。

还记得UDP有什么特征吗？UDP和TCP相比的最大特点就是UDP非常简单，它自管发送消息就行了，不需要知道消息是否被收到。所以对于UDP来说，知道要接受的地址就够了，这个地址就是DatagramSocketAddress。

可以看到DatagramSocketAddress中有一个receivedAmount属性：

```
    private final int receivedAmount;
```

receivedAmount表示的是这个地址接收到的bytes数目。

注意这个类只在netty内部使用，主要用在Socket类中的两个方法，从ByteBuffer和memoryAddress中创建DatagramSocketAddress：

```
    public final DatagramSocketAddress recvFrom(ByteBuffer buf, int pos, int limit) throws IOException {
        return recvFrom(fd, buf, pos, limit);
    }

    public final DatagramSocketAddress recvFromAddress(long memoryAddress, int pos, int limit) throws IOException {
        return recvFromAddress(fd, memoryAddress, pos, limit);
    }
```

## DomainSocketAddress

一般来说Socket Address需要一个IP地址，用来寻找到目标地址，但是对于UNIX来说，一切都是文件，为了提升UNIX平台中的数据传输效率，UNIX平台引入了一个新的数据在不同进程中交换的方法，这个方法就叫做Unix domain socket或者IPC socket。

DomainSocketAddress不是用IP进行传输的，而是用UNIX文件进行数据交换。

从类的实现来说，DomainSocketAddress中定义了一个socketPath的字符串，表示数据沟通的文件路径,也就是socketPath：

```
    public DomainSocketAddress(String socketPath) {
        this.socketPath = ObjectUtil.checkNotNull(socketPath, "socketPath");
    }
```

那么如何表明两个DomainSocketAddress是同一个address呢?

```
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (!(o instanceof DomainSocketAddress)) {
            return false;
        }

        return ((DomainSocketAddress) o).socketPath.equals(socketPath);
    }
```

可以看到，只要是socketPath相同，那么这两个DomainSocketAddress就是相同的。

## DomainDatagramSocketAddress

最后一个要讲的类是DomainDatagramSocketAddress，从名字可以看出，它是使用IPC来进行沟通的DatagramSocketAddress。

同样的，这个类中也有一个receivedAmount，表示这个地址接收到的字节数：

```
private final int receivedAmount;
```

和DatagramSocketAddress一样，DomainDatagramSocketAddress也是一个内部使用的类。它也是用在Socket类中，用来从buf和memoryAddress中返回一个DomainDatagramSocketAddress：

```
    public final DomainDatagramSocketAddress recvFromDomainSocket(ByteBuffer buf, int pos, int limit)
            throws IOException {
        return recvFromDomainSocket(fd, buf, pos, limit);
    }

    public final DomainDatagramSocketAddress recvFromAddressDomainSocket(long memoryAddress, int pos, int limit)
            throws IOException {
        return recvFromAddressDomainSocket(fd, memoryAddress, pos, limit);
```

这里的Socket是一个JNI的bridge类，上面所提到的方法实际上都调用的是native方法。

## 总结

以上就是SocketAddress和它的各种address实现，当然我们最常用到的还是InetSocketAddress这个以IP为基础的Socket Address.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.flydean.com/www.flydean.com/docs/java/netty/04-netty-socketaddress.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
