一、基本概念
1、客户端VS服务器
- JVM是java machine environment的简称,即java虚拟机
- Tomcat是服务器最常用的软件,绑定一个端口即可使用,通过启动服务器软件来执行客户端提交的代码
2、访问服务器的项目
访问http://ip地址:端口/项目地址,例如:
访问http:192.168.12.32:8080/qq/login?user=root&passwd=123
ip地址为127.0.0.1或者localhost即本地ip地址
3、网络参考模型
- OSI参考模型(七层)
- TCP/IP模型(四层)
网络接口层:物理层、数据链路层
4、搭建一个简易的web服务器
4.1创建一个java工程
在intellJ idea创建一个java工程,这里创建了一个01test
工程示例:
4.2添加web框架支持
我这里为了展示如何添加,新建了一个工程02test:
点击确定即可。
4.3给web配置tomcat服务器
这里给web配置的是最常用的tomcat服务器,tomcat服务器是开源的轻量级Web应用服务器,操作如下:
选择第一项工件
,然后选择当前的项目,再滑到下面,修改应用程序上下文,此处为修改url的访问地址,修改后可以此名字来访问当前项目,这么做是为了以后访问的方便,不改亦可以。例如修改为/02test
(一定不要漏了/),访问url变为http://127.0.0.1:8080/02test,否则默认为http://127.0.0.1:8080/02Test_war_exploded。然后,点击确定。
接着,点击运行或者调试tomcat服务器,开始部署服务器:
服务器输出框显示部署成功,随之浏览器弹出新窗口,显示 E N D END END,即完成部署工作。
4.4添加tomcat库
因为web要依赖服务器库,这里要添加tomcat库:
点击,选择项目结构
添加库,选择电脑上安装的tomcat库,点击添加,最后确定,会发现外部库中多了tomcat库:
4.5添加html文件
新建html文件夹,这里做个简单的登录页面,添加的html源代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录窗口</title>
</head>
<body>
<!--创建登录界面-->
<form action="/02test" method="post">
<div>用户名<input name="username"></div>
<div>密码 <input name="password"></div>
<button type="submit">登录</button>
</form>
</body>
</html>
这里可以尝试在浏览器访问http://localhost:8080/02test/html/login.html,得到登录页面:
4.6添加java文件
为了处理客户端传送过来的数据,在这里简单写个服务器处理脚本。由于读者资源有限,本电脑兼任客户端、服务器两者。在src文件夹下添加java类,类名命名为com.syy.servlet.LoginServlet
,源代码如下:
package com.syy.servlet;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 处理登录请求
*/
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1、获取客户端发送的数据(请求参数)
String username = req.getParameter("username");
String password = req.getParameter("password");
System.out.println(username + " " + password);
//2、判断
if ("syy".equals(username) && "123".equals(password)){
//登录成功
resp.getWriter().write("Login successful!");
}
else {
resp.getWriter().write("Login failure!");
}
}
}
4.7修改html文件
因为想要给服务器返回客户端的提交信息,故要在原来的login.html作修改:
- 查找web服务器的服务器接口名字,这里是
/login
- 在login.html文件中加入web服务器的服务器接口名字
4.8验证
点击调试,选择重新部署,在浏览器的登录页面输入用户名、密码(这里我输入的是syy、123),可看到登录成功的界面,并在服务器输出框返回了用户名、密码:
二、集线器-网桥-交换机
1、设备之间的连线
- 同种设备之间用交叉线,不同种设备用直通线
2、数据发送的顺序
- 先发ARP数据包进行广播
- 再发ICMP数据包进行控制,也可以说是差错通知与信息查询
3、网桥
- 在同一个网段里传输数据,工作在数据链路层,无mac地址
- 只有两个端口
- 可以通过自学习得知两个端口侧所有设备的MAC地址,即建立了一张MAC地址表,从而可以隔绝冲突域
4、交换机
- 相当于接口更多的网桥,二级交换机工作在数据链路层,三层交换机工作在网络层,有ip地址和mac地址
- 是局域网的最终方案,即在同个网段工作,所以不能跨网段(除了三层交换机)
三、MAC地址_IP地址__子网掩码
1、MAC地址操作
2、查看所有网络连接
PS C:\Users\Y> ipconfig /all
3、arp相关命令
4、IP地址的分类
- A类:0开头,网络ID即0-127,但是可以分配给主机的只能是1-126,因为0不能用,127作为保留网段
- B类:10开头,即128-191
- C类:110开头,即192-223,平常我们用的192.168.1.10属于C类
- D类:1110开头,多播地址
- E类:1111开头,保留为今后使用
- 127.0.0.1是本地环回地址,代表本地地址
5、网段与网关
- 网段是主机位为0,比如192.168.1.0,一般称为网络号
- 网关一般是主机位为1,比如192.168.1.1,一般设为路由器的端口ip地址
6、不同网段之间的Ping
发送者会用自己的子网掩码来计算对方的网段,跟自己的网段比较,若在同一网段则可ping通,否则超时。
四、超网_静态路由
1、超网概念
超网跟子网反过来,它是将多个连续的网段 合并成一个更大的网段。
其实就是将子网掩码往左移,增加主机数
2、默认路由
默认路由是一种特殊的静态路由,指的是当 路由表中与包的目的地址之间没有匹配的表项时,路由器能够做出选择。如果没有默认路由,那么目的地址在路由表中没有匹配表项的包将被丢弃。
3、默认路由与具体路由的使用
- 当有多种路由要传输到同一个端口时,则用默认路由
- 当有确定且为下一跳的路由,则用具体的路由
五、局域网_NAT
1、网络分类
- LAN(local area network):局域网
- MAN(metropolitan area network:域域网
- WAN(wide。。):广域网
2、私网网段
- A类:10.0.0.0/8,1个A类网络网段
- B类:172.16.0.0-172.31.0.0/16 ,16个B类网络网段
- C类:192.168.0.0-192.168.255.0/24,256个C类网络网段
3、NAT
- NAT转换出的公网ip是离服务器最近的路由ip
- 采用PAT端口转换机制
六、物理层_数据链路层
1、各层对应协议及传输数据格式
2、数据链路层
2.1三个基本问题
- 封装成帧
- 透明传输
- 差错检验
2.2封装成帧
注意点:在进行传输时,各个传输端点之间(例如PC与交换机、PC与路由器、路由器与路由器等),会将上个链路传输下来的PDU去掉头部尾部的协议信息,再加上自己的协议信息,例如以太网协议(集线器、交换机使用的协议)、PPP协议(路由器的点到点协议),再传输给下一个链路。
2.3差错检验
当传输协议改变时,即头部信息改变,FCS也会改变。
以太网帧传输的字节至少要64
字节,以太网帧现在使用最多的格式为Ethernet V2标准。
2.4Ethernet V2帧的格式
2.5Ethernet V2标准
2.6PPP协议
2.7网卡
wireshark抓到的帧是经过了校验差错的帧(FCS被网卡过滤掉了),故没有FCS。
七、网络层
1、ip包数据格式
2、ping的几个用法
八、传输层
1、TCP、UDP比较
2、UDP
2.1数据格式
2.2端口
3、TCP
3.1标志位(flags)
3.2建立连接–3次握手
seq为序号,ack为希望对方发送的序列号。
3.2.1前两次握手的特点
3.2.2为什么要进行三次握手
3.3释放连接–四次挥手
3.3.1状态解读
注意:由于FIN-WAIT、LAST-ACK状态非常短暂,在cmd用netstat -n
查看所有端口状态时,只显示ESTABLISHED、TIME-WAIT、CLOSE-WAIT、CLOSED这四个状态。通常,可以用TIME-WAIT表示客户端,CLOSE-WAIT表示服务器。
3.3.2释放细节
3.3.3为什么要进行四次挥手
3.3.4抓包
在这里简单写了一个TCP服务器与客户端通信的程序:
客户端代码:
package com.mj;
import java.awt.FlowLayout;
import java.awt.Font;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;
@SuppressWarnings("serial")
public class TCPClient2 extends JFrame {
public TCPClient2() {
setTitle("客户端2");
setBounds(300, 300, 600, 400);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLayout(new FlowLayout(FlowLayout.LEFT, 20, 20));
// 字体
Font font = new Font("微软雅黑", Font.PLAIN, 18);
JLabel label = new JLabel();
label.setFont(font);
add(label);
JTextField tf = new JTextField(10);
tf.setFont(font);
add(tf);
JButton saveBtn = new JButton("发送");
saveBtn.setFont(font);
saveBtn.addActionListener((evt) -> {
try (
Socket socket = new Socket("127.0.0.1", Constants.PORT);
OutputStream os = socket.getOutputStream();
InputStream is = socket.getInputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
) {
// 写数据到服务器
os.write(tf.getText().getBytes("UTF-8"));
// 关闭输出(表明写给服务器的内容都写完了)
socket.shutdownOutput();
// 读取服务器发送的数据
byte[] buffer = new byte[8192];
int len;
while ((len = is.read(buffer)) != -1) {
baos.write(buffer, 0, len);
}
byte[] bytes = baos.toByteArray();
label.setText(new String(bytes, "UTF-8"));
} catch (Exception e) {
e.printStackTrace();
}
});
add(saveBtn);
}
public static void main(String[] args) throws Exception, IOException {
new TCPClient2().setVisible(true);
}
}
服务器代码:
package com.mj;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class TCPServer2 {
public static void main(String[] args) throws Exception {
try (ServerSocket server = new ServerSocket(Constants.PORT)) {
System.out.println("服务器2----开始监听");
while (true) {
Socket client = server.accept();
new Thread(() -> {
try {
doClient(client);
client.close();
} catch (Exception e) {
e.printStackTrace();
}
}).start();
}
}
}
static void doClient(Socket client) throws Exception {
try (
ByteArrayOutputStream baos = new ByteArrayOutputStream();
InputStream is = client.getInputStream();
OutputStream os = client.getOutputStream();
) {
// 读取客户端的数据
byte[] buffer = new byte[8192];
int len;
while ((len = is.read(buffer)) != -1) {
baos.write(buffer, 0, len);
}
byte[] bytes = baos.toByteArray();
String content = new String(bytes, "UTF-8");
String ip = client.getInetAddress().getHostAddress();
System.out.format("服务器2接收到[%s]的数据:%s%n", ip, content);
// 写数据给客户端
os.write(doString(content).getBytes("UTF-8"));
}
}
static String doString(String str) {
str = str.replace("你", "朕");
str = str.replace("吗", "");
str = str.replace("么", "");
str = str.replace("?", "!");
return str.replace("?", "!");
}
static void test() {
// ServerSocket serverSocket = new ServerSocket(8888);
// Socket socket = serverSocket.accept();
//
// ByteArrayOutputStream baos = new ByteArrayOutputStream();
// InputStream is = socket.getInputStream();
// byte[] buffer = new byte[8192];
// int len;
// while ((len = is.read(buffer)) != -1) {
// baos.write(buffer, 0, len);
// }
// byte[] bytes = baos.toByteArray();
//
// String string = new String(bytes, "UTF-8");
//
// System.out.format("服务器接收到[%s]的数据:%s%n", socket.getInetAddress(), string);
//
// is.close();
// baos.close();
// socket.close();
// serverSocket.close();
}
}
端口设置代码:
package com.mj;
public class Constants {
public static final int PORT = 8888;
}
用wireshark抓了一下四次挥手的包:
2104 373.700684 127.0.0.1 127.0.0.1 TCP 44 1184 → 8888 [FIN, ACK] Seq=4 Ack=1 Win=2619648 Len=0
2105 373.700695 127.0.0.1 127.0.0.1 TCP 44 8888 → 1184 [ACK] Seq=1 Ack=5 Win=2619648 Len=0
2106 373.709662 127.0.0.1 127.0.0.1 TCP 47 8888 → 1184 [PSH, ACK] Seq=1 Ack=5 Win=2619648 Len=3
2107 373.709690 127.0.0.1 127.0.0.1 TCP 44 1184 → 8888 [ACK] Seq=5 Ack=4 Win=2619648 Len=0
2108 373.710083 127.0.0.1 127.0.0.1 TCP 44 8888 → 1184 [FIN, ACK] Seq=4 Ack=5 Win=2619648 Len=0
2109 373.710122 127.0.0.1 127.0.0.1 TCP 44 1184 → 8888 [ACK] Seq=5 Ack=5 Win=2619648 Len=0
第1、2、5、6行是四次挥手的包,第3、4行是服务器给客户端发送的数据包。
当server收到client的FIN时,如果server没有数据要发给client,则server会将第2、3次挥手合并为一次挥手。
7 1.357038 172.26.236.210 180.163.151.162 TCP 54 29060 → 443 [FIN, ACK] Seq=1 Ack=74 Win=510 Len=0
8 1.402669 180.163.151.162 172.26.236.210 TCP 60 443 → 29060 [FIN, ACK] Seq=74 Ack=2 Win=322 Len=0
9 1.402743 172.26.236.210 180.163.151.162 TCP 54 29060 → 443 [ACK] Seq=2 Ack=75 Win=510 Len=0
4、长链接与短链接
长链接:服务器与客户端一旦连接之后,中间需要保持连接来持续发送数据。长链接一般用于TCP保活,多用于操作频繁,点对点的通讯,而且连接数不能太多情况。
短链接:跟长链接相反,服务器与客户端连接之后,一旦完成一次读写传递操作,则断开连接。短链接一般用于web网站的http服务器,因为web网站会有成千上万的客户端进行连接,用短链接可以节省资源。当客户端数量庞大且无需频繁操作时,用短链接合适。
包。
当server收到client的FIN时,如果server没有数据要发给client,则server会将第2、3次挥手合并为一次挥手。
7 1.357038 172.26.236.210 180.163.151.162 TCP 54 29060 → 443 [FIN, ACK] Seq=1 Ack=74 Win=510 Len=0
8 1.402669 180.163.151.162 172.26.236.210 TCP 60 443 → 29060 [FIN, ACK] Seq=74 Ack=2 Win=322 Len=0
9 1.402743 172.26.236.210 180.163.151.162 TCP 54 29060 → 443 [ACK] Seq=2 Ack=75 Win=510 Len=0
4、长链接与短链接
长链接:服务器与客户端一旦连接之后,中间需要保持连接来持续发送数据。长链接一般用于TCP保活,多用于操作频繁,点对点的通讯,而且连接数不能太多情况。
短链接:跟长链接相反,服务器与客户端连接之后,一旦完成一次读写传递操作,则断开连接。短链接一般用于web网站的http服务器,因为web网站会有成千上万的客户端进行连接,用短链接可以节省资源。当客户端数量庞大且无需频繁操作时,用短链接合适。