当前位置: 首页 > news >正文

JavaEE - 网络编程

一、网络编程基础

为什么需要网络编程?
用户在浏览器中,打开在线视频网站,如优酷看视频,实质是通过网络,获取到网络上的一个视频资源。
在这里插入图片描述
与本地打开视频文件类似,只是视频文件这个资源的来源是网络。
相比本地资源来说,网络提供了更为丰富的网络资源:
在这里插入图片描述
所谓的网络资源,其实就是在网络中可以获取的各种数据资源。
而所有的网络资源,都是通过网络编程来进行数据传输的。

1.1、什么是网络编程

网络编程,指网络上的主机,通过不同的进程,以编程的方式实现网络通信(或称为网络数据传输)。

在这里插入图片描述

当然,我们只要满足进程不同就行;所以即便是同一个主机,只要是不同进程,基于网络来传输数据,也属于网络编程。

特殊的,对于开发来说,在条件有限的情况下,一般也都是在一个主机中运行多个进程来完成网络编程。

但是,我们一定要明确,我们的目的是提供网络上不同主机,基于网络来传输数据资源:

  • 进程A:编程来获取网络资源
  • 进程B:编程来提供网络资源

1.2、网络编程中的基本概念

发送端和接收端
在一次网络数据传输时:

  • 发送端:数据的发送方进程,称为发送端。发送端主机即网络通信中的源主机。
  • 接收端:数据的接收方进程,称为接收端。接收端主机即网络通信中的目的主机。
  • 收发端:发送端和接收端两端,也简称为收发端。

注意:发送端和接收端只是相对的,只是一次网络数据传输产生数据流向后的概念。
在这里插入图片描述

请求和响应
一般来说,获取一个网络资源,涉及到两次网络数据传输:

  • 第一次:请求数据的发送
  • 第二次:响应数据的发送。

好比在快餐店点一份炒饭:
先要发起请求:点一份炒饭,再有快餐店提供的对应响应:提供一份炒饭
在这里插入图片描述

客户端和服务端

  • 服务端:在常见的网络数据传输场景下,把提供服务的一方进程,称为服务端,可以提供对外服务。
  • 客户端:获取服务的一方进程,称为客户端。

对于服务来说,一般是提供:
客户端获取服务资源
在这里插入图片描述
客户端保存资源在服务端
在这里插入图片描述
好比在银行办事:

  • 银行提供存款服务:用户(客户端)保存资源(现金)在银行(服务端)
  • 银行提供取款服务:用户(客户端)获取服务端资源(银行替用户保管的现金)

常见的客户端服务端模型
最常见的场景,客户端是指给用户使用的程序,服务端是提供用户服务的程序:

  1. 客户端先发送请求到服务端
  2. 服务端根据请求数据,执行相应的业务处理
  3. 服务端返回响应:发送业务处理结果
  4. 客户端根据响应数据,展示处理结果(展示获取的资源,或提示保存资源的处理结果)

在这里插入图片描述

二、Socket套接字

Socket套接字,是由系统提供用于网络通信的技术,是基于TCP/IP协议的网络通信的基本操作单元。基于Socket套接字的网络程序开发就是网络编程。

2.1、Socket套接字分类

Socket套接字主要针对传输层协议划分为如下三类:

  • 1、流套接字:使用传输层TCP协议
    TCP,即Transmission Control Protocol(传输控制协议),传输层协议。
    以下为TCP的特点(细节后续再学习)
    有连接
    可靠传输
    面向字节流
    有接收缓冲区,也有发送缓冲区
    大小不限

对于字节流来说,可以简单的理解为,传输数据是基于IO流,流式数据的特征就是在IO流没有关闭的情
况下,是无边界的数据,可以多次发送,也可以分开多次接收。

  • 2、数据报套接字:使用传输层UDP协议
  • UDP,即User Datagram Protocol(用户数据报协议),传输层协议。
    以下为UDP的特点(细节后续再学习):
    无连接
    不可靠传输
    面向数据报
    有接收缓冲区,无发送缓冲区
    大小受限:一次最多传输64k

对于数据报来说,可以简单的理解为,传输数据是一块一块的,发送一块数据假如100个字节,必须一
次发送,接收也必须一次接收100个字节,而不能分100次,每次接收1个字节

  • 3、原始套接字
    原始套接字用于自定义传输层协议,用于读写内核没有处理的IP协议数据。
    我们不学习原始套接字,简单了解即可。

在这里插入图片描述
接下来先来了解TCP和UDP两种协议

2.2、TCP 与 UDP 的 区别

在这里插入图片描述

有连接 和 无连接

可以怎么去理解:
有链接:像打电话

比如说:现在我们要打电话给某个朋友。
输入号码,按下手机拨号键。
手机开始发出 嘟嘟嘟 声音,开始等待对方接听,

而且,我们拨号之后,并不是马上就能接通的!
必须要等待 对方接听之后,我们才能与其交流。

之所以说:有链接 就像 打电话一样,是因为 打电话,必须要接通了之后,才能交流;没有接通,双方就无法交流。
有连接的意思:就是在两者确认建立联系后,就可以开始交互了。

