文章目录
1. TCP 流套接字
TCP 不需要一个类;来表示 “TCP” 数据报。
TCP 不是以数据报为单位进行传输的,而是以字节流的方式进行传输的。
1.1 ServerSocket API
ServerSocket 是专门给服务器使用的 Socket 对象。
ServerSocket 构造方法:
- ServerSocket(int port) :创建一个服务端流套接字Socket,并绑定到指定端口
ServerSocket 方法:
1.2 Socket API
Socket 是既会给客户端使用,也会给服务器使用。
不管是客户端还是服务端 Socket,都是双方建立连接以后,保存的对端信息,即用来与对方收发数据的。
Socket 构造方法:
Socket 方法:
- InetAddress getInetAddress():返回套接字所连接的地址
- InputStream getInputStream():返回此套接字的输入流
- OutputStream getOutputStream():返回此套接字的输出流
进一步通过 socket 对象,获取到内部的 流对象,借助流对象来进行发送/接收。
在服务器这边,是由 accept 返回的。
在客户端这边,是由代码构造的。构造的时候指定一个 ip 和 端口号。(此处指定的是服务器的 ip 和 端口)
1.3 TCP中的长短连接
TCP发送数据时,需要先建立连接,什么时候关闭连接就决定是短连接还是长连接:
短连接: 每次接收到数据并返回响应后,都关闭连接,即是短连接。也就是说,短连接只能一次收发数据。
长连接: 不关闭连接,一直保持连接状态,双方不停的收发数据,即是长连接。也就是说,长连接可以多次收发数据。
区别:
- 建立与关闭的耗时:短连接每次请求、响应都需要建立连接,关闭连接;而长连接只需要
第一次建立连接,之后的请求、响应都可以直接传输。相对来说建立连接,关闭连接也是要耗时
的,长连接效率更高。 - 主动发送请求不同:短连接一般是客户端主动向服务端发送请求;而长连接可以是客户端主动发送
请求,也可以是服务端主动发。 - 两者的使用场景有不同:短连接适用于客户端请求频率不高的场景,如浏览网页等。长连接适用于
客户端与服务端通信频繁的场景,如聊天室,实时游戏等。
2. TCP 版本的回显服务器
1、先用 ServerSocket 创建一个对象。
private ServerSocket serverSocket = null;
指定一个构造方法来绑定一个端口
public TcpEchoServer(int port) throws IOException {
serverSocket = new ServerSocket(port);
}
创建一个 start 方法,通过创建的对象来调用 accept 方法(accept 是接收连接)
public void start() throws IOException{
System.out.println("启动服务器!!!");
while (true) {
//使用 clientSocket 和具体的客户端进行交互
Socket clientSocket = serverSocket.accept();
}
}
accept 方法的效果是 “接收连接”,前提是要有客户端来建立连接。
客户端在构造 Socket 对象的时候,就会指定服务器的 IP 和 端口号,如果没有客户端来连接,此时 accept 就会阻塞。
accept 会返回一个 Socket 对象。
Socket clientSocket = serverSocket.accept();
TCP socket 里面涉及到两种 socket 对象
此时的 clientSocket 是一个 socket 对象,serverSocket 也是一个 socket 对象。
下面来举个例子说明。
比如张三去 4s 店买车,刚进店的时候,店员小王就来接待他,在了解了需求后就把张三汽车介绍的员工那里。
这个时候小王就把张三带到李四那里,而李四是专门为顾客介绍汽车有哪些特点等等的。
店员小王负责的是具体的买车服务,而李四负责的是引领顾客到指定位置。
店员小王就相当于是 serverSocket 对象,讲解员李四 就相当于是 clientSocket 对象。
2、接收到连接后,就可以写一个方法拿 clientSocket 与客户端进行交互了。
使用这个方法来处理一个链接,这一个连接对应到一个客户端,但是这里可能会涉及到多次交互。
private void processConnection(Socket cilentSocket) {
System.out.printf("[%s:%d] 客户端上线!\n", cilentSocket.getInetAddress().toString(), cilentSocket.getPort());
}
cilentSocket.getInetAddress() 是获取 ip 地址 ,cilentSocket.getPort() 是获取端口号。
接下来就可以基于上述 socket 对象和客户端进行通信了。
先用 try catch 拿到 InputStream 和 OutputStream。
try (InputStream inputStream = cilentSocket.getInputStream();
OutputStream outputStream = cilentSocket.getOutputStream()){
} catch (IOException e) {
e.printStackTrace();
}
接下来读取请求。
Scanner scanner = new Scanner(inputStream);
if (!scanner.hasNext()) {
// 没有下个数据说明读完了(也就是客户端关闭连接)
System.out.printf("[%s:%d] 客户端下线!\n", cilentSocket.getInetAddress().toString(),
cilentSocket.getPort());
break;
}
//
String request =