Java网络编程学习笔记(五)客户端Socket

来源:互联网 发布:php array splice 编辑:程序博客网 时间:2024/05/22 14:13

使用Socket

Socket是两台主机之间的一个连接。它可以完成7个基本操作:

  • 连接远程机器。
  • 发送数据。
  • 接受数据。
  • 关闭连接。
  • 绑定端口。
  • 监听入站数据。
  • 在绑定端口上接受来自远程机器的连接。

Java的Socket类(客户端和服务端都可以使用)提供了前4个操作的方法。后面3个操作仅服务器需要,即等待客户端的连接,这些操作由ServerSocket类实现。

构造和连接Socket

java.net.Socket类是Java完成客户端TCP操作的基础类。其他建立TCP网络连接的面向客户端的类(如URL、URLConnection、Applet和JEditorPane)最终都会调用这个类的方法。

设置超时时间

强烈推荐使用setSoTimeOut()方法为连接设置一个超时时间。超时时间按毫秒度量,所以下面这个语句设置socket在15秒无响应之后超时:

socket.setSoTimeOut(15000);

对于一个本地内部网服务器来说,15秒的响应时间太长了,但是对于一个负载很大的公共服务器(如time.nist.gov),这个时间则很短。一旦打开socket并设置其超时时间,可以调用getInputStream() 来返回一个InputStream,用它从socket读取字节。

基本构造函数

每个Socket构造函数指定要连接的主机和端口。主机可以指定为InetAddress或String。远程端口指定为1到65535之间的int值:

public Socket(String host, int port) throws UnknownHostException, IOExceptionpublic Socket(InetAddress host, int port) throws IOException

这些构造函数会连接socket,即在构造函数返回之前,会与远程主机建立一个活动的网络连接。

选择从哪个本地接口连接

有两个Socket构造函数可以指定要连接的主机和端口,以及从哪个接口和端口连接:

public Socket(String host, int port, InetAddress interface, int localPort) throws IOException, UnknownHostExceptionpublic Socket(InetAddress host, int port, InetAddress interface, int localPort) throws IOException

这个Socket连接到前两个参数中指定的主机和端口。它从后两个参数指定的本地网络接口和端口来连接。

构造但不连接

如果没有为Socket构造函数提供任何参数,它就没有目标主机可以连接:

public Socket()

可以以后再为某个connect()方法传入一个SocketAddress来建立连接。

Socket地址

SocketAddress类(一个空的抽象类)表示一个连接端点。当前只支持TCP/IP Socket。实际使用的Socket地址都是InetSocketAddress的实例。SocketAddress类的主要用途是为暂时的socket连接信息(如IP地址和端口)提供一个方便的储存。Socket类提供了两个返回SocketAddress对象的方法:

public SocketAddress getRemoteSocketAddress()public SocketAddress getLocalSocketAddress()

getRemoteSocketAddress()返回所连接系统的地址,getLocalSocketAddress()返回发起连接的地址,如果Socket尚未连接,则这两个方法都返回null。通常会用一个主机和一个端口(对于客户端)或者只使用一个端口(对于服务器)来创建InetSocketAddress类(SocketAddress唯一的子类):

public InetSocketAddress(InetAddress address, int port)public InetSocketAddress(String host, int port)public InetSocketAddress(int port)

还可以使用该静态工厂类方法从而不再在DNS中查找主机:

public static InetSocketAddress createUnresolved(String host, int port)

InetSocketAddress提供了一些获取方法,可以用来检查这个对象:

public final InetAddress getAddress()public final int getPort()public final String getHostName()

代理服务器

最后一个构造函数创建一个未连接的Socket,它通过一个指定的代理的服务器连接:

public Socket(Proxy proxy)

一般情况下,Socket使用的代理服务器由socksProxyHost和socksProxyPort系统属性控制,这些属性应用于系统中的所有Socket。但是构造函数创建的socket会使用指定的代理服务器。最值得一提的是,可以为参数传入Proxy.NO_PROXY,完全绕过所有代理服务器,而直接连接远程主机。

获取Socket的信息

Socket对象有一些属性可以通过获取方法来访问:

public InetAddress getInetAddress()     // 访问远程地址public int getPort()                    // 访问远程端口public InetAddress getLocalAddress()    // 访问本地地址public int getLocalPort()               // 访问本地端口

如果连接现在是关闭的,getInetAddress()和getPort()方法则给出Socket连接时所连接的主机和端口。

关闭还是连接

  • isClosed()
    如果socket关闭,该方法会返回true。不过,这不是一个万全的测试。如果Socket从一开始从未连接,该方法也会返回false。

  • isConnected()
    该方法会指出Socket是否从未连接过一个远程主机。要查看一个Socket当前是否打开,需要检查两个条件,首先isConnected()要返回true,另外isClosed()要返回false。

  • isBound()
    该方法会告诉你Socket是否成功地绑定到本地系统上的出站端口。

