`
QING____
  • 浏览: 2232602 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

JAVA-SOCKET编程

    博客分类:
  • JAVA
 
阅读更多

 

一.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.

  1.  InetSocketAddress(InetAddress,int port)
  2.  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将不会被确保发送的顺序,也不对发送做确保.

  1. DatagramPacket(byte[],int length,InetAddress)
  2. 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);

 

  1. DatagramSocket()
  2. DatagramSocket(int port)
  3. DatagramSocket(int port,InetAddress)
  4. 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上.

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics