浙江省嘉兴市建设局网站,网站推广优化业务,温州建设网站公司哪家好,展览公司设计费前言 在上一节内容中#xff0c;我们介绍了什么是套接字#xff0c;以及使用UDP数据报套接字网络编程#xff0c; 最后我们还介绍了Java数据报套接字通信模型以及相关代码实例。在这一节我们将会介绍TCP流套接字编程。 一、流套接字及通信模型
1.1 TCP套接字 TCP#xff0…前言 在上一节内容中我们介绍了什么是套接字以及使用UDP数据报套接字网络编程 最后我们还介绍了Java数据报套接字通信模型以及相关代码实例。在这一节我们将会介绍TCP流套接字编程。 一、流套接字及通信模型
1.1 TCP套接字 TCP即Transmission Control Protocol传输控制协议是传输层协议。
TCP主要特点(会在后续单独章节中详细介绍) 有连接可靠传输面向字节流有接收、发送缓冲区大小不限 对于字节流来说可以简单的理解为传输数据是基于IO流流式数据的特征就是在IO流没有关闭的情况下是无边界的数据可以多次发送也可以分开多次接收。 1.2 原始套接字 原始套接字用于自定义传输层协议用于读写内核没有处理的IP协议数据。
1.3 Java流套接字通信模型 注意事项
客户端和服务端开发时经常是基于一个主机开启两个进程作为客户端和服务端但真实的场景一般都是不同主机。注意目的IP和目的端口号标识了一次数据传输时要发送数据的终点主机和进程Socket编程我们是使用流套接字和数据报套接字基于传输层的TCP或UDP协议但应用层协议也需要考虑。端口占用问题如果一个进程A已经绑定了一个端口再启动一个进程B绑定该端口就会报错这种情况也叫端口被占用。解决端口被占用的问题 如果占用端口的进程A不需要运行就可以关闭A后再启动需要绑定该端口的进程B 如果需要运行A进程则可以修改进程B的绑定端口换为其他没有使用的端口。
二、TCP流套接字编程
2.1 ServerSocket API ServerSocket 是创建TCP服务端Socket的APIServerSocket构造方法如下 另外ServerSocket 相关方法如下
2.2 Socket API Socket 是客户端Socket或服务端中接收到客户端建立连接accept方法的请求后返回的服务端Socket。不管是客户端还是服务端Socket都是双方建立连接以后保存的对端信息及用来与对方收发数据的。 Socket的构造方法如下 Socket相关方法
2.3 TCP中的长短连接
TCP发送数据时需要先建立连接什么时候关闭连接就决定是短连接还是长连接 短连接每次接收到数据并返回响应后都关闭连接短连接只能一次收发数据。 长连接不关闭连接一直保持连接状态双方不停的收发数据长连接可以多次收发数据。 TCP长短连接对比 建立连接、关闭连接的耗时短连接每次请求、响应都需要建立连接关闭连接而长连接只需要第一次建立连接之后的请求、响应都可以直接传输。相对来说建立连接关闭连接也是要耗时的长连接效率更高。主动发送请求不同短连接一般是客户端主动向服务端发送请求而长连接可以是客户端主动发送请求也可以是服务端主动发。两者的使用场景有不同短连接适用于客户端请求频率不高的场景如浏览网页等。长连接适用于客户端与服务端通信频繁的场景如聊天室实时游戏等。 三、代码实例
3.1 TCP 客户端
package network;import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.lang.ref.SoftReference;
import java.net.Socket;
import java.util.Scanner;/*** author Zhang* date 2024/5/2115:13* Description:*/
public class TcpEchoClient {private Socket socket null;public TcpEchoClient(String serverIP,int serverPort) throws IOException {// 此时需要在创建 Socket 的同时和服务器”建立连接“此时就得告诉 Socket 服务器在哪里socket new Socket(serverIP,serverPort);}public void start(){Scanner scanner new Scanner(System.in);try(InputStream inputStream socket.getInputStream();OutputStream outputStream socket.getOutputStream()){PrintWriter writer new PrintWriter(outputStream);Scanner scannerNetwork new Scanner(inputStream);while (true){// 1. 从控制台读取用户输入的内容System.out.print(--);String request scanner.next();// 2. 把字符串作为请求发给服务器// 这里用println是为了让后后面请求带上换行也就是和服务器读取请求scanner.next 呼应writer.println(request);writer.flush();// 3. 读取服务器返回的响应String response scannerNetwork.next();// 4. 在界面上显示内容System.out.println(response);}} catch (IOException e) {throw new RuntimeException(e);}}public static void main(String[] args) throws IOException {TcpEchoClient client new TcpEchoClient(127.0.0.1,9090);client.start();}
}3.2 TCP 服务端
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;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;/*** author Zhang* date 2024/5/2115:13* Description:*/
public class TcpEchoServer {private ServerSocket serverSocket null;public TcpEchoServer(int port) throws IOException {serverSocket new ServerSocket(port);// 指定端口号}public void start() throws IOException {System.out.println(服务器启动);ExecutorService service Executors.newCachedThreadPool(); //创建线程池while (true){//通过 accept 方法把内核中已经建立好的连接那回到应用程序中Socket clientSocket serverSocket.accept();/*** 此处不能直接调用processConnection这会导致服务器不能处理多个客户端通过创建新的线程来调用是更合理的方法* Thread t new Thread(()-{* try {* processConnection(clientSocket);* } catch (IOException e) {* throw new RuntimeException(e);* }* });* t.start();*///使用创建线程的方式开销会比较大此处我们使用线程池的方式service.submit(new Runnable() {Overridepublic void run() {try {processConnection(clientSocket);} catch (IOException e) {throw new RuntimeException(e);}}});}}// 通过这个方法来处理当前的连接private void processConnection(Socket clientSocket) throws IOException {//进入方法先打印一个日志System.out.printf([%s:%d] 客户端上线\n,clientSocket.getInetAddress(),clientSocket.getPort());//InputStream 和OutputStream就是在字节流可以借助这两个对象完成数据的“发送”和“接收”//通过InputStream 进行read 操作就是“接收”操作站在CPU角度//通过OutputStream 进行 writ额操作就是“发送”操作。try(InputStream inputStream clientSocket.getInputStream();OutputStream outputStream clientSocket.getOutputStream()){//使用try方式避免后续使用完了流对象忘记关闭//由于客户端发来的可能是“多条数据”针对对条数据进行循环处理while (true){Scanner scanner new Scanner(inputStream);if (!scanner.hasNext()){//连接断开了此时循环就应该结束System.out.printf([%s:%d] 客户端下线!\n,clientSocket.getInetAddress(),clientSocket.getPort());break;}// 1. 读取请求并解析 此处就以next来作为读取请求的方式next 的规则读取到“空白符”就返回。String request scanner.next();// 2. 根据请求计算响应String response process(request);//3. 把响应写回客户端// 方式一可以把String转成字符数组写入到OutputStream// 方式二使用PrintWriter 把OutputStream 包裹一下来写入字符串PrintWriter printWriter new PrintWriter(outputStream);printWriter.println(response); //此处的打印是写入到outputStream 对应的流对象中也就是写入到 clientSocketprintWriter.flush(); //刷新缓冲区如果没有此操作数据仍然可能在内存中没有被写入网卡// 4.打印一下这次请求交互的内容System.out.printf([%s:%d] req%s resp%s\n,clientSocket.getInetAddress(),clientSocket.getPort(),request,response);}}catch (IOException e){e.printStackTrace();}finally {//在这个地方进行clientSocket的关闭processConnection就是处理一个连接如果这个方法执行完毕这个连接也就处理完了clientSocket.close();}}public String process(String request){// 此处也是写的服务器响应和请求一样return request;}public static void main(String[] args) throws IOException {TcpEchoServer server new TcpEchoServer(9090);server.start();}
}启动客户端和服务器
运行结果
//客户端发送请求
TcpEchoClient--张三
张三
--
----------------------------------
//服务器响应
TcpEchoServer服务器启动
[/127.0.0.1:60107] 客户端上线
[/127.0.0.1:60107] req张三 resp张三