目录
引入1
Tomcat是一个开源的Java Web服务器和Servlet容器,它遵循Servlet和JavaServer Pages(JSP)规范。Tomcat的架构主要包含两个核心组件:连接器(Connector)和容器(Container)。
那么Tomcat究竟是做什么的呢?--- 服务器开发
简而言之,一句话总结,tomcat就是一个用来 承载项目的web容器**。**
众所周知,我们自己创建的项目是不能随意访问的。
要想让网络能够随意访问这些项目,就要把它们丢进tomcat中。
上面将项目丢进tomcat的操作 ,就是实现其中配置的项目接受http请求。
使用tomcat的过程,就是实现以下路径的转变:
经过上述简单概念,对tomcat的作用和概念有了简单的了解,接下来引入另一个重要概念:Socket。
引入2--socket
如下的关系图是两个应用程序(或两台计算机)之间传输数据的构造图:
流程
简述不同计算机程序之间是如何互传信息的流程:
本台应用程序A输出一些数据信息,并且通过socket传输给本台操作系统对数据进行处理后变成由0和1组成的二进制数据流信号,进而被本台的网卡通过计算机网络(WiFi等)传输给另一台计算机的网卡接收,经另一台计算机的操作系统处理信息后,通过socket传输给应用程序B上展示。
以上就是一个完整的工作流程。
Socket(应用程序之间的通讯保障)
Socket(套接字)是网络通信中的一个抽象接口,它提供了进程间通信的端点。**Socket允许同一台计算机或不同计算机上的应用程序通过网络进行通信,实现了跨网络的进程间通信。**Socket不仅包含了IP地址和端口号等通信信息,还提供了数据传输和接收的接口。通过Socket,应用程序可以建立网络连接、发送和接收数据,实现网络通信的各种功能。
(每个Socket都对应一个来源,多个socket对应多个来源;Socket会一直保存来源线程,我们只需要通过Response直接返回到socket上即可!)
网卡(计算机之间的通讯保障)
网卡通常安装在计算机的主板插槽中,或者通过USB、PCIe等接口与计算机连接。它的主要功能是实现计算机与网络之间的数据交换,即将计算机内部的数据通过网络传输到外部,同时将外部网络的数据传输到计算机内部。
简而言之,网卡就是相互之间通过计算机网络 接收和发送0101这种信息的一种物理硬件。
端口
端口是网络通信中的逻辑接口,用于区分不同的服务和应用进程。每个端口都有一个唯一的标识符,即端口号 ,用于标识特定的服务或应用进程。当计算机通过网络发送数据时,数据会被封装成数据包,并标记上目标端口号,以便接收端能够正确识别和处理。端口的作用在于确保数据能够准确地被发送到目标服务或应用进程,实现网络通信的精确性和可靠性。
端口号
- 作用:端口号用于唯一标识一个网络服务或应用程序。当计算机接收到一个数据包时,它会检查数据包中的目标端口号,并将数据包发送到监听该端口的服务或应用程序。
- 范围:端口号的范围通常是0到65535。其中,0到1023是系统保留端口,通常被用于常见的网络服务,如HTTP(80端口)、HTTPS(443端口)、FTP(21端口)等。这些端口通常被系统或管理员保留,不建议普通用户随意使用。1024到65535是用户端口或动态端口,可以由用户根据需要自由分配。
如下,就是一个对端口号应用的例子:
通过端口号实现一台计算机上不同网络服务或应用程序的区分,以保证数据信息能够准确无误的传输。
实例
下面来实现客户端向服务端发送信息,以及服务端接收到并且输出的代码:
client端
java
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;
public class Client {
public static void main(String[] args) throws IOException {
//客户端只需要从本地IP地址向指定的端口去发送
Socket socket=new Socket("127.0.0.1",8080);
//打开输出流--准备传送数据
OutputStream outputStream=socket.getOutputStream();
//创建输出流类型的流数据类型参数
PrintStream printStream=new PrintStream(outputStream);
Scanner in=new Scanner(System.in);
String x=in.nextLine();//获取一行的数据
printStream.println(x);
printStream.close();
in.close();
}
}
解析
接下来开始解析上述的代码:
java
Socket socket=new Socket("127.0.0.1",8080);
"127.0.0.1"是本地IP,8080是一个端口号,这一条表示声明一个Socket对象,用于建立本地客户端与服务器之间的连接,通过8080端口。
java
OutputStream outputStream=socket.getOutputStream();
通过Socket对象的getOutputStream()方法获取输出流;这个输出流用于向服务器发送数据
java
PrintStream printStream=new PrintStream(outputStream);
创建一个PrintStream对象,将OutputStream作为参数传入(将将 OutputStream
包装成 PrintStream
)
为什么使用PrintStream?
PrintStream提供了丰富的打印方法,使得向输出流写入数据变得更加方便,(如
print()
,println()
,printf()
等),这些方法可以自动将参数转换为字符串并写入到输出流中,同时,PrintStream还提供自动刷新功能,每次写入后都会自动调用flush()
方法,确保数据被及时发送到输出流。
客户端的输出流一定要使用PrintStream吗?
如果数据是纯字节数据 (如图像、音频文件等),那么直接使用
OutputStream
的write()
方法就足够了,不需要将OutputStream
包装成PrintStream
。如果数据是文本数据 ,并且需要格式化输出(如添加换行符、格式化数字等),那么使用
PrintStream
会更加便捷。在客户端向服务器发送文本数据时 ,虽然可以直接使用
OutputStream
的write(byte[] b, int off, int len)
方法来发送字符串的字节表示(通过字符串的getBytes()
方法获取字节数组),但这样做需要手动处理字符串的编码和转换,而且不方便添加换行符等文本格式。因此,使用PrintStream
可以简化这些操作。
java
Scanner in=new Scanner(System.in);
String x=in.nextLine();//获取一行的数据
键盘输出一行作为传送信息
java
printStream.println(x);
printStream.close();
in.close();
使用PrintStream的println()方法将用户输入的字符串发送到服务器;其中println()方法会在字符串末尾自动添加换行符,并将其写入到输出流中。
完成这一切后,关闭scanner和printStream。
server端
java
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args) throws IOException {
run();
}
public static void run() throws IOException {
ServerSocket serverSocket=new ServerSocket(8080); //0~65535监听接口
while(true){//一直等待客户端连接(无限循环)
//无限循环就意味着需要做出解除方法
Socket socket= serverSocket.accept();//接收到client信息后,将获取的请求初始化给Socket(阻塞监听)
//输出客户端给我们发来的程序
InputStream inputStream=socket.getInputStream(); //打开输入流-接收输入的信息--并赋值给inputStream
int count=0;
while(count==0){
count= inputStream.available(); //获取可读出的字节数
}
byte[] bytes=new byte[count]; //开辟适合大小的byte数组
inputStream.read(bytes);// 从输入流中读取数据到byte数组
String context=new String(bytes);//将输入流转化为字符串输出
System.out.println(context);
}
}
}
解析
接下来开始解析上述的代码:
java
ServerSocket serverSocket = new ServerSocket(8080);
创建一个监听端口8080的ServerSocket
实例。端口号8080是任选的,但必须是0到65535之间的有效端口。
while (true)
服务器持续运行,等待客户端连接。
java
Socket socket = serverSocket.accept();
accept
方法会阻塞(暂停执行),直到一个客户端连接到服务器。一旦有连接,它就返回一个Socket
实例,用于与该客户端通信。
java
//输出客户端给我们发来的程序
InputStream inputStream=socket.getInputStream(); //打开输入流-接收输入的信息--并赋值给inputStream
int count=0;
while(count==0){
count= inputStream.available(); //获取可读出的字节数
}
byte[] bytes=new byte[count]; //开辟适合大小的byte数组
inputStream.read(bytes);// 从输入流中读取数据到byte数组
String context=new String(bytes);//将输入流转化为字符串输出
System.out.println(context);
InputStream inputStream = socket.getInputStream();
:从客户端连接获取输入流,用于读取客户端发送的数据(创建输入流)。int count = 0; while (count == 0) { count = inputStream.available(); }
:这段代码读取输入流中可用的字节数。byte[] bytes = new byte[count];
:根据available()
返回的字节数创建一个字节数组。inputStream.read(bytes);
:从输入流中读取数据到字节数组。String context = new String(bytes);
:将字节数组转换为字符串。方便下一步输出。System.out.println(context);
:在控制台上打印从客户端接收到的字符串。
相关方法
通过上述的讲解和应用,罗列了以下问题:
问题1:ServerSocket和Socket有什么关系?
ServerSocket
ServerSocket
类位于java.net
包中,它用于创建一个服务器端套接字,该套接字可以监听来自客户端的连接请求。服务器通过绑定到一个特定的端口来等待客户端的连接。一旦有客户端尝试连接到该端口,ServerSocket
就会接受这个连接,并返回一个新的Socket
实例,该实例代表了服务器与客户端之间的通信链路。
ServerSocket
的主要作用包括:
- 监听端口:服务器绑定到一个特定的端口,等待客户端的连接请求。
- 接受连接 :通过
accept()
方法阻塞等待,直到一个客户端连接请求到达。- 返回通信链路 :一旦连接建立,
accept()
方法会返回一个Socket
对象,用于后续的通信。Socket
Socket
类同样位于java.net
包中,它代表了一个客户端套接字,或者是一个服务器与客户端之间已经建立的通信链路(在服务器端,这个Socket
对象是由ServerSocket
的accept()
方法返回的)。Socket
提供了输入流和输出流,用于在客户端和服务器之间发送和接收数据。
Socket
的主要作用包括:
- 建立连接 :客户端通过
Socket
类的构造函数或connect()
方法尝试连接到服务器。- 获取输入/输出流 :通过
getInputStream()
和getOutputStream()
方法获取用于读取和写入数据的流。- 通信:使用输入/输出流与服务器进行通信,发送和接收数据。
- 关闭连接 :通过
close()
方法关闭与服务器的连接。关系和作用总结
ServerSocket
用于服务器端,负责监听端口和接受客户端的连接请求。Socket
用于客户端(或者服务器端与客户端之间的通信链路),负责建立连接、发送和接收数据。- 服务器端通过
ServerSocket
接受连接后,会返回一个Socket
对象,该对象用于与客户端进行后续的通信。- 客户端通过
Socket
连接到服务器后,同样获得一个Socket
对象(这个对象在客户端创建时就已经获得),用于与服务器进行通信。问题2:ServerSocket和Socket的区别?
一、构造方法与参数
Socket
- 常见的构造方法有:
Socket(String host, int port)
:创建一个Socket并连接到指定主机上的指定端口。Socket(InetAddress address, int port)
:创建一个Socket并连接到指定IP地址和端口。ServerSocket
- 常见的构造方法有:
ServerSocket(int port)
:创建一个绑定到指定端口的ServerSocket实例。ServerSocket(int port, int backlog)
:创建一个绑定到指定端口的ServerSocket实例,并设置客户端连接请求队列的长度。ServerSocket(int port, int backlog, InetAddress bindAddr)
:创建一个绑定到指定端口和IP地址的ServerSocket实例,并设置客户端连接请求队列的长度。二、核心方法
Socket
connect(SocketAddress endpoint)
:连接到服务器。getInputStream()
:获取输入流,用于从服务器接收数据。getOutputStream()
:获取输出流,用于向服务器发送数据。ServerSocket
accept()
:监听并接受客户端的连接。此方法会阻塞,直到有客户端连接为止。一旦连接建立,它会返回一个新的Socket对象,表示与客户端的连接。getLocalPort()
:获取服务器套接字绑定的本地端口。isClosed()
:检查ServerSocket是否已关闭。三、通信过程
Socket
- 客户端创建一个Socket对象,指定服务器地址和端口。
- 客户端通过Socket对象的输出流向服务器发送数据。
- 客户端通过Socket对象的输入流接收服务器的响应。
ServerSocket
- 服务器创建一个ServerSocket对象,监听指定的端口。
- 服务器调用ServerSocket对象的accept()方法,等待客户端的连接请求。
- 一旦有客户端连接到服务器,ServerSocket会创建一个Socket对象与客户端进行通信。
- 服务器通过Socket对象的输入流接收客户端发送的数据。
- 服务器通过Socket对象的输出流发送响应给客户端。