Socket建立在TCP/IP协议的基础上,可以看做是通信连接两端的收发器,是两台机器间通信的端点,服务器与客户端都通过Socket来收发数据。
本文从构造器,异常,方法三个方面来详解Socket类
一、构造方法
Socket有以下几种常用的构造方法:
(1)Socket()
(2)Socket(InetAddress address, int port)
(3)Socket(String host, int port)
(4)Socket(InetAddress address, int port, InetAddress localAddr, int localPort)
(5)Socket(String host, int port, InetAddress localAddr, int localPort)
先说一下构造方法中的参数InetAddress类,该类表示IP地址,它提供了一系列静态工厂方法。列如:
InetAddress address1 = InetAddress.getLocalHost(); //获取本地主机IP
InetAddress address1 = InetAddress.getByName("192.168.2.36"); //返回
192.168.2.36的IP
InetAddress address1 = InetAddress.getByName("www.baidu.com"); //返回www.baidu.com的IP
除3第一种构造方法外,其他几种都会在初始化时试图建立与服务器的连接,如果成功,返回Socket对象,如果因为某些原因失败,会抛出IOException。
当Socket请求与服务器连接时,可能要等待一段时间,默认情况下,会一直等下去,直到成功或者抛出异常。例如底层网络传输速度很慢,可能就会等待很久,这个时候通常会限定等待时间,此时就需要使用第一个不带参数的构造器:
Socket socket = new Socket();
SocketAddress address = new InetSocketAddress("localhost",8000);
Socket.connect(address,60000); //设置等待时间为6万毫秒,即1分钟
用这种方法创建Socket如果限定时间内连接成功,connect顺利返回,如果不成功,抛出SocketTImeoutException
这里的connect(SocketAddress endpoint, int timeout )方法作用是将此套接字连接到服务器,并指定一个超时值。
对于其他的构造方法,都需要早参数中设置服务器地址,包括IP地址或主机名,以及端口。再一个Socket对象中,既包含服务器IP和端口,又包含客户端IP和端口,默认情况下,客户端IP为客户端所在主机IP,端口由操作系统随机分配。而一台主机如果处于多个网络中(如同时处于Internet和局域网),就会有多个IP,当我们想要客户端使用某个IP时,就可以使用第(4)(5)中构造方法指定客户端的IP和端口。
二、异常
Socket的构造方法连接主机时,可能会抛出以下几种异常:
UnknownHostException:无法识别主机名字或IP
ConnectException:没有服务器监听指定端口,或服务器拒绝连接
SocketTimeoutException:连接超时
BindException:无法把Socket对象与指定本地IP或端口绑定
三、方法
以下为Socket类提供的常用方法,在这里我分为三类:
1.从Socket中获取信息的方法:
getInetAddress(); //返回套接字连接的地址。 返回类型为InetAddress
getInputStream(); // 返回此套接字的输入流。 返回类型为InputStream
getLocalAddress(); //获取套接字绑定的本地地址。 返回类型为InetAddress
getLocalPort(); //返回此套接字绑定到的本地端口。 返回类型为int
getLocalSocketAddress(); // 返回此套接字绑定的端点的地址,如果尚未绑定则返回 null。 返回类型为SocketAddress
getOutputStream(); //返回此套接字的输出流。 返回类型为OutputStream
getPort(); //返回此套接字连接到的远程端口。 返回类型为int
getRemoteSocketAddress(); // 返回此套接字连接的端点的地址,如果未连接则返回 null。 返回类型为SocketAddress
2.关闭Socket的相关方法:
当客户端与服务器结束通信后,应及时关闭Socket,以释放Socket占用的各种资源
close()方法用于关闭Socket对象,当一个对象被关闭后,将不能进行I/O操作,所以close()方法通常写在finally块中,否则会导致IOException。
Socket为测试Socket的状态提供了三种方法
isBound() // 返回套接字的绑定状态。
isClosed() //返回套接字的关闭状态。
isConnected() //返回套接字的连接状态。
如果判断一个Socket是否处于连接状态,可以用以下的方法:
boolean flag = socket.isConnected() && !socket.isClosed();
当调用close()方法时,输入流和输出流都被关闭,而有时只需要关闭其中一个,此时采用Socket提供的半关闭方法:
shutdownInput() //关闭输入流
shutdownOutput() //关闭输出流
这里需要注意,关闭了输入输出流不等于关闭了Socket,所以最后还是需要执行close()方法
测试半关闭的方法:
isInputShutdown()
isOutputShutdown()
3.设置Socket属性的相关方法:
Socket有以下几种属性:
(1)TCP_NODELAY:表示立即发送数据报
setTcpNoDelay(boolean on) //启用/禁用 TCP_NODELAY(启用/禁用 Nagle 算法)。
采用Nagle 算法不会立即发送数据,会将数据放在缓冲区中,缓冲区满了再发送,默认为false,表示采用Nagle 算法
(2)SO_RESUSEADD:表示是否允许重用Socket所绑定的本地地址
setReuseAddress(boolean on)
关闭Socket后,不会立即释放端口,会等一段时间来接收延迟数据,这样确保延迟数据不会被关闭后新绑定到此端口的Socket收到。
setReuseAddress(boolean on)设置为true,关闭Socket后,此端口可立即被重用。
这里需要注意,该方法必须在Socket绑定端口前调用,否则无效。可以用以下的创建方式:
Socket socket = new Socket();
socket.setReuseAddress(true);
socket.connect(new SocketAddress("remotehost",8000));
(3)SO_TIMEOUT:表示接受数据报的等待超时时间。
setSoTimeout(int timeout) //启用/禁用带有指定超时值的 SO_TIMEOUT,以毫秒为单位。默认为0,表示不会超时,会一直等待下去
(4)SO_LINGER:当执行close()方法时,是否立即关闭底层Socket
setSoLinger(boolean on, int linger) // 启用/禁用具有指定逗留时间(以秒为单位)的 SO_LINGER。
默认情况下,执行Socket的close()方法,会立即返回,但是底层的Socket对象并不会立刻关闭
如果执行以下方法:
socket.setSoLinger(true,0);
socket.close();
close()会立即返回,底层的Socket对象也会立刻关闭,未发送完的数据直接关闭;
如果执行以下方法:
socket.setSoLinger(true,100);
socket.close();
close()方法不会立即返回,而是进入阻塞状态,在100秒内发送完数据时返回,或到达100秒没发送完数据,也返回,剩余数据丢弃
(5)SO_SNFBUF:发送数据缓冲区大小
setSendBufferSize(int size) // 将此 Socket 的 SO_SNDBUF 选项设置为指定的值
用于设置输入缓冲区的大小,一般传输大的文件,如HTTP,FTP协议的通信,都采用较大的缓冲区,可以减少传输数据的次数,对于次数频繁而数据量比较小的数据,如网络游戏,通常采用比较小的缓冲区,确保能快速收发数据
(6)SO_RCVBUF:接受数据缓冲区大小
setReceiveBufferSize(int size) //将此 Socket 的 SO_RCVBUF 选项设置为指定的值。
缓冲区的设置规则和SO_SNFBUF选项相同
(7)SO_KEEPALIVE:长期处于空闲状态的Socket,是否自动关闭
setKeepAlive(boolean on) //启用/禁用 SO_KEEPALIVE。
把该方法设置为true,空闲连接(连接两端没有收发数据)超过2小时,会自动关闭,该选项的默认值为false,不会监听空闲连接,不活动的客户端会永久存活,直到服务器崩溃。
(8)OOBINLINF:是否支持发送一个字节的紧急数据报
setOOBInline(boolean on) //启用/禁用 OOBINLINE(TCP 紧急数据的接收者) 默认情况下,此选项是禁用的,即在套接字上接收的 TCP 紧急数据被静默丢弃。
将此方法设置为true,则可以调用以下方法发送紧急TCP数据报:
socket.sendUrgentData(int data); // 在套接字上发送一个紧急数据字节。
除非使用一些高层次协议,否则接收方很难区分普通数据报和紧急数据报,只能按照同样的方式处理它们
获取这些属性值得get方法在这里省略。