一.InetAddress类
表示为互联网协议地址.InetAddress类具有一个缓存,用于存储成功或者不成功的主机名解析.可以通过系统属性"networkaddress.cache.ttl"(time-to-live)来设定.InetAddress有2个子类:Inet4Address和Inet6Address,大部分时候我们无需关注获得InetAddress是何种类型.可以使用instanceof来判断获取的IP地址是何种类型.
InetAddress类中有个内置的Cache内部类,用来缓存"IP信息"(基于Map实现)
- String getHostName():获取此IP地址的主机名,如果此InetAddress是用主机名创建的,将返回主机名.否则将执行反向名称查找并给予系统配置的名称查找服务返回结果.如果有安全管理器且查找不被允许则返回IP地址的文本形式.
- String getCanonicalHostName():获取IP地址的完整限定域名(包括主机所在域信息 + 主机名).如果有安全管理器且查找不被允许则返回IP地址的文本形式
InetAddress inet = InetAddress.getLocalHost(); System.out.println(inet.getCanonicalHostName());//BJXX-LIUGUANQ.360.local System.out.println(inet.getHostName());//BJXX-LIUGUANQ
- String getHostAddress():获取IP地址的文本形式//10.12.124.57
- static InetAddress getByName(String host):根据主机名,获取inetAddress.
- InetAddress.getByName("BJXX-YANGKUN").getHostName();//10.12.144.92
- static InetAddress getLocalHost():获取本机IP地址.
- boolean isMulticastAddress():检测当前地址是否为多播地址,多播地址用于"UDP发送多播数据时"使用,多播地址有严格的协议要求,IPV4多播地址为"224.0.0.0"~"239.255.255.255",IPV6的多播地址为任意FF开头的地址.参见MulticastSocket.
- boolean isAnyLocalAddress():是否为任意本地地址,在Socket编程中,如果尚未制定特定地址,那么此时地址将会被置为0.检测是如果发现地址的值为0,此方法返回true.
- boolean isLookbackAddress():是否为回送地址,IPV4中以"127"开头的地址均为回送地址(即127.0.0.1),IPV6的回送地址为"0:0:0:0:0:0:0:1"(即,::1).
- boolean isLinkLocalAddress():是否为本地连接地址(link-local).IPV4中为169.254.0.1 ~ 169. 254.254.255.IPV6为FE80::/16;link-local地址是一种互联网保护协议地址,只能被用于本地局域网内的通信,link-local的数据包将不会被路由器转发.本地链接地址将有操作系统管理配置(或自动装配),提供了一种在本地局域网中实现P2P通讯的手段.(无线鼠标??)
- boolean isSiteLocalAddress() :是否为"站点本地"地址.IPV4中以"172.16"或者"192.168"开头,是封闭局域网IP,不能直接和开放互联网通信(需要地址转换技术,路由器?).具有开放互联网通信的所有基础..
二.InetSocketAddress类
为Socket链接而设计,为InetAddress + port.
- InetSocketAddress(InetAddress,int port)
- InetSoketAddress(String hostname,int port)
三.NetworkInterface类(基于网络接口):
此类表示一个由名称和分配给此接口的IP地址列表组成的网络接口.它用于标识缴入多播组的本地接口.
- static Enumeration<NetworkInterface> getNetworkInterfaces():返回此机器上的所有网络接口,如果找不到,则返回null.此方法为获取网络接口的入口方法.
- boolean isUp():检测网络接口是否已经开启并可用.
- boolean isLookback()是否为回送接口.
- String getName():获取网络接口的名字.
- Enumeration<InetAddress> getInetAddresses():一个便捷的方法,获取当前网络接口上所有的IP信息.
public static String buildHostKey() throws SocketException,UnknownHostException{ Enumeration<NetworkInterface> netInterfaces = NetworkInterface.getNetworkInterfaces();//一个主机有多个网络接口 while(netInterfaces.hasMoreElements()){ NetworkInterface netInterface = netInterfaces.nextElement(); Enumeration<InetAddress> addresses = netInterface.getInetAddresses();//每个网络接口,都会有多个"网络地址",比如一定会有lookback地址,会有siteLocal地址等.以及IPV4或者IPV6 . while(addresses.hasMoreElements()){ InetAddress address = addresses.nextElement(); if(address.isSiteLocalAddress() && !address.isLoopbackAddress()){ return address.getHostAddress(); } } } return null; }
一个物理机器会有多个NetworkInterfaces(网络接口,可以简单认为"网卡"),每个Interfaces都可以创建Socket(套接字),Socket是网络两端的program
接收和发送数据的桥梁.Socket的创建基于Interfaces驱动器和OS共同的调度而生成.
Program如果想发送数据,则必须和本机Socket建立连接,然后socket负责将数据通过"网络"传递给远端.
四.DatagramPacket(数据包):
数据包用来实现无连接包投送服务.每条保温仅根据包所提供的信息从一个机器路由到另一台机器.packet将不会被确保发送的顺序,也不对发送做确保.
- DatagramPacket(byte[],int length,InetAddress)
- DatagramPacket(byte[],int length,SocketAddress):指定发送数据字节数组和需要发送的数据长度,同时指定IP信息.
五.DatagramSocket类(数据包套接字):单播方式下的UDP
发送Datagram的套接字,每个packet在套接字上发送时都会被单独编址和路由,即从一个机器到另一台机器的packet,可能经由不同的路由方式,因此pakcet发送的顺序和到达时间并不能得到保证,因为采取UDP无连接方式发送,也不能确保消息一定会被接收.
为了接收广播包,应该讲DatagramSocket绑定到通配符地址(多播地址等),在某些实现中,将DatagramSoket绑定到一个具体的地址时也可以接收到广播的packet.
InetAddress address = ...
DatagramSocket socket = new DatagramSocket(address);
///等效于
///DatagramSocket socket = new DatagramSocket(null);socket.bind(address);
- DatagramSocket()
- DatagramSocket(int port)
- DatagramSocket(int port,InetAddress)
- DatagramSocket(SocketAddress address)
- void bind(SocketAddress address):侦听address,等待接收数据.将当前DatagramSocket绑定在指定的InetSocketAddress上.如果address为null,将会系统将选择一个临时的端口和本地地址.如果当前Socket已经关闭,则抛出SocketException.如果当前Socket已经执行bind操作,再次bind将抛出异常.bind操作将触发底层的网络连接和检测.如果出现异常,将会被抛出.作为Server端的程序,可以使用bind方法绑定侦听当前机器的端口通信.
- public void connect(InetAddress address):建立Socket连接发送数据(建立program到Socket的连接)和远程address建立连接.此方法可能被阻塞,直到建立连接.如果address不可达或者连接失败,将抛出异常.如果connect(address)执行成功,那么socket实例将会保存address作为接下来所有packet发送的"目的地",因为DatagramPacket也可以指定(大部分情况下,是通过packet指定发送的目的地),如果在send(packet)是,packet指定的address为null,则使用connect方法指定的address;如果packet的address和connect方法指定的address不同,将导致数据包无法发送而抛出"address diff"异常.Socke实例会保存自己的连接状态(持有connectState属性)(NOT_CONNECTED,CONNECTED,CONNECTED_NO_IMPL),
- public void disconnect(): 断开Socket连接,此方法也是只有DatagramSocket具有.断开套接字连接,连接断开后,相应的资源会被释放.如果当前socket已经close,将此操作将无效.断开连接,将导致当前socket处于"NOT_CONNECTED"状态.socket在"断开连接"之后,仍然可以重新bind()或者connect().{可以切换数据包地址等},如果disconnect之后,发送数据,只会发给本地侦听的socket.如果disconnect之后,接收数据,只能接收本地socket发送的数据包.{区别与Socket类}
- public boolean isBound(): 检测Socket绑定状态(如果已经bind()或者connect()且成功时,返回true),如果尚未与Socket建立绑定,则返回false.
- public boolean isConnected(): 是否已经建立连接,如果connect()执行成功则会修改DatagramSocket的connectState属性.
- public SocketAddress getRemoteSocketAddress(): 返回远端地址信息.
- public SocketAddress getLocalSocketAddress(): 返回绑定的本地Socket地址信息.
- public void send(DatagramPacket p): 经由套接字发送数据包,packet中需要包含发送的数据/数据长度/远程主机信息等.此方法体中使用synchronized(p)区块,因此对于DatagramSocket实例而言,发送数据是线程安全的.如果当前socket已经"关闭(closed)",将抛出异常.
- public void receive(DatagramPacket p):经由socket获取数据包,此方法返回后,packet已经被封装完成.此方法为线程安全.此方法会阻塞,直到接收到packet为止.如果指定了SO_TIMEOUT选项,那么将至少阻塞timeout时间后,返回.此后数据包中也将包含发送方的IP和端口号.如果执行了connect(address)方法,那么receive(packet)方法也将接收到来自其他address的数据包(非connect方法指定的地址),不过receive方法把这些数据包给忽略(读取之后,直接抛弃).
////////////源码 while (!stop) { // peek at the packet to see who it is from. InetAddress peekAddress = new InetAddress(); int peekPort = getImpl().peek(peekAddress); if ((!connectedAddress.equals(peekAddress)) ||(connectedPort != peekPort)) { // throw the packet away and silently continue DatagramPacket tmp = new DatagramPacket(new byte[1], 1); getImpl().receive(tmp); } else { stop = true; } }
- public void setSoTimeout(int timeout):设置socket-operation超时时间.即对DatagramSocket在receive()阻塞的最大时间.如果超时,receive()将抛出SocketTimeoutException,不过Socket仍然有效.需要在bind()和connect()之前设置此参数.
- public void setSendBufferSize(int size):
- public void setReceiveBufferSize(int size):设置数据发送和接收的缓冲区大小.此值为建议值(单位:字节),具体底层将会分配多少可以调用其getReceive/SendBufferSize来检测.参数需要在Socket连接创建之前设置(bind,connect).如果底层不支持对缓冲区的重置,将会抛出SocketException.sendBufferSize将重置Socket中SO_SNDBUF选项的值,增大此值将会导致更多的包发送时队列化.如果实际发送的数据超过了bufferSize,此包是否会被发送或者丢弃,在不同平台实现上有差异.选择合适的SO_SNDBUF有助于提高性能.receiveBufferSize将重置Socket中SO_RCVBUF选项的值.适当增加sendBufferSize或者receiveBufferSize,可以有效的减少socket通讯交互的次数.对于数据包较小但是发送频率较高的,可以减小bufferSize,来减少"延迟"(游戏等对延迟有要求的);如果数据包较大发送频率较低,可以增大bufferSize,来减少网络通讯次数从而减少整体的传输时间,增加传输效率.sendBufferSize和receiveBufferSize需要配合使用.
- public void setReuseAddress(boolean on):设置地址是否可以重用.设置SO_REUSEADDR选项.如果socket关闭,如果在此socket绑定的端口上仍然有数据传输,那么此port将不能被立即释放,即如果此时新建socket并绑定到此端口将会导致"address already bound"异常.设置SO_REUSEADDR为true,允许在旧socket关闭后,新的socket可以立即bind到相同port上.