无连接:发微信

不需要接通,直接就能发数据。
发微信,我们都知道:发送信息的时候,是不需要对方在线或者回复,按下回车,立马就能加个信息发送出去,不过 对方 看没看见这条消息,我们是不确定的 。
这种情况,就叫做无连接。

所以 TCP,就是要求双发先建立连接,连接好了,才能进行传数据。
而 UDP,直接传输数据,不需要双方建立连接。

可靠传输 和 不可靠传输

可靠传输:发送方 知道 接收方 有没有接收到数据

注意!不要理解错了。
可靠传输,不是说数据发送之后,对方100% 就能收到。
你代码写得再好,也刚不住挖掘机把你家网线挖断了。
网线都断了,你能把数据发出去才有鬼。

可靠传输,不是说传输数据百分百成功,关键还得看这里面是否能感知到 传输数据成功了。

关于可靠传输,还有一种错误理解。
可靠传输,就是“安全传输”。这种说法也是一个典型的错误。
可靠 和 安全 是 两码事!!!!

安全,指的是 数据在传输过程,不容易被黑客窃取,不容易被篡改。
可靠,指的是 数据发给对方,发送方能知道接收方有没有收到数据。
在这里插入图片描述

不可靠传输:发送方 不知道 接收方有没有接收到数据
在这里插入图片描述

总得来说:
可靠,就是我们对于自己发送的信息,心里有点数。
心里没底,就是不可靠。

面向字节流 与 面向数据报

面向字节流:数据是以字节为单位,进行传输的。

这个就非常类似于 文件操作中的文件内容相关的操作 中的字节流。
网络传输也是一样!
假设,现有100个字节的数据。
我们可以一直发完。
也可以 一次发 10个字节,发送十次。
也可以 一次发 2 个字节,发送50次。

面向数据报:
以数据报为单位,进行传输。

一个数据报都会明确大小。
一次 发送/接收 必须是 一个 完整的数据报。
不能是半个,也不能是一个半,必须是整数个。

在代码中,这两者的区别是非常明显的!

全双工

全双工 对应的是 半双工

全双工:一条链路,双向通信。

举个例子:间谍
通常抓到一个间谍,都会对其进行拷问。
说:你的上级是谁?平时是怎么联系的?
间谍:我和他认识,知道彼此身份,并且有相互联系的方式。
      他是xxx,联系方式xxxxxx。所以别再打我,作用不大,因为我都会说。

半双工:一条链路,单向通信。

举个例子:间谍
通常抓到一个间谍,都会对其进行拷问。
说:你的上级是谁?平时是怎么联系的?
间谍:我和上级是单向通信的,他联系到我,我联系不到他。所以别再打我,作用不大

TCP 和 UDP 都是全双工。
半双工理解即可。

三、UDP数据报套接字编程

在这里插入图片描述

3.1、DatagramSocket API

DatagramSocket API
DatagramSocket 是UDP Socket,用于发送和接收UDP数据报。

DatagramSocket 构造方法

方法签名方法说明
DatagramSocket()创建一个UDP数据报套接字的Socket,绑定到本机任意一个随机端口(一般用于客户端)
DatagramSocket(intport)创建一个UDP数据报套接字的Socket,绑定到本机指定的端口(一般用于服务端)

DatagramSocket 方法

方法签名方法说明
void receive(DatagramPacket p)从此套接字接收数据报(如果没有接收到数据报,该方法会阻塞等待)
void send(DatagramPacket p)从此套接字发送数据报包(不会阻塞等待,直接发送)
void close()关闭此数据报套接字

3.2、DatagramPacket API

DatagramPacket 是 UDP Socket 发送和接收的数据报。

DatagramPacket 构造方法

方法签名方法说明
DatagramPacket(byte[] buf, int length)构造一个DatagramPacket以用来接收数据报,接收的数据保存在字节数组(第一个参数buf)中,接收指定长度(第二个参数length)
DatagramPacket(byte[] buf, int offset, int length, SocketAddress address)构造一个DatagramPacket以用来发送数据报,发送的数据为字节数组(第一个参数buf)中,从0到指定长度(第二个参数length)。address指定目的主机的IP和端口号

DatagramPacket 方法

方法签名方法说明
InetAddress getAddress()从接收的数据报中,获取发送端主机IP地址;或从发送的数据报中,获取接收端主机IP地址
int getPort()从接收的数据报中,获取发送端主机的端口号;或从发送的数据报中,获取接收端主机端口号
byte[] getData()获取数据报中的数据

构造UDP发送的数据报时,需要传入 SocketAddress ,该对象可以使用 InetSocketAddress 来创建。

3.3、InetSocketAddress API

InetSocketAddress ( SocketAddress 的子类 )构造方法:

方法签名方法说明
InetSocketAddress(InetAddress addr, int port)创建一个Socket地址,包含IP地址和端口号

3.4、示例一:写一个最简单的客户端服务器程序【回显服务】

什么是回显服务?
回显服务 - Echo Server
简单来说,我说什么,你回什么。