toString()

Socket类只覆盖了java.lang.Object中的一个标准方法:toString()。toString()方法会生成一个类似这样的字符串:

Socket[addr=www.oreilly.com/198.112.208.11,port=80,localport=50055]

该方法主要用于调试,不过不要依赖这个格式,将来格式可能会改变。

设置Socket选项

  • TCP_NODELAY
public void setTcpNoDelay(boolean on) throws SocketExceptionpublic boolean getTcpNoDelay() throws SocketException

设置TCP_NODELAY为true可确保包会尽快地发送,而无论包的大小。如果远程系统没有足够快地将确认发送回本地系统,那么依赖于小数据量信息稳定传输的应用程序会变得很慢。因此,这个选项在游戏或网络计算机应用程序(服务器需要实时跟踪客户端鼠标的移动)中很重要。

  • SO_LINGER
public void setSoLinger(boolean on, int seconds) throws SocketExceptionpublic int getSoLinger() throws SocketException

SO_LINGER选项指定了Socket关闭时如何处理尚未发送的数据报。默认情况下,close()方法将其立即返回,但系统仍会尝试发送剩余的数据。如果SO_LINGER打开而且延时设置为任意正数,close()方法会阻塞,等待发送数据和接收确认。

  • SO_TIMEOUT
public void setSoTimeOut(int millisceonds) throws SocketExceptionpublic int getSoTimeOut() throws SocketException

正常情况下,尝试从Socket读取数据时,read()调用会阻塞尽可能长的时间来得到足够的字节。设置SO_TIMEOUT可以确保这次调用阻塞的时间不会超过某个固定的毫秒数。当时间到期时就会抛出一个InterruptedIOException异常,你应当准备好捕获这个异常。不过,Socket仍然是连接的,下一次调用可能会成功。超时时间按毫秒给出,0被解释为无限超时,这是默认值。

  • SO_RCVBUF和SO_SNDBUF

TCP使用缓冲区提升网络性能。较大的缓冲区会提升快速连接的性能,而较慢的连接利用较小的缓冲区会有更好的表现。

public void setReceiveBufferSize(int size) throws SocketException, IllegalArgumentExceptionpublic int getReceiveBufferSize() throws SocketExceptionpublic void setSendBufferSize(int size) throws SocketException, IllegalArgumentExceptionpublic int getSendBufferSize() throws SocketException

尽管看起来应该能独立地设置发送和接收缓冲区,但实际上缓冲区通常会设置为二者中较小的一个。不过,底层实现完全可以忽略或调整这个建议。不过,大多数情况下,除非网络在某个方向上负载过大,否则默认值就很合适。

  • SO_KEEPALIVE
public void setKeepAlive(boolean on) throws SocketExceptionpublic boolean getKeepAlive() throws SocketException

如果打开了SO_KEEPALIVE,客户端偶尔会通过一个空闲连接发送一个数据包(一般两小时一次),以确保服务器未崩溃。

  • OOBINLINE

下面的这个方法几乎会立即发送参数中的最低字节。

public void sendUrgentData(int data) throws IOException

默认情况下,Java会忽略从Socket接收的紧急数据。不过,如果你希望接收正常数据中的紧急数据,就需要使用下面的方法设置OOBINLINE选项为true:

public void setOOBInline(boolean on) throws SocketExceptionpublic boolean getOOBInline() throws SocketException

Java不区分紧急数据和非紧急数据,这使它不能理想地发挥作用,不过如果有一个特定字节对你的程序有特殊含义,而且从不出现在常规的输入流中,这就能让你更快地发送这个字节。

  • SO_REUSEADDR

如果开启SO_REUSEADDR(默认为关闭),就允许另一个Socket绑定到一个刚刚关闭但未释放的端口,即使此时仍有可能存在前一个Socket未接收的数据。

public void setReuseAddress(boolean on) throws SocketExceptionpublic boolean getReuseAddress() throws SocketException
  • IP_TOS服务类型

服务器类型储存在IP首部中一个名为IP_TOS的8位字段中。Java允许你使用下面两个方法检查和设置Socket放在这个字段中的值:

public int getTrafficClass() throws SocketExceptionpublic void setTrafficClass(int trafficClass) throws SocketException

在21世纪的TCP栈中,这个字节的高6位包含一个差分服务代码点(Differentiated Services Code Point, DSCP)值,低两位包含一个显式拥塞通知(Explicit Congestion Notification,ECN)值。作为表达偏好的另一种方法,setPerformancePreferences()方法为连接时间、延迟和带宽指定相对优先性。

0 0
原创粉丝点击