TCP,即Transmission Control Protocol(传输控制协议),传输层协议。
以下为TCP的特点:
- 有连接
- 可靠传输
- 面向字节流
- 有接收缓冲区,也有发送缓冲区
- 大小不限
基于TCP的Socket通信
- 服务端先启动,创建ServerSocket对象,等待连接。
客户端启动,创建Socket对象,请求连接。 - 服务器端接收请求,调用accept方法,并返回一个Socket对象,连接成功
- 客户端的Socket对象通过调用getOutputStream()方法获取OutputStream对象,并使用write()方法将数据写入到发送缓冲区。随后,通过调用flush()方法确保数据已被发送出去
- 服务器端Socket对象通过调用getInputStream()方法获取与该socket关联的InputStream实例,然后使用read()方法从接收缓冲区中读取数据。
- 客户端释放资源,断开连接。
服务器:
package network;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
public class TcpEchoServer {
// serverSocket 就是外场拉客的小哥
// clientSocket 就是内场服务的小姐姐.
// serverSocket 只有一个. clientSocket 会给每个客户端都分配一个~
private ServerSocket serverSocket = null;
public TcpEchoServer(int port) throws IOException {
serverSocket = new ServerSocket(port);
}
public void start() throws IOException {
System.out.println("服务器启动!");
while (true) {
Socket clientSocket = serverSocket.accept();
processConnection(clientSocket);
}
}
// 通过这个方法来处理一个连接.
// 读取请求
// 根据请求计算响应
// 把响应返回给客户端
private void processConnection(Socket clientSocket) throws IOException {
System.out.printf("[%s:%d] 客户端上线!\n", clientSocket.getInetAddress().toString(),
clientSocket.getPort());
// try () 这种写法, ( ) 中允许写多个流对象. 使用 ; 来分割
try (InputStream inputStream = clientSocket.getInputStream();
OutputStream outputStream = clientSocket.getOutputStream()) {
// 没有这个 scanner 和 printWriter, 完全可以!! 但是代价就是得一个字节一个字节扣, 找到哪个是请求的结束标记 \n
// 不是不能做, 而是代码比较麻烦.
// 为了简单, 把字节流包装秤了更方便的字符流~~
Scanner scanner = new Scanner(inputStream);
PrintWriter printWriter = new PrintWriter(outputStream);
while (true) {
// 1. 读取请求
if (!scanner.hasNext()) {
// 读取的流到了结尾了 (对端关闭了)
System.out.printf("[%s:%d] 客户端下线!\n", clientSocket.getInetAddress().toString(),
clientSocket.getPort());
break;
}
// 直接使用 scanner 读取一段字符串.
String request = scanner.next();
// 2. 根据请求计算响应
String response = process(request);
// 3. 把响应写回给客户端. 不要忘了, 响应里也是要带上换行的.
printWriter.println(response);
System.out.printf("[%s:%d] req: %s; resp: %s\n", clientSocket.getInetAddress().toString(),
clientSocket.getPort(), request, response);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
clientSocket.close();
}
}
private String process(String request) {
return request;
}
public static void main(String[] args) throws IOException {
TcpEchoServer tcpEchoServer = new TcpEchoServer(9090);
tcpEchoServer.start();
}
}
客户端:
package network;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
public class TcpEchoClient {
private Socket socket = null;
public TcpEchoClient(String serverIp, int port) throws IOException {
// 这个操作相当于让客户端和服务器建立 tcp 连接.
// 这里的连接连上了, 服务器的 accept 就会返回.
socket = new Socket(serverIp, port);
}
public void start() {
Scanner scanner = new Scanner(System.in);
try (InputStream inputStream = socket.getInputStream();
OutputStream outputStream = socket.getOutputStream()) {
PrintWriter printWriter = new PrintWriter(outputStream);
Scanner scannerFromSocket = new Scanner(inputStream);
while (true) {
// 1. 从键盘上读取用户输入的内容.
System.out.print("-> ");
String request = scanner.next();
// 2. 把读取的内容构造成请求, 发送给服务器.
// 注意, 这里的发送, 是带有换行的!!
printWriter.println(request);
// 3. 从服务器读取响应内容
String response = scannerFromSocket.next();
// 4. 把响应结果显示到控制台上.
System.out.printf("req: %s; resp: %s\n", request, response);
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws IOException {
TcpEchoClient client = new TcpEchoClient("127.0.0.1", 9090);
client.start();
}
}
运行结果: