目录
- [1 协议](#1 协议)
-
- [1.1 网络协议分类](#1.1 网络协议分类)
-
- [1.1.1 OSI 七层网络模型](#1.1.1 OSI 七层网络模型)
- [1.1.2 TCP/IP协议(四层或五层)](#1.1.2 TCP/IP协议(四层或五层))
- [2 封装/分用](#2 封装/分用)
- [3 IP](#3 IP)
- [4 Mac](#4 Mac)
- [5 网络编程(TCP/UDP开发)](#5 网络编程(TCP/UDP开发))
- [6 总结](#6 总结)
1 协议
"协议" 就是一个规范/约定。
1.1 网络协议分类
网络协议有两大分类方法:
- OSI 七层模型(这每层模型是啥,代表的什么含义必须背过)
- TCP/IP协议(四层),它也是http协议的一个基础,也就是http协议是依靠TCP/IP协议实现的。http是应用层协议。
1.1.1 OSI 七层网络模型
- 应用层
应用层存放的就是程序,我们编写的程序就放在应用层里面。
功能:针对特定应用的协议。
比如:电子邮件通过电子邮件协议、远程登录通过远程登录协议、文件传输通过文件传输协议进行链接等。
应用层里面著名的协议有HTTP、SSH、FTP。 - 表示层
将网络信息包转换成程序需要的数据结构。
功能:设备固有数据格式和网络标准数据格式的转换。 - 会话层
管理断开和连接会话的。
功能:通信管理。负责建立和断开通信连接,管理传输层以下的分层。 - 传输层
填写收件人信息和发件人信息的,也就是管理目标地址和原地址的ip和端口号的。
功能:管理两个节点之间的数据传输,负责可靠传输(确保数据被可靠地传送到目标地址)。
网络交互必备的5个部分:目标地址IP、目标地址的端口、源地址IP、源端口号和协议。
传输层里面著名的协议有TCP、UDP,两者的区别是:
(1) UDP:无连接、不可靠、面向字节报的。
(2) TCP:需要连接、可靠、面向字节流的。 - 网络层
选择快递传输的方式(用车还是用飞机)
功能:地址管理和路由选择。
网络层里面著名的协议有IP。 - 数据链路层
确定具体的传输路径。
功能:互联设备之间传送和识别数据帧。 - 物理层
用来发光信号和电信号的。
功能:以0、1代表电压的高低、灯光的闪灭。界定连接器和网线的规格。
1.1.2 TCP/IP协议(四层或五层)
- 应用层
- 传输层
- 网络层
- 数据链路层
- 物理层(可忽略,忽略掉就为4层了)
2 封装/分用
封装就是数据一层一层往下传递的过程。
分用就是封装相反的过程。
3 IP
IP是指互联网地址标识,我们所说的IP通常是指IPv4和IPv6,但由于IPv6还没有普及,所以泛指的还是IPv4。
IPv4是由32位组成,分为四部分,十进制" . " 分发法,数据范围是0-255。
4 Mac
Mac地址是物理地址,也叫网卡地址。
规则:它在机器出厂时就已经制定好了,那么原则上来说它是全球唯一的。也就是出厂之后会随机分配一个全球唯一的地址。
如果是Linux系统,那么格式是 xx:xx:xx...;如果是Windows系统,那么格式是xx-xx-xx-xx...。
5 网络编程(TCP/UDP开发)
传输层协议:TCP、UDP。
- 下面将使用UDP进行编程示例,实现一个客户端和服务器端的UDP交互。
需要创建两个类,一个是客户端,一个是服务器端。
客户端类:
java
package UDP;
import java.io.IOException;
import java.net.*;
import java.util.Scanner;
/*
* UDP客户端
* */
public class UDPClient {
//服务器端IP
private static final String ip = "127.0.0.1";
//服务器端端口号
private static final int port = 9001;
//数据包大小
private static final int bleng = 1024;
public static void main(String[] args) throws IOException {
//1.创建客户端
DatagramSocket socket = new DatagramSocket();
//让用户来输入发送的消息
Scanner scanner = new Scanner(System.in);
while(true){
System.out.println("->");
//接收到用户输入的信息
String msg = scanner.nextLine();
//2.构建发送数据包
DatagramPacket datagramPacket = new DatagramPacket(
msg.getBytes(),
msg.getBytes().length,
InetAddress.getByName(ip),
port
);
//3.发送消息
socket.send(datagramPacket);
DatagramPacket serPacket = new DatagramPacket(new byte[bleng],bleng);
//接收服务器端的消息
socket.receive(serPacket);
System.out.println("收到服务器端的信息"+new String(serPacket.getData()));
}
}
}
服务器端类:
java
package UDP;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
/*
* UDP服务器
* */
public class UDPServer {
//端口号
private static final int port = 9001;
//接收数据容器的大小
private static final int bleng = 1024;
public static void main(String[] args) throws IOException {
//1.创建一个udp服务器
DatagramSocket socket = new DatagramSocket(port);
while(true){
DatagramPacket clientPacket = new DatagramPacket(new byte[bleng],bleng);
//2.等待客户端连接
socket.receive(clientPacket);
//执行此代码表示已经有客户端连接了
//3.拿到客户端的请求信息
String msg = new String(clientPacket.getData());
System.out.println("接收到客户端的消息:"+msg);
//4.补充功能:给客户端反馈一个消息
String serMsg = "我收到了";
//String serMsg = msg.replace("吗?",".").repalce("你","我");
//构建客户端发送数据包
DatagramPacket serPacket = new DatagramPacket(
serMsg.getBytes(),
serMsg.getBytes().length, //注意是字节数组的长度
clientPacket.getAddress(),
clientPacket.getPort()
);
//给客户端发送消息
socket.send(serPacket);
}
}
}
一般先启动服务器端,再启动客户端。
如何把普通的项目部署到服务器上,然后通过本地这个客户端来连接服务器上的应用呢?
- 下面将使用TCP进行编程示例,实现一个客户端和服务器端的TCP交互。
也需要创建两个类,一个是客户端,一个是服务器端。
客户端类:
java
package TCP;
import java.io.*;
import java.net.Socket;
import java.util.Scanner;
/*
* TCP客户端
* */
public class TCPClient {
//服务器端IP
//服务器端IP
private static final String ip = "127.0.0.1";
//服务器端端口号
private static final int port = 9002;
public static void main(String[] args) throws IOException {
//1.创建tcp客户端,并且链接服务器端
Socket socket = new Socket(ip,port);
try(BufferedReader reader = new BufferedReader(
new InputStreamReader(socket.getInputStream()));
BufferedWriter writer = new BufferedWriter(
new OutputStreamWriter(socket.getOutputStream())
);Scanner scanner = new Scanner(System.in);
){
while(true){
System.out.println("->");
String msg = scanner.nextLine();
//2.发送消息给服务器端
writer.write(msg + "\n");
writer.flush();
//3.接收服务器响应的内容
String serMsg = reader.readLine();
System.out.println("服务器反馈:" + serMsg);
}
}
}
}
服务器端类:
java
package TCP;
import com.sun.scenario.effect.impl.sw.java.JSWBlend_SRC_OUTPeer;
import sun.java2d.pipe.BufferedTextPipe;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
/*
* TCP服务器
* */
public class TCPServer {
//端口号
private static final int port = 9002;
public static void main(String[] args) throws IOException {
//1.创建TCP服务器端
ServerSocket serverSocket = new ServerSocket();
System.out.println("服务器启动成功!");
//2.等待客户端得连接
Socket client = serverSocket.accept();
System.out.println(String.format("有客户端连接了,客户端IP:%s,端口:%d",client.getInetAddress().getHostAddress(),client.getPort()));
//try-catch
// BufferedReader bufferedReader = null;
// BufferedWriter bufferedWriter = null;
// try {
// //拿到读取对象
// bufferedReader = new BufferedReader(
// new InputStreamReader(client.getInputStream())
// );
// //接收服务器端的信息
// String msg = bufferedReader.readLine();
// //拿到写入对象
// bufferedWriter = new BufferedWriter(
// new OutputStreamWriter(client.getOutputStream())
// );
// String serMsg = "我收到了";
// bufferedWriter.write(serMsg + "\n"); //写入消息必须加 \n ,因为它在读取的时候是按行进行读取的,如果不加这个换行符,你是读取不到的,所以写入不成功。即因为客户端需要按行读取信息,所以在写入时必须加上换行符,作为最后的后缀信息。
// //刷新缓冲区
// bufferedWriter.flush();
// }finally {
// if(bufferedReader != null){
// bufferedReader.close();
// }
// if(bufferedWriter != null){
// bufferedWriter.close();
// }
// }
//try-resouce
try(BufferedWriter bufferedWriter = new BufferedWriter(
new OutputStreamWriter(client.getOutputStream())
);BufferedReader bufferedReader = new BufferedReader(
new InputStreamReader(client.getInputStream())
)
){
while(true){
//3.接收客户端的消息
String msg = bufferedReader.readLine();
if(msg != null && msg.equals("")){
System.out.println("收到客户端信息" + msg);
//4.返回给客户端响应信息
String serMsg = "我收到了";
bufferedWriter.write(serMsg + "\n");
//缓冲区刷新
bufferedWriter.flush();
}}
}
}
}
TCP为了加速发送和接收,它创建了一个缓冲区,也就是缓存区,严格意义上来讲是创建了两个缓冲区,为:发送缓冲区和接收缓冲区(提高性能的措施)。
- 使用TCP实现一个英译汉操作,也就是客户端输入为英文,服务器端解析为中文。
客户端类:
java
package TCP;
import java.io.*;
import java.net.Socket;
import java.util.Scanner;
/*
* 英译汉客户端
* */
public class CNTCPClient {
//服务器端IP
//服务器端IP
private static final String ip = "127.0.0.1";
//服务器端端口号
private static final int port = 9003;
public static void main(String[] args) throws IOException {
//1.创建tcp客户端,并且链接服务器端
Socket socket = new Socket(ip,port);
try(BufferedReader reader = new BufferedReader(
new InputStreamReader(socket.getInputStream()));
BufferedWriter writer = new BufferedWriter(
new OutputStreamWriter(socket.getOutputStream())
);Scanner scanner = new Scanner(System.in);
){
while(true){
System.out.println("英文:");
String msg = scanner.nextLine();
//2.发送消息给服务器端
writer.write(msg + "\n");
writer.flush();
//3.接收服务器响应的内容
String serMsg = reader.readLine();
System.out.println("中文:" + serMsg);
}
}
}
}
服务器端:
java
package TCP;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
/*
* 英译汉服务器
* */
public class CNTCPSrever {
//端口号
private static final int port = 9003;
//定义翻译字典
static HashMap<String,String> dirMap = new HashMap<>();
static {
dirMap.put("hello","你好");
dirMap.put("cat","猫");
dirMap.put("dog","狗");
}
public static void main(String[] args) throws IOException {
//1.创建TCP服务器端
ServerSocket serverSocket = new ServerSocket(port);
System.out.println("英译汉服务器启动成功!");
//2.等待客户端得连接
Socket client = serverSocket.accept();
//执行到此行说明已经有客户端连接
System.out.println(String.format("有客户端连接了,客户端IP:%s,端口:%d",client.getInetAddress().getHostAddress(),client.getPort()));
//try-resouce
try(BufferedWriter bufferedWriter = new BufferedWriter(
new OutputStreamWriter(client.getOutputStream())
); BufferedReader bufferedReader = new BufferedReader(
new InputStreamReader(client.getInputStream())
)
){
while(true){
//3.得到客户端的英文单词
String en = bufferedReader.readLine();
if(en != null && !en.equals("")){
//4.英译汉结果的处理方法
String cn = processData(en);
//5.将结果返回给客户端
bufferedWriter.write(cn + "\n");
//缓冲区刷新
bufferedWriter.flush(); }}
}
}
//英译汉方式1
private static String processData(String en){
String cn = "未知";
switch (en){
case "hello":
cn = "你好";
break;
case "cat":
cn = "猫";
break;
case "dog":
cn = "狗";
break;
//......
default:
break;
}
return cn;
}
//英译汉方式2
private static String processData2(String en){
//可以调用数据库查询英文的结果
String cn = "未知";
cn = dirMap.get(en);
return cn;
}
}
- 因为HTTP协议是基于TCP/IP实现的,所以我们学会了TCP开发也就能开发一个简单的http服务器了。我们只需要TCP遵循http协议的一个规范就可以开发出简易版Http服务器:根据不同的URL地址,返回不同的信息。
xxx/404 -> 没有找到页面
xxx/200 -> 你好,世界
java
package TCP;
/*
简易版HTTP
*/
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class TCPServerByHTTP {
//端口号
private static final int port = 9004;
public static void main(String[] args) throws IOException {
//1.创建TCP服务器端
ServerSocket serverSocket = new ServerSocket(port);
System.out.println("服务器启动成功!");
//2.等待客户端的连接
Socket client = serverSocket.accept();
//执行到此行说明已经有客户端连接
System.out.println(String.format("有客户端连接了,客户端IP:%s,端口:%d",client.getInetAddress().getHostAddress(),client.getPort()));
//3.得到两个读写对象
try(BufferedWriter bufferedWriter = new BufferedWriter(
new OutputStreamWriter(client.getOutputStream())
); BufferedReader bufferedReader = new BufferedReader(
new InputStreamReader(client.getInputStream())
)
){
//读取首行信息 【方法类型】【url】【http版本号】
String en = bufferedReader.readLine();
if(en != null && !en.equals("")){
String[] fLine = en.split("");
//读取到请求方法的类型
String method = fLine[0];
//读取到url
String url = fLine[1];
//读取http版本号
String httpVer = fLine[2];
System.out.println(String.format("读取到了客户端请求,方法类型:%s,URL:%s,版本:%s",method,url,httpVer));
//4.业务逻辑处理
String content = "<h1>未知</h1>";
if(url.contains("404")){
content = "<h1>没有找到此页面</h1>";
}else if(url.contains("200")){
content = "<h1>你好,世界!</h1>";
}
//5.将结果打印到浏览器上
//写入首行信息
bufferedWriter.write(httpVer + "200 ok\n ");
//写入Head(Content-Type/Content-Length)
bufferedWriter.write("Content-Type:text/html;charset=utf-8;\n");
bufferedWriter.write("Content-Length: "+ content.getBytes().length+"\n");
//写入空行
bufferedWriter.write("");
//写入body
bufferedWriter.write(content);
//缓冲区刷新
bufferedWriter.flush(); }}
}
}
6 总结
jar包和war包的区别:
- jar包是普通Java项目包的格式,它在所有jdk中都可以通过:java -jar jar名字来启动。
- war包是web程序的应用包,它必须放在web容器中才能启动,例如Tomcat、Nginx。