就像回声,重复着我们说过话。

回显服务,就是这样的。
我们发送什么样子的数据,它就给我们返回一个同样的数据。
也就是说:根据我们请求的内容数据,来返回一个具有相同数据的响应。

这样的程序属于最简单的网络编程中的程序。
因为不涉及到任何的业务逻辑,就只是通过 socket API 进行单纯的数据转发。
我通过这个程序,来向大家演示 API 的使用。

准备工作
在这里插入图片描述

服务器部分
在这里插入图片描述

package network;import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;/*** Created with Intellij IDEA.* Description:* User: ryy* Date: 2023-05-02* Time: 16:28*/
public class UdpEchoServer {//进行网络编程,首先要准备 socket 实例private DatagramSocket socket = null;//port 是服务器的端口号public UdpEchoServer(int port) throws SocketException {socket = new DatagramSocket(port);}//启动服务器public void start() throws IOException {System.out.println("启动服务器!");//UDP是不需要建立连接的,直接接收客户端发来的数据即可。while (true){//1、读取客户端发来的请求//为了接受数据,我们需要先准备好一个空的DatagramPacket对象.//然后,由receive 来填充数据。DatagramPacket requestPacket = new DatagramPacket(new byte[1024],1024);socket.receive(requestPacket);//把 DatagramPacket 对象构造出一个字符串String request = new String(requestPacket.getData(),0, requestPacket.getLength(),"UTF-8");//2、根据请求计算响应(由于咱们这是一个回显服务,这一步就可以省略了)String response = process(request);//3、把响应写回客户端DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),response.getBytes().length,requestPacket.getSocketAddress());socket.send(responsePacket);// 打印具体 IP、端口、请求、响应System.out.printf("[%s:%d] request: %s,response: %s\n",requestPacket.getAddress().toString(),// 客户端IPrequestPacket.getPort(),// 客户端端口号request,//请求response);// 响应}}//由于是 回显服务,响应和请求是一样的private String process(String request) {return request;}public static void main(String[] args) throws IOException {UdpEchoServer server = new UdpEchoServer(9090);server.start();}
}

运行效果
在这里插入图片描述

客户端部分
在这里插入图片描述

