- TCP Socket 通信概念
- TCP Socket 通信过程
- Socket 类
- ServerSocket 类
- 案例:文件上传工具
TCP 是面向连接的可靠数据传输协议。TCP 通信过程类似于打电话,电话接通后双方才能通话,在挂断电话之前,电话一直占线。TCP 接连一旦建立起来,会一直占用,知道关闭连接。此外,TCP 为了保证数据的正确性,会重发一切没有收到的数据,还会对数据内容进行验证并保证数据传输的正确顺序。因此 TCP 协议对系统资源有很高的要求。
一、TCP Socket 通信概念
Socket 是网络上的两个程序,通过一个双向的通信连接,实现数据的交流。这个链路的一端称为一个 Socket。Socket 通常用来实现客户端和服务端的连接。Socket 是 TCP/IP 协议的一个十分流行的编程接口,一个 Socket 有一个 IP 地址和一个端口号唯一确定。一旦建立连接,Socket 还会包含本机和远程主机的 IP 地址和远端口号,Socket 是成对出现的。
二、TCP Socket 通信过程
使用 Socket 进行 C/S 结构编程的通信过程如下:
服务器端监听某个端口是否有连接请求,此时服务器端程序处于阻塞状态,知道客户端向服务器端发出连接请求,服务器端接收客户端请求,服务器会应答请求并处理请求,然后将结果应答给客户端,这样就会建立连接。一旦连接建立起来,通信 Socket 可以获得输入输出流对象。借助于输入输出流对象就可以实现服务器与客户端的通信,最后不要忘记关闭 Socket 和释放一些资源(包括关闭输入输出流)。
三、Socket 类
java.net
包为 TCP Socket 编程提供了两个核心类:Socket
和 ServerSocket
,分别用来表示双向连接的客户端和服务器端。
下面是Socket
类常用的构造函数:
Socket(address: InetAddress!, port: Int)
:创建 Socket 对象,并指定远程主机 IP 地址和端口号;Socket(address: InetAddress!, port: Int, localAddr: InetAddress!, localPort: Int)
:创建 Socket 对象,并指定远程主机 IP 地址、端口号以及本机的 IP 地址 (localAddr) 和 端口号 (localPort);Socket(host: String!, port: Int)
:创建 Socket 对象,并指定远程主机名和端口号,IP 地址为 null,null 表示回送地址,即 127.0.0.1;Socket(host: String!, port: Int, localAddr: InetAddress!, localPort: Int)
:创建 Socket 对象,并指定远程主机、端口号以及本机的 IP 地址 (localAddr) 和 端口号 (localPort)。host 为主机名,IP 地址为 null,null 表示回送地址,即 127.0.0.1;
提示:“数据类型!” 表示 “平台类型”,String! 表示 String 或 String?。什么是平台类型?
Socket
其他的常用函数和属性如下:
getInputStream()
函数:通过此 Socket返回输入流对象。getOutputStream()
函数:通过此 Socket返回输出流对象。port: Int
属性:返回 Socket连接到的远程端口。localPort: Int
属性:返回 Socket绑定到本地端口。inetAddress
属性:返回 Socket连接地址。localAddress
属性:返回 Socket绑定的本地地址。isClosed
属性:判断返回 Socket是否处于关闭状态。isConnected
属性:判断返回 Socket是否处于连接状态。close()
函数:关闭 Socket。
注意:Socket 与 流所占用的资源类似,不能通过 Java 虚拟机的垃圾收集器回收,需要程序员释放。释放的方式有两种,一种是可以在 finally
代码块调用 close()
函数关闭 Socket,释放流所占用的资源。另一种是通过自动资源管理技术释放资源,Socket
和 ServerSocket
都实现了 AutoCloseable
接口,所以 kotlin 中可以使用 use
函数。
四、ServerSocket 类
ServerSocket
类常用的构造函数:
ServerSocket(port: Int, maxQueue: Int)
。创建绑定到特定端口的服务器 Socket。maxQueue 设置连接请求的最大队列长度,入多队列满时,则拒绝该连接。默认值是 50。ServerSocket(port: Int)
。创建绑定到特定端口的服务器 Socket。连接请求的最大队列长度是 50。
ServerSocket
其他的常用函数和属性如下:
getInputStream()
函数:通过此 Socket返回输入流对象。getOutputStream()
函数:通过此 Socket返回输出流对象。isClosed
属性:判断返回 Socket是否处于关闭状态。isConnected
属性:判断返回 Socket是否处于连接状态。accept()
函数:侦听并接收到 Socket 的连接。此函数在建立连接之前一直是阻塞状态 。
ServerSocket
类本身不能直接获得 I/O 流对象,而是通过 accept()
函数返回 Socket 对象,通过 Socket 对象取得 I/O 流对象,进行网络通信。另外,ServerSocket
也实现了 AutoCloseable
接口,通过自动资源管理技术关闭 ServerSocket
。
五、案例:文件上传工具
- 服务器端代码:
fun main(args: Array<String>?) {
println("服务器端运行...")
ServerSocket(8080).use { server ->
server.accept().use { socket ->
BufferedInputStream(socket.getInputStream()).use { bis ->
FileOutputStream("./TestDir/subDir/fxy.png").use { fos ->
bis.copyTo(fos)
println("接收完成!")
}
}
}
}
}
注意:ServerSocket
的 accept()
函数阻塞当前线程,所以一般会是在子线程中执行 accept()
函数。
- 客户端代码:
fun main(args: Array<String>?) {
println("客户端运行...")
Socket("127.0.0.1", 8080).use { socket ->
BufferedOutputStream(socket.getOutputStream()).use { bos ->
FileInputStream("./TestDir/fxy.png").use { fis ->
fis.copyTo(bos)
println("上传成功!")
}
}
}
}