TCP Socket编程
- [1. ServerSocket](#1. ServerSocket)
- [2. Socket](#2. Socket)
- [3. TCP的长短连接](#3. TCP的长短连接)
- [4. Socket 通信模型](#4. Socket 通信模型)
- [5. 代码示例:TCP 回显服务器](#5. 代码示例:TCP 回显服务器)
流套接字: 使用传输层TCP协议
TCP: 即Transmission Control Protocol(传输控制协议),传输层协议。
TCP的特点:
- 有连接
- 可靠传输
- 面向字节流
- 有接收缓冲区,也有发送缓冲区
- 大小不限
1. ServerSocket
ServerSocket 是创建TCP服务端Socket的API。
注意: ServerSocket 只能用于 服务器端。
构造方法:
方法签名 | 方法说明 |
---|---|
ServerSocket(int port) | 创建一个服务端流套接字Socket,并绑定到指定端口 |
方法:
方法签名 | 方法说明 |
---|---|
Socket accept() | 开始监听指定端口(创建时绑定的端口),有客户端连接后,返回一个服务端Socket对象,并基于该Socket建立与客户端的连接,否则阻塞等待 |
void close() | 关闭此套接字 |
2. Socket
Socket 是客户端Socket,或服务端中接收到客户端建立连接(accept方法)的请求后,返回的服务端Socket。
构造方法:
方法签名 | 方法说明 |
---|---|
Socket(String host, int port) | 创建一个客户端流套接字Socket,并与对应IP的主机上,对应端口的进程建立连接 |
注意:这里面的 host 和 port 是要连接的服务器的 IP 地址和端口号。
方法:
方法签名 | 方法说明 |
---|---|
InetAddress getInetAddress() | 返回套接字所连接的地址 |
InputStream getInputStream() | 返回此套接字的输入流 |
OutputStream getOutputStream() | 返回此套接字的输出流 |
3. TCP的长短连接
TCP发送数据时,需要先建立连接,什么时候关闭连接就决定是短连接还是长连接:
短连接: 每次接收到数据并返回响应后,都关闭连接,即是短连接。也就是说,短连接只能一次收数据。
长连接: 不关闭连接,一直保持连接状态,双方不停的收发数据,即是长连接。也就是说,长连接可以多次收发数据。
两者区别如下:
- 建立连接、关闭连接的耗时:短连接每次请求、响应都需要建立连接,关闭连接;而长连接只需要第一次建立连接,之后的请求、响应都可以直接传输。相对来说建立连接,关闭连接也是要耗时的,长连接效率更高。
- 主动发送请求不同:短连接一般是客户端主动向服务端发送请求;而长连接可以是客户端主动发送请求,也可以是服务端主动发。
- 两者的使用场景有不同:短连接适用于客户端请求频率不高的场景,如浏览网页等。长连接适用于客户端与服务端通信频繁的场景,如聊天室,实时游戏等。
4. Socket 通信模型
5. 代码示例:TCP 回显服务器
服务器代码:
javascript
class TcpEchoServer {
public ServerSocket serverSocket;//专门用来接受请求并建立链接
public Socket clientSocket;//专门用来处理请求
public TcpEchoServer(int port) throws IOException {
this.serverSocket=new ServerSocket(port);
}
public void start() throws IOException {
System.out.println("服务器启动!");
//也可以利用线程池
ExecutorService threadsPool= Executors.newCachedThreadPool();
while(true){
//接受请求
clientSocket=serverSocket.accept();
// //利用多线程才能让服务器同时处理多个客户端的请求
// Thread t=new Thread(()->{
// //建立链接并处理请求
// try {
// createConnection(clientSocket);
// } catch (IOException e) {
// throw new RuntimeException(e);
// }
// });
// t.start();
//创建线程池相对于每次创建一个线程来说效率更高一些
threadsPool.submit(()->{
try {
createConnection(clientSocket);
} catch (IOException e) {
throw new RuntimeException(e);
}
});
}
}
public void createConnection(Socket clientSocket) throws IOException {
System.out.printf("[%s:%d]建立链接成功\n",clientSocket.getInetAddress().toString(),clientSocket.getPort());
//三个步骤
//1.读取客户端请求(根据打开的文件流确定了读取的是客户端发来的请求)
//这里针对TCP的读写和对于文件的读写是一摸一样的
//利用socket构造文件流
try(InputStream inputStream=clientSocket.getInputStream()){//注意打开的流
//直接利用scanner读取(利用原生的InputStream也是可以的,但Scanner更方便)
Scanner scanner=new Scanner(inputStream);
try(OutputStream outputStream=clientSocket.getOutputStream()){//注意打开的流
while(true){
if(!scanner.hasNext()){
System.out.printf("[%s:%d]断开链接\n",clientSocket.getInetAddress().toString(),clientSocket.getPort());
break;
}
//读取请求(TCP以字符流进行传输)
// 读到空白符/ 空格/换行才会停止
String request=scanner.next();
//2.根据请求计算响应
String response=process(request);
//3.返回响应(根据打开的文件流决定了是往客户端返回请求)
//为了方便用PrintWriter对OutputStream进行包裹
PrintWriter printWriter=new PrintWriter(outputStream);
// 因为使用 next,读到空白符/ 空格/换行才会停止,所以须使用 println
printWriter.println(response);
printWriter.flush();
System.out.printf("[%d][req:%s resp:%s]\n",clientSocket.getPort(),request,response);
}
}
}finally {
clientSocket.close();//记得及时关闭
}
}
public String process(String request){
return request;
}
public static void main(String[] args) throws IOException {
TcpEchoServer tcpEchoServer=new TcpEchoServer(9090);
tcpEchoServer.start();
}
}
客户端代码:
javascript
class TcpEchoClient {
public Socket client;
//TCP中客户端构造函数的ip和port指的是要链接的服务器的IP和port
public TcpEchoClient(String serverIp, int serverPort) throws IOException {
this.client = new Socket(serverIp, serverPort);
}
public void start() throws IOException {
System.out.println("和服务器建立链接成功");
Scanner scanner = new Scanner(System.in);
//这里针对TCP的读写和对于文件的读写是一摸一样的
//利用socket构造文件流
try (InputStream inputStream = client.getInputStream()) {
try (OutputStream outputStream = client.getOutputStream()) {
//接收从控制台输入的字符串
while (true) {
System.out.println("->");
String request = scanner.next();
//构造请求并发送请求(PrintWriter和Scanner对应)//注意文件流
PrintWriter printWriter = new PrintWriter(outputStream);
// 因为使用 next,读到空白符/ 空格/换行才会停止,所以须使用 println
printWriter.println(request);
printWriter.flush();//如果不及时刷新,服务器可能不能及时接收到数据
//接收响应
Scanner respScanner = new Scanner(inputStream);
String response = respScanner.next();
//显示到控制台上
System.out.printf("[req:%s resp:%s]\n", request, response);
}
}
}
}
public static void main(String[] args) throws IOException {
TcpEchoClient tcpEchoClient = new TcpEchoClient("127.0.0.1", 9090);
tcpEchoClient.start();
}
}
注意:当然要先启动服务器再启动客户端!
好啦! 以上就是对 TCP Socket编程的讲解,希望能帮到你 !
评论区欢迎指正 !