package network;import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.util.Scanner;/*** Created with Intellij IDEA.* Description:* User: ryy* Date: 2023-05-02* Time: 16:28*/
public class UdpEchoClient {private DatagramSocket socket = null;private String serverIP;private int serverPort;public UdpEchoClient(String ip, int port) throws SocketException {//此处的 serverPort 是服务器的端口// 服务器启动的时候,不需要 socket来指定窗口,客户端自己的端口是系统随机分配的。socket = new DatagramSocket();serverIP = ip;serverPort = port;}//启动客户端public void start() throws IOException {Scanner scanner = new Scanner(System.in);while (true){//1、先从控制台上读取用户输入的字符串System.out.println("用户输入字符:");String request = scanner.next();//2、把用户输入的内容,构造成一个 UDP 并发送//   构造的请求包括两个部分//   1)数据的内容 -》 request//   2)要发给谁,服务器的 IP + 端口号DatagramPacket requestPacket = new DatagramPacket(request.getBytes(),request.getBytes().length,InetAddress.getByName(serverIP),serverPort);socket.send(requestPacket);//3、从服务器读取响应数据,并解析DatagramPacket responsePacket = new DatagramPacket(new byte[1024],1024);socket.receive(responsePacket);String response = new String(responsePacket.getData(),0,responsePacket.getLength(),"UTF-8");//4、把解析的内容显示到控制台System.out.printf("[%s:%d] request: %s,response: %s\n",serverIP,// 服务器IPserverPort,// 服务器端口request,//请求response);// 响应}}public static void main(String[] args) throws IOException {UdpEchoClient client = new UdpEchoClient("127.0.0.1",9090);client.start();}
}

在这里插入图片描述

客户端 和 服务器交互效果
在这里插入图片描述
这才是我们服务器正常的运行状态,同时处理多个客户端发送的请求。

再来写一个简单程序:就是上面代码的基础上,带上点业务逻辑

写一个翻译程序(英译汉)
请求是一些简单的英文单词。
响应是 英文单词 对应的 中文翻译。
客户端不变,把服务器代码进行调整。
主要是调整 process 方法。
其他步骤都是一样的。
关键的逻辑就是“根据想求来处理响应”
在这里插入图片描述

package network;import java.io.IOException;
import java.net.SocketException;
import java.util.HashMap;
import java.util.Map;/*** Created with Intellij IDEA.* Description:* User: ryy* Date: 2023-05-02* Time: 22:02*/// 创建一个类 UdpDictionaryServer 来表示  字典服务器
// 因为代码的逻辑几乎是一样,且所有的办法都是public的
// 所以我们在这里就直接继承,就可以使用内部所有的方法,并且可以进行重写操作。
public class UdpDictionaryServer extends UdpEchoServer{private Map<String,String> map = new HashMap<>();public UdpDictionaryServer(int port) throws SocketException {super(port);map.put("dog","小狗");map.put("cat","小猫");map.put("duck","小鸭子");}@Overridepublic String process(String request) {// 如果查询的单词在“词库”中存在,就返回其 键值对/对应的中文,//反之,如果查询的单词在 “词库”中 不存在,返回 没有对应的词。return map.getOrDefault(request,"该词没有翻译!");}public static void main(String[] args) throws IOException {UdpDictionaryServer server = new UdpDictionaryServer(9090);server.start();}
}

代码运行结果
在这里插入图片描述

总结

一个服务器,最关键的逻辑就是“根据想求来处理响应”!
什么样的请求,得到什么样的响应。
这是我们一个服务器要完成的一个最最关键的事情。
通过这个东西,才能让我们的程序真正帮我们解决一些实际问题。
这一点,大家要体会我们 服务器-客户端 的交互过程。

之所以,网络编程 是一个 服务器-客户端的结构,是因为 有些工作,我们希望让服务器完成一些工作,既然要完成这样的工作,就得有输入(请求),也有输出(响应)。
从输入到输出,从请求到响应的这个过程,这就是服务器要完成的基本工作。

四、TCP流套接字编程

TCP 和 UDP 的差别很大!
在 TCP API 中,也是涉及到两个核心的类
在这里插入图片描述

4.1、ServerSocket API

4.1.1、ServerSocket 构造方法

ServerSocket 是创建TCP服务端Socket的API。

方法签名方法说明
ServerSocket(int port)创建一个服务端流套接字Socket,并绑定到指定端口

4.1.2、ServerSocket 方法

方法签名方法说明
Socketaccept()开始监听指定端口(创建时绑定的端口),有客户端连接后,返回一个服务端Socket对象,并基于该Socket建立与客户端的连接,否则阻塞等待
void close()关闭此套接字

4.3、Socket API

Socket 是客户端Socket,或服务端中接收到客户端建立连接(accept方法)的请求后,返回的服务端Socket。

不管是客户端还是服务端Socket,都是双方建立连接以后,保存的对端信息,及用来与对方收发数据的。

4.3.1、Socket 构造方法

方法签名方法说明
Socket(String host, int port)创建一个客户端流套接字Socket,并与对应IP的主机上,对应端口的进程建立连接

4.3.2、Socket 方法

方法签名方法说明
InetAddress getInetAddress()返回套接字所连接的地址
InputStream getInputStream()返回此套接字的输入流
OutputStream getOutputStream()返回此套接字的输出流

4.4、回显服务器 - TCP版

TCP服务器实现
在这里插入图片描述

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;/*** Created with Intellij IDEA.* Description:* User: ryy* Date: 2023-05-02* Time: 22:31*/
public class TcpEchoServer {// listen 的 中文意思是 监听// 但是,在Java socket 中是体现不出来 “监听”的含义// 之所以这么叫,其实是 操作系统原生的 API 里有一个操作叫做 listen// 而 ServerSocket 确实起到了一个监听的效果// 所以,取个 listenSocket 的名字private ServerSocket listenSocket = null;public TcpEchoServer(int port) throws IOException {listenSocket = new ServerSocket(port);}//启动服务器public void start() throws IOException {System.out.println("启动服务器!");while (true){//注意,UDP 是无连接,所以一上来就发送数据报//而TCP是有连接的,要先建立连接,连接好之后才能发送数据//accept 就是用来建立连接的,如果没有客户端建立连接,那服务器就会阻塞//accept 返回了一个Socket 对象,称为 clientSocket ,后续和客户端的沟通都是通过 clientSocket 来完成的//换句话说,ServerSocket 就干了一件事, 建立连接Socket clientSocket = listenSocket.accept();//处理连接之后的客户端的请求processConnection(clientSocket);}}private void processConnection(Socket clientSocket) {//先打印IP 和 端口号System.out.printf("[%s : %d]  客户端建立连接!",clientSocket.getInetAddress().toString(),clientSocket.getPort());//接下来就是处理响应和请求//此处 处理 TCP Socket 文件读写 和 普通文件的读写是一样的try(InputStream inputStream = clientSocket.getInputStream()){try (OutputStream outputStream = clientSocket.getOutputStream()){//循环处理每个请求Scanner scanner = new Scanner(inputStream);while (true){if(!scanner.hasNext()){System.out.printf("[%s : %d] 客户端断开连接!",clientSocket.getInetAddress().toString(),clientSocket.getPort());break;}//此处使用 Scanner 更方便// 如果不用 Scanner,而使用原生的 InputStream 的 read 也是可以的。// 但是很麻烦!它需要构造一个 字节数组 来存储 read 读取的数据。//  read 还会返回字节的个数,如果为-1,即为没有后续数据了,读完了。String request = scanner.next();//2、根据请求 计算响应String response = process(request);//3、把响应返回给客户端//为了方便 用 PrintWriter 把 outputStream 包裹一下PrintWriter printWriter = new PrintWriter(outputStream);printWriter.print(response);//刷新缓冲区printWriter.flush();System.out.printf("[%s : %d]  request -》 %s  response -》 %s\n",clientSocket.getInetAddress().toString(),clientSocket.getPort(),request,response);}}} catch (IOException e) {e.printStackTrace();}finally {try {//此处用来关闭文件clientSocket.close();} catch (IOException e) {e.printStackTrace();}}}private String process(String request) {return  request;}public static void main(String[] args) throws IOException {TcpEchoServer server = new TcpEchoServer(9090);server.start();}
}

TCP客户端实现

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;/*** Created with Intellij IDEA.* Description:* User: ryy* Date: 2023-05-03* Time: 21:36*/
public class TcpEchoClient {// 用普通的 Socket 即可,不用 ServerSocket 了private Socket socket = null;//此处也不用手动给客户端指定端口号,由系统自动分配(隐式)public TcpEchoClient(String serverIP,int serverPort) throws IOException {// 其实这里是可以给定端口号的,但是这里给了之后,含义是不同的。// 这里传入的 IP 与 端口号 的 含义: 表示的不是自己绑定,而是表示 和 这个IP 端口 建立连接// 这里表示 与 IP 为serverIP的主机上的 端口为9090的程序,建立连接。socket = new Socket(serverIP,serverPort);}private void start(){System.out.println("和服务器建立连接成功!");Scanner scanner = new Scanner(System.in);try(InputStream inputStream = socket.getInputStream()){try (OutputStream outputStream = socket.getOutputStream()){while (true){//客户端还是4个步骤//1、从控制台上输入字符串System.out.println("请输入请求:");String request = scanner.next();//2、把字符串构造成请求,然后发送请求给服务器PrintWriter printWriter = new PrintWriter(outputStream);printWriter.println(request);printWriter.flush();//3、服务器响应请求,并解析Scanner scaResponse = new Scanner(inputStream);String response = scaResponse.next();//4、将响应显示到控制台上System.out.printf("request:%s,response:%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();}
}

此是TCP版本的回显服务写完了,但是还会出现一些问题,UDP版本的可以多个客户端与服务器建立连接,但是TCP就不行,下面我们看一下出现的问题

多线程版本的回显服务器程序

客户端代码和上面一样,只需要改一下服务器里面的代码

当前的服务器,同一时刻只能处理一个客户端连接。
作为一个服务器应该给很多客户端提供服务,而这里只能处理一个客户端,这显然是不科学的。

在这里插入图片描述

public class TcpThreadEchoServer {// listen 的 中文意思是 监听// 但是,在Java socket 中是体现不出来 “监听”的含义// 之所以这么叫,其实是 操作系统原生的 API 里有一个操作叫做 listen// 而 ServerSocket 确实起到了一个监听的效果// 所以,取个 listenSocket 的名字private ServerSocket listenSocket = null;public TcpThreadEchoServer(int port) throws IOException {listenSocket = new ServerSocket(port);}//启动服务器public void start() throws IOException {System.out.println("启动服务器!");while (true){//注意,UDP 是无连接,所以一上来就发送数据报//而TCP是有连接的,要先建立连接,连接好之后才能发送数据//accept 就是用来建立连接的,如果没有客户端建立连接,那服务器就会阻塞//accept 返回了一个Socket 对象,称为 clientSocket ,后续和客户端的沟通都是通过 clientSocket 来完成的//换句话说,ServerSocket 就干了一件事, 建立连接Socket clientSocket = listenSocket.accept();//处理连接之后的客户端的请求//改进方法 - 就是在客户端建立连接的时候,创建一个线程执行这里的 processConnection 方法Thread t = new Thread(() -> {processConnection(clientSocket);});t.start();}}private void processConnection(Socket clientSocket) {//先打印IP 和 端口号System.out.printf("[%s : %d]  客户端建立连接!\n",clientSocket.getInetAddress().toString(),clientSocket.getPort());//接下来就是处理响应和请求//此处 处理 TCP Socket 文件读写 和 普通文件的读写是一样的try(InputStream inputStream = clientSocket.getInputStream()){try (OutputStream outputStream = clientSocket.getOutputStream()){//循环处理每个请求Scanner scanner = new Scanner(inputStream);while (true){if(!scanner.hasNext()){System.out.printf("[%s : %d] 客户端断开连接!\n",clientSocket.getInetAddress().toString(),clientSocket.getPort());break;}//此处使用 Scanner 更方便// 如果不用 Scanner,而使用原生的 InputStream 的 read 也是可以的。// 但是很麻烦!它需要构造一个 字节数组 来存储 read 读取的数据。//  read 还会返回字节的个数,如果为-1,即为没有后续数据了,读完了。String request = scanner.next();//2、根据请求 计算响应String response = process(request);//3、把响应返回给客户端//为了方便 用 PrintWriter 把 outputStream 包裹一下PrintWriter printWriter = new PrintWriter(outputStream);printWriter.println(response);//刷新缓冲区printWriter.flush();System.out.printf("[%s : %d]  request -》 %s  response -》 %s\n",clientSocket.getInetAddress().toString(),clientSocket.getPort(),request,response);}}} catch (IOException e) {e.printStackTrace();}finally {try {//此处用来关闭文件clientSocket.close();} catch (IOException e) {e.printStackTrace();}}}private String process(String request) {return  request;}public static void main(String[] args) throws IOException {TcpThreadEchoServer server = new TcpThreadEchoServer(9090);server.start();}
}

效果图
在这里插入图片描述

相关文章:

JavaEE - 网络编程

一、网络编程基础 为什么需要网络编程&#xff1f; 用户在浏览器中&#xff0c;打开在线视频网站&#xff0c;如优酷看视频&#xff0c;实质是通过网络&#xff0c;获取到网络上的一个视频资源。 与本地打开视频文件类似&#xff0c;只是视频文件这个资源的来源是网络。 相比本…...

【Android车载系列】第11章 系统服务-SystemServer自定义服务

1 编写自定义系统服务 1.1 AIDL接口定义 系统源码目录/frameworks/base/core/java/android/app/下新建AIDL接口IYvanManager.aidl package android.app;/** * 目录&#xff1a;/frameworks/base/core/java/android/app/IYvanManager.aidl */ interface IYvanManager{String …...

Lerna

Lerna Lerna是一个优化基于gitnpm的多pagkage项目的管理工具 解决的痛点 痛点一:重复操作 多Package本地link多Package依赖安装多Package单元测试多Package代码提交多Package代码发布 痛点二:版本一致性 发布时版本一 致性发布后相互依赖版本升级 package越多&#xff0c;管…...

迁移学习 pytorch

迁移学习(Transfer Learning)是通过使用一个预训练模型来快速训练一个新的网络模型,通常应用于数据集较小或计算资源较少的情况下。在 PyTorch 中,由于 torchvision 库中已经内置了一些经典的预训练模型,因此我们可以通过简单的调用函数来实现迁移学习。 下面是一个基于 …...

【python】keras包:深度学习( RNN循环神经网络 Recurrent Neural Networks)

RNN循环神经网络 应用&#xff1a; 物体移动位置预测、股价预测、序列文本生成、语言翻译、从语句中自动识别人名、 问题总结 这类问题&#xff0c;都需要通过历史数据&#xff0c;对未来数据进行预判 序列模型 两大特点 输入&#xff08;输出&#xff09;元素具有顺序关系…...

vue框架快速入门

vue 1、第一个Vue程序1.1、什么是Vue程序1.2、为什么要使用MVVM1.3、Vue1.4、第一个vue程序 2、基础语法2.1、v-bind2.2、v-if&#xff0c; v-else2.3、v-for2.4、v-on 3、Vue表单双绑、组件3.1、什么是双向数据绑定3.2、在表单中使用双向数据绑定3.3、什么是组件 4、Axios异步…...

Java连接顺丰开放平台

今天使用Java去访问顺丰的开放平台时&#xff0c;JSON转换一直不成功&#xff0c;最终发现是 可以看到这里是 "apiResultData": "{\"success\": .........它是以 " 开头的&#xff01;&#xff01;&#xff01;如果是对象的话&#xff0c;那么…...

前端三剑客 - HTML

前言 前面都是一些基础的铺垫&#xff0c;现在就正式进入到web开发环节了。 我们的目标就是通过学习 JavaEE初阶&#xff0c;搭建出一个网站出来。 一个网站分成两个部分&#xff1a; 前端&#xff08;客户端&#xff09; 后端&#xff08;服务器&#xff09; 通常这里的客户端…...

【计算机视觉 | 自然语言处理】BLIP:统一视觉—语言理解和生成任务(论文讲解)

文章目录 一、前言二、试玩效果三、研究背景四、模型结构五、Pre-training objectives六、CapFilt架构七、Experiment八、结论 一、前言 今天我们要介绍的论文是 BLIP&#xff0c;论文全名为 Bootstrapping Language-Image Pre-training for Unified Vision-Language Understa…...

c++基础-运算符

目录 1关系运算符 2运算符优先级 3关系表达式的书写 代码实例&#xff1a; 下面是面试中可能遇到的问题&#xff1a; 1关系运算符 C中有6个关系运算符&#xff0c;用于比较两个值的大小关系&#xff0c;它们分别是&#xff1a; 运算符描述等于!不等于<小于>大于<…...

美术馆c++

题目&#xff1a; 杜老师非常喜欢玩一种叫做“美术馆”的数字游戏&#xff0c;蜗蜗看了之后决定也来试一试&#xff0c;他改编了这个游戏&#xff0c;规则如下&#xff1a; 有一个 n&#xfffd; 行 m&#xfffd; 列的方格&#xff0c;每一个格子中有一个数&#xff0c;数字…...

浅谈MySQL索引以及执行计划

MySQL索引及执行计划 &#x1f42a;索引的作用&#x1f42b;索引的分类&#xff08;算法&#xff09;&#x1f999;BTREE索引算法演变&#x1f992;Btree索引功能上的分类4.1 辅助索引4.2 聚集索引4.3 辅助索引和聚集索引的区别 &#x1f418;辅助索引分类&#x1f98f;索引树高…...

在c++项目中使用rapidjson(有具体的步骤,十分详细) windows10系统

具体的步骤&#xff1a; 先下载rapidjson的依赖包 方式1&#xff1a;直接使用git去下载 地址&#xff1a;git clone https://github.com/miloyip/rapidjson.git 方式2&#xff1a;下载我上传的依赖包 将依赖包引入到项目中 1 将解压后的文件放在你c项目中 2 将rapidjson文…...

编译方式汇总:Makefile\configure\autogen.sh\configure.ac、Makefile.am文件

一、前言 文章目的&#xff1a;针对各种开源项目&#xff0c;由于部分项目文档写的不够详细&#xff0c;&#xff08;或者是我太菜了&#xff09;&#xff0c;没有进行详细的介绍怎么编译该项目&#xff0c;导致花费过多时间在查找如何编译该项目上。因此该篇文章针对目前遇到的…...

explicit关键字

explicit关键字只能用来修饰构造函数。使用explicit可以禁止编译器自动调用拷贝初始化&#xff0c;还可以禁止编译器对拷贝函数的参数进行隐式转换。 那么什么是隐式转换呢&#xff1f; 类 命名 参数&#xff1b; //有参构造类 命名 命名对象&#xff1b; //拷贝构造&#x…...

[优雅的面试] 你了解python的对象吗

前情提要&#xff1a;小编面试&#xff0c;结果面试官着急去吃饭~又约了这次来面&#xff0c;不晓得又会问什么问题呢&#xff1f; 面试官大佬&#xff1a;小伙子来的挺准时的(赞赏的表情~)&#xff0c;今天咱们接着聊哈&#xff0c;小伙子&#xff0c;你有对象了没&#xff1f…...

【hello Linux】线程概念

目录 1. 线程概念的铺设 2. Linux线程概念 2.1 什么是线程 2.2 线程的优点 2.3 线程的缺点 2.4 线程异常 2.5 线程用途 3. Linux进程VS线程 4. Linux线程控制 4.1 POSIX线程库 4.2 创建线程 4.3 进程ID和线程ID 4.4 线程终止 4.5 线程等待 4.6 分离线程 Linux&#x1f337; 1…...

JavaWeb07(MVC应用01[家居商城]连接数据库)

目录 一.什么是MVC设计模式&#xff1f; 1.2 MVC设计模式有什么优点&#xff1f; 二.MVC运用&#xff08;家居商城&#xff09; 2.1 实现登录 2.2 绑定轮播【随机三个商品】 2.2.1 效果预览 index.jsp 2.3 绑定最新上架&热门家居 2.3.1 效果预览 2.3.2 代码实现 数据…...

如何使用电商API接口API接口如何应用

使用API接口 API&#xff08;应用程序接口&#xff09;是现代软件开发中必不可少的一部分&#xff0c;它通常允许软件与其他软件或服务进行交互。使用API可以大大提高软件的灵活性和可扩展性&#xff0c;并允许您轻松添加新的功能和服务&#xff0c;因此&#xff0c;API接口的…...

【移动端网页布局】流式布局案例 ⑥ ( 多排按钮导航栏 | 设置浮动及宽度 | 设置图片样式 | 设置文本 )

文章目录 一、多排按钮导航栏样式及核心要点1、实现效果2、总体布局设计3、设置浮动及宽度4、设置图片样式5、设置文本 二、完整代码实例1、HTML 标签结构2、CSS 样式3、展示效果 一、多排按钮导航栏样式及核心要点 1、实现效果 要实现下面的导航栏效果 ; 2、总体布局设计 该导…...

1. 先从云计算讲起

本章讲解知识点 什么是云计算&#xff1f; 为什么要用云计算&#xff1f; 物理服务器与云服务器对比 云计算服务类型 云计算部署类型 1. 什么是云计算&#xff1f; 云计算是一种通过计算机网络以服务的方式提供动态可伸缩的虚拟化资源的计算模式。按照服务层次分为IaaS、…...

ZooKeeper安装与配置集群

简介: ZooKeeper是一个分布式的&#xff0c;开放源码的分布式应用程序协调服务&#xff0c;是Hadoop和Hbase的重要组件。它是一个为分布式应用提供一致性服务的软件&#xff0c;它提供了一个分布式环境中的高可用性、高性能、有序访问的数据存储&#xff0c;可以让分布式应用程…...

浅谈Mysql的RR和RC隔离级别的主要区别

MySQL默认为RR级别 首先默认RR是因为mysql为了保证在主从同步过程中数据的安全的问题&#xff08;涉及到binlog三种格式&#xff09;。 就是说两个并发事务数AB&#xff0c;A先开启事物最后提交也是最后&#xff0c;事务B开启和提交都在A内部&#xff0c;由于隔离级别不同&…...

Build生成器模式

设计模式简述 设计模式的核心在于提供了相关问题的解决方案&#xff0c;使得人们可以更加简单方便地复用成功的设计和体系结构。 生成器模式&#xff08;创建型设计模式&#xff09; 意图&#xff1a;将一个复杂对象的构建与它的表示分离&#xff0c;使得同样的构建过程可以…...

C++程序设计——常见C++11新特性

一、列表初始化 1.C98中{}的初始化问题 在C98中&#xff0c;允许使用花括号{}对数组元素进行统一的列表初始化值设定&#xff0c;比如&#xff1a; 但是对于一些自定义类型&#xff0c;就无法使用这样的方式进行初始化了&#xff0c;比如&#xff1a; 就无法通过编译&#xff…...

Rust main 函数返回值类型不能是 String

是的&#xff0c;Rust 的 main 函数返回值类型不能是 String。 Rust 的 main 函数只能返回以下几种类型之一&#xff1a; ()&#xff1a;表示空类型&#xff0c;不返回任何值。i32&#xff1a;表示程序的退出码&#xff0c;通常非零值表示执行失败&#xff0c;0 表示执行成功…...

视频里的音乐怎么转换成mp3格式?

视频里的音乐怎么转换成mp3格式&#xff1f;视频里的音乐转换为mp3的原因有很多&#xff0c;主要是因为mp3格式是一种音频格式&#xff0c;文件大小较小&#xff0c;更易于存储和传输。相比之下&#xff0c;视频格式则是一种视频文件格式&#xff0c;虽然包含音频&#xff0c;但…...

CSS3 grid网格布局

文章目录 CSS3 grid网格布局概述grid属性说明使用grid-template-rows & grid-template-columns 定义行高和列宽grid-auto-flow 定义项目的排列顺序grid-auto-rows & grid-auto-columns 定义多余网格的行高和列宽row-gap & column-gap 设置行间距和列间距gap 简写形…...

SPSS如何进行均值比较和T检验之案例实训?

文章目录 0.引言1.均值过程2.单样本T检验3.独立样本T检验4.成对样本T检验 0.引言 因科研等多场景需要进行数据统计分析&#xff0c;笔者对SPSS进行了学习&#xff0c;本文通过《SPSS统计分析从入门到精通》及其配套素材结合网上相关资料进行学习笔记总结&#xff0c;本文对均值…...

Packet Tracer - 配置交换机端口安全

Packet Tracer - 配置交换机端口安全 地址分配表 设备 接口 IP 地址 子网掩码 S1 VLAN 1 10.10.10.2 255.255.255.0 PC1 NIC 10.10.10.10 255.255.255.0 PC2 NIC 10.10.10.11 255.255.255.0 非法笔记本电脑 NIC 10.10.10.12 255.255.255.0 目标 第 1 部…...

嘉善网站建设jswebs/色盲测试图片

发布一个k8s部署视频&#xff1a;https://edu.csdn.net/course/detail/26967 课程内容&#xff1a;各种k8s部署方式。包括minikube部署&#xff0c;kubeadm部署&#xff0c;kubeasz部署&#xff0c;rancher部署&#xff0c;k3s部署。包括开发测试环境部署k8s&#xff0c;和生产…...

云南定制化网站建设/网络优化工程师证书

1.删除数组中的多个元素&#xff0c;也就是去掉数组中不符合条件的选项 分析&#xff1a;用for或者forEach遍历数组的话在方法体内部 splice后数组长度发生了变化&#xff0c;最后得不到正确的结果。 filter() 方法创建一个新数组, 其包含通过所提供函数实现的测试的所有元素。…...

做类似猪八戒网的网站/广告搜索引擎

文章目录1.sudo !!2.mtr 命令3.nl 命令4.shulf 和tree 、pstreeshulf 命令tree命令pstree 这个是进程按树形结构显示&#xff0c;显示当前进程以及相关子进程&#xff0c;输出信息跟“tree”类似5.last 命令6.curl ifconfig.me7.lsof -i:端口号8.cut 命令9.seq 命令11.关于 脚本…...

7b2 wordpress/幽默软文广告经典案例

1 测试背景说明之前的博客已经对Oracle 的闪回技术进行说明,如下&#xff1a;我们现在用的最多的还是闪回查询&#xff0c;但闪回查询有时间限制&#xff0c;超过了时间就无法查询到数据&#xff0c;此时就需要通过备份来查找需要的数据。 显然用备份的方式会麻烦很多。在Oracl…...

做网站选什么系统/域名注册流程

本篇教程通过PHPstudy安装Mysql数据库。什么是phpstudy&#xff1f;phpStudy是一个PHP调试环境的程序集成包。该程序包集成最新ApachePHPMySQLphpMyAdminZendOptimizer&#xff0c;一次性安装&#xff0c;无须配置即可使用&#xff0c;是非常方便、好用的PHP调试环境。该程序不…...

杭州网站seo公司/推广发帖网站

前言本章节将使用 Go 来编写 gRPC Server 和 Client&#xff0c;让其互相通讯。在此之上会使用到如下库&#xff1a;google.golang.org/grpcgithub.com/golang/protobuf/protoc-gen-go安装gRPCgo get -u google.golang.org/grpcProtocol Buffers v3wget https://github.com/goo…...