使用Socket
Socket是两台主机之间的一个连接。它可以完成7个基本操作:
- 连接远程机器
- 发送数据
- 接受数据
- 关闭连接
- 绑定端口
- 监听入站数据
- 在绑定端口上接受来自远程机器的连接
Java的Socket类(客户端和服务端都可以使用)提供了前4个操作的方法。后面3个操作仅服务器需要,即等待客户端的连接,这些操作由ServerSocket类实现。
Socket的基本方法
基本构造函数
Socket构造函数指定要连接的主机和端口。这不只是创建这个对象,实际上它会在网络上建立连接。
选择本地接口构造
有两个Socket构造函数前两个参数可以指定要连接的主机和端口,以及后两个参数从哪个接口和端口连接,具体可以查看Java api。
构造但不连接
使用无参数的构造函数创建一个Socket对象,可以以后再为其调用connect()
来建立连接。
设置超时时间
下一步是可选的:使用setSoTimeOut()
方法为连接设置一个超时时间。
返回流
getInputStream()
一旦成功连接了socket,可以调用来返回一个InputStream,用它从socket读取字节。getOutputStream()
同样,使用请求输出流。
Socket地址
SocketAddress抽象类表示一个连接端点。当前只支持TCP/IP Socket。实际使用的Socket地址都是InetSocketAddress的实例。 InetSocketAddress是SocketAddress唯一的子类。
InetSocketAddress()
普通的构造函数InetSocketAddress.createUnresolved()
除了普通的构造函数,还可以使用该静态工厂类方法从而不再在DNS中查找主机。getRemoteSocketAddress()
返回所连接系统的地址。getLocalSocketAddress()
返回发起连接的地址。getAddress()
、getPort()
、getHostName()
可以用来检查这个对象。
代理服务器
构造函数Socket(Proxy proxy)
构造一个未连接的Socket,用来指定一个代理的服务器连接。
一般情况下,Socket使用的代理服务器由socksProxyHost和socksProxyPort系统属性控制,这些属性应用于系统中的所有Scoket。但是构造函数创建的socket会使用指定的代理服务器。最值得一提的是,可以为参数传入Proxy.NO_PROXY,完全绕过所有代理服务器,而直接连接远程主机。
获取Socket的信息
Socket对象有一些属性可以通过获取方法来访问:
getInetAddress()
getPort()
getLocalAddress()
getLocalPort()
如果连接现在是关闭的,这两个方法则给出Socket所连接时所连接的主机和端口。
关闭还是连接
isClosed()
如果socket关闭,该方法会返回true。不过,这不是一个万全的测试。如果Socket从一开始从未连接,也会返回isConnected()
它会指出Socket是否从未连接过一个远程主机。
要查看一个Socket当前是否打开,需要检查两个条件,首先
isConnected()
要返回true,另外isCLosed()
要返回false。
isBound()
它会告诉你Socket是否成功地绑定到本地系统上的出站端口。
Object方法
toString()
返回Socket连接端点信息,不要依赖这个格式,将来格式可能会改变。equals()
和hashCode()
没有覆盖这两个方法。
设置Socket选项
TCP_NODELAY
setTcpNoDelay()
getTcpNoDelay()
设置TCP_NODELAY为true可确保会尽快地发送,而无论包的大小。如果远程系统没有足够快地将确认发送回本系统,那么依赖于小数量信息稳定传输的应用程序会变得很慢。因此在游戏中这个选项很重要。
SO_LINGER
setSoLinger()
getSoLinger()
指定Socket关闭时如何处理尚未发送的数据报。如果打开而且延时设置为任意正数,close()
方法会阻塞,等待发送数据和接收确认。默认情况下,close()
方法将其立即返回,但系统仍会尝试发送剩余的数据,这种情况get函数会返回-1。
SO_TIMEOUT
setSoTimeOut()
getSoTimeOut()
正常情况下,尝试从Socket读取数据时,read()
调用会阻塞尽可能长的时间来得到足够的字节。设置该参数可以确保这次调用阻塞的时间不会超过某个固定的毫秒数。当时间到期时就会抛出一个InterruptedIOException异常,你应当准备好捕获这个异常。不过,Socket仍然是连接的,下次调用可能会成功。0被解释为无限超时。
SO_RCVBUF和SO_SNDBUF
TCP使用缓冲区提升网络性能。较大的缓冲区会提升快速连接的性能,而较慢的连接利用较小的缓冲区会有更好的表现。因此游戏这种小数量传输适合后者。
提示:可以在程序中测量
InetAddress.isReachable()
调用的时间。
setReceiveBufferSize()
getReceiveBufferSize()
setSendBufferSize()
getSendBufferSize()
尽管看起来应该能独立地设置发送和接收缓冲区,但实际上缓冲区通常会设置为二者中较小的一个。而且,其实底层实现完全可以忽略或调整这个建议。不过,大多数情况下,除非网络在某个方向上负载过大,否则默认值就很合适。
SO_KEEPALIVE
setKeepAlive()
getKeepAlive()
如果打开了该选项,客户端偶尔会通过一个空闲连接发送一个数据包(一般两小时一次),确保服务器未崩溃。
OOBINLINE
sendUrgentData()
这个方法几乎会立即发送参数中的最低字节。
默认情况下,Java会忽略从Socket接收的紧急数据。不过,如果你希望接收正常数据中的紧急数据,就需要使用下面的方法设置OOBINLINE选项为true。
setOOBInline()
getOOBInline()
Java不区分紧急数据和非紧急数据,使它不能理性地发挥作用,不过如果有一个特定字节对你的程序有特殊含义,而且从不出现在常规的输入流中,这就能让你更快地发送这个字节。
SO_REUSEADDR
如果开启该选项,就允许另一个Socket绑定到一个刚刚关闭但未释放的端口,即使此时仍有可能存在前一个Socket未接收的数据。这个选项只能在Socket连接之前改变。
setReuseAddress()
getReuseAddress()
IP_TOS服务类型
Java允许你使用下面两个方法检查和设置Socket放在这个IP_TOS字段的8位字段的值:
getTrafficClass()
setTrafficClass()
在21世纪的TCP栈中,这个字节的高6位包含一个差分服务代码点(Differentiated Services Code Point, DSCP)值,低两位包含一个显式拥塞通知(Explicit Congestion Notification,ECN)值。
作为表达偏好的另一种方法,setPerformancePreferences()
方法为连接时间、延迟和带宽指定相对优先性。
Socket异常
见Java API : SocketException