17.3.3 使用Socket进行通信
17.3.3 使用Socket进行通信
客户端通常可以使用Socket
的构造器来连接到指定服务器,Socket
通常可以使用如下两个构造器。
本地主机只有一个IP地址的情况
方法 | 描述 |
---|---|
Socket(String host, int port) |
创建连接到指定远程主机、远程端口的Socket ,该构造器没有指定本地地址、本地端口,默认使用本地主机的默认IP地址,默认使用系统动态分配的端口。 |
Socket(InetAddress address, int port) |
Creates a stream socket and connects it to the specified port number at the specified IP address. |
适用于本地主机有多个IP地址的情况
方法 | 描述 |
---|---|
Socket(String host, int port, InetAddress localAddr, int localPort) |
创建连接到指定远程主机、远程端口的Socket ,并指定本地IP 地址和本地端口,适用于本地主机有多个IP 地址的情形。 |
Socket(InetAddress address, int port, InetAddress localAddr, int localPort) |
Creates a socket and connects it to the specified remote address on the specified remote port. |
上面两个构造器中指定远程主机时既可使用InetAddress
来指定,也可直接使用String
对象来指定,但程序通常使用String
对象(如192.168.2.23)来指定远程IP
地址。当本地主机只有一个IP地址时,使用第一类Socket构造器更为简单。如下代码所示。
1 | //创建连接到本机、30000端口的`Socket` |
当程序执行上面Socket
构造器时,该代码将会连接到指定服务器,让服务器端的ServerSocket
的accept()
方法向下执行,于是服务器端和客户端就产生一对互相连接的Socket
。
上面程序连接到“远程主机”的地址使用的是127.0.0.1,这个IP地址是一个特殊的地址,它总是代表本机的IP
地址。因为本书的示例程序的服务器端、客户端都是在本机运行的,所以Socket
连接的远程主机的IP地址使用127.0.0.1
获取Socket的输入输出流
当客户端、服务器端产生了对应的Socket
之后,就得到了如图17.4所示的通信示意图,程序无须再区分服务器端、客户端,而是通过各自的Socket
进行通信。Socket
提供了如下两个方法来获取输入流和输出流。
方法 | 描述 |
---|---|
InputStream getInputStream() |
返回该Socket 对象对应的输入流,让程序通过该输入流从Socket 中取出数据。 |
OutputStream getOutputStream() |
返回该Socket 对象对应的输岀流,让程序通过该输出流向Socket 中输出数据。 |
看到这两个方法返回的InputStream
和OutputStream
,读者应该可以明白Java
在设计IO体系上的苦心了——不管底层的IO流是怎样的节点流:文件流也好,网络Socket
产生的流也好,程序都可以将其包装成处理流,从而提供更多方便的处理。下面以一个最简单的网络通信程序为例来介绍基于TCP
协议的网络通信。
程序示例 最简单的TCP通信
下面的服务器端程序非常简单,它仅仅建立ServerSocket
监听,并使用Socket
获取输出流输出。
1 | import java.net.*; |
下面的客户端程序也非常简单,它仅仅使用Socket
建立与指定IP地址、指定端口的连接,并使用Socket
获取输入流读取数据。
1 | import java.net.*; |
上面程序中①号代码是使用Socket
和ServerSocket
建立网络连接的代码,接下来代码通过Socket
获取输入流、输岀流进行通信的代码。通过程序不难看出,一旦使用ServerSocket
,Socket
建立网络连接之后,程序通过网络通信与普通IO
并没有太大的区别。
先运行程序中的Server
类,将看到服务器一直处于等待状态,因为服务器使用了死循环来接收来自客户端的请求;
再运行Client
类,将看到程序输岀:“来自服务器的数据:来自服务器的数据:你好! 你是第0号客户端”,这表明客户端和服务器端通信成功。
上面程序为了突出通过ServerSocket
和Socket
建立连接,并通过底层IO
流进行通信的主题,程序没有进行异常处理,也没有使用finally
块来关闭资源
设置超时时间
在实际应用中,程序可能不想让执行网络连接、读取服务器数据的进程一直阻塞,而是希望当网络连接、读取操作超过合理时间之后,系统自动认为该操作失败,这个合理时间就是超时时长。Socket
对象提供了一个setSoTimeout(int timeout)
方法来设置超时时长。如下代码片段所示:
1 | Socket s=new Socket("127.0.0.1",30000); |
为Socket
对象指定了超时时长之后,如果在使用Socket
进行读、写操作完成之前超出了该时间限制,那么这些方法就会抛出SocketTimeoutException
异常,程序可以对该异常进行捕获,并进行适当处理。如下代码所示:
1 | try{ |
Socket
连接服务器时指定超时时长
假设程序需要为Socket
连接服务器时指定超时时长,即经过指定时间后,如果该Socket
还未连接到远程服务器,则系统认为该Socket
连接超时。但Socket
的所有构造器里都没有提供指定超时时长的参数,所以程序应该先创建一个无连接的Socket
,再调用Socket
的connect()
方法来连接远程服务器而connec
方法就可以接收一个超时时长参数。如下代码所示。
1 | //创建一个无连接的 Socket |