目录
一、网络通信协议
1、概述
计算机网络是通过传输介质、通信设施和网络通信协议,把分散在不同地点的计算机设备互连起来的,实现资源共享和数据传输的系统。网络编程就是编写程序使互联网的两个(或多个)设备(如计算机)之间进行数据传输。Java语言对网络编程提供了良好的支持。通过其提供的接口我们可以很方便地进行网络编程。
网络通信协议是网络编程中的关键,通信双方达成的一种共识,当通信双方都遵守这样的约定,才能正确传输信息。
2、为什么协议要分层?
1、分层可以避免一个协议太过于庞大和复杂。
2、分层之后协议之间解耦合,上层协议不需要理解下层协议的细节
3、任意层次的协议的可以进行灵活的替换
3、对于参考模型的介绍
在这里我们对于TCP/IP模型进行详细的描述:
应用层:和应用程序直接打交道的协议。
传输层:负责端到端之间的传输(关注起点和终点)
网络层:负责点到点之间的传输(关注传输的路径)
数据链路层:负责相邻点之间如何具体传输。
物理层:网络通信的基础硬件措施。
面试常见:
1、对于一台主机来说,它的操作系统内核实现了从应用层到物理层
2、对于一台路由器来说,它实现了从网络层到物理层。
3、对于一台交换机来说,它实现了从数据链路层到物理层。
4、对于集线器来说,它实现的物理层。
4、TCP网络传输的基本流程
在这些基本操作中涉及到封装和分用两个操作流程。
封装的意思就是给基于数据的基础上,在数据前加上协议报头,当数据由应用层传输到传输层时,传输层会在得到的数据基础上加上传输层协议报头。 当数据由传输层到达网络层时,会继续添加网络层协议报头,也就是我们常说的IP地址。当数据由网络层传输到数据链路层时会在数据前加上数据链路层协议报头,也就是常数的MAC地址。数据链路层将数据发送给物理层时,物理层就会将这个数据转化成光电信号,通过一些硬件设备,例如网线,光纤,电磁波等传输出去。
分用的过程正好与封装相反,当物理层接收到对方发送过来的光电信号时,会将它解析成二进制的bit流,进一步得到数据链路层数据帧。 数据链路层解析数据帧,玻璃针头和针尾取出,其中的IP数据报交给网络层。网络层接收到刚才的网络层数据报,通过解析去掉网络层的协议报头。把数据交给传输层,传输层拿到传输层,数据报在进行解析,去掉传输层报头,最后将数据交给应用层,这时应用层就可以解析数据,分析出发送者是谁,接收者是谁显示到桌面上。
当发送者通过自己的主机将数据封装完成之后,会通过路由器广域网等发送到服务器。当服务器通过分用把数据拆开之后看到接收方的地址,然后服务器将数据重新封装之后,通过路由器广域网等途径发送到接收者的主机,然后接收者的主机一步一步的进行分佣,最终得到数据并且显示出来。
二、网络编程套接字(socket)
套接字(socket)是一组API,用来实现网络编程,一般是通过客户端(server)和服务器(cilent)实现的。
IP地址:用来识别互联网上一台主机的位置。
端口号:用来区分是主机上的那个应用。
在一次通信过程中涉及到五元组:源IP,目的IP,源端口,目的端口,协议类型。
JAVA中提供了两种风格:
UDP (DatagramSocket面向数据报,发送和接受数据要以数据包为单位)
TCP (ServerSocket面向字节流)
1、UDP(面向数据报)
1)UDP的服务器
//UDP的服务器
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
public class Test01 {
//进行实例化操作
private DatagramSocket socket=null;
//端口号
public Test01(int port) throws SocketException {
socket=new DatagramSocket(port);
}
//开启服务器
public void start() throws IOException {
System.out.println("开启服务器");
while (true){
//1、读取请求并分析
DatagramPacket requstPacket=new DatagramPacket(new byte[4096],4096);
socket.receive(requstPacket);
String reques=new String(requstPacket.getData(),0,
requstPacket.getLength()).trim();
//2、请求数据相应
String response=process(reques);
//3、把响应返回给服务器
DatagramPacket responsePacket=new DatagramPacket(response.getBytes()
,response.getBytes().length,requstPacket.getSocketAddress());
socket.send(responsePacket);
//日志
System.out.printf("[%s:%d] req: %s; resp:%s \n",requstPacket.getAddress().toString(),
requstPacket.getPort(),reques,response);
}
}
private String process(String reques) {
//我们写最简单的服务器回溯
return reques;
}
public static void main(String[] args) throws IOException {
Test01 test01=new Test01(9090);
test01.start();
}
}
2)UDP的客户端
import java.io.IOException;
import java.net.*;
import java.util.Scanner;
public class Test02 {
// 客户端的主要流程分成四步.
// 1. 从用户这里读取输入的数据.
// 2. 构造请求发送给服务器
// 3. 从服务器读取响应
// 4. 把响应写回给客户端.
private DatagramSocket socket = null;
private String serverIp;
private int serverPort;
// 需要在启动客户端的时候来指定需要连接哪个服务器
public Test02(String serverIp, int serverPort) throws SocketException {
this.serverIp = serverIp;
this.serverPort = serverPort;
socket = new DatagramSocket();
}
public void start() throws IOException {
Scanner scanner = new Scanner(System.in);
while (true) {
// 1. 读取用户输入的数据
System.out.print("-> ");
String request = scanner.nextLine();
if (request.equals("exit")) {
break;
}
// 2. 构造请求发送给服务器
DatagramPacket requestPacket = new DatagramPacket(request.getBytes(),
request.getBytes().length, InetAddress.getByName(serverIp), serverPort);
socket.send(requestPacket);
// 3. 从服务器读取响应
DatagramPacket responsePacket = new DatagramPacket(new byte[4096], 4096);
socket.receive(responsePacket);
String response = new String(responsePacket.getData(), 0, responsePacket.getLength()).trim();
// 4. 显示响应数据
System.out.println(response);
}
}
public static void main(String[] args) throws IOException {
Test02 client = new Test02("127.0.0.1", 9090);
// UdpEchoClient client = new UdpEchoClient("47.98.116.42", 9090);
client.start();
}
}
2、TCP(面向字节流)
客户端发送的数据是按行读取(\n)
1)服务器
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Test05 {
// 1. 初始化服务器
// 2. 进入主循环
// 1) 先去从内核中获取到一个 TCP 的连接
// 2) 处理这个 TCP 的连接
// a) 读取请求并解析
// b) 根据请求计算响应
// c) 把响应写回给客户端
private ServerSocket serverSocket=null;
public Test05(int port) throws IOException {
//绑定端口号
serverSocket=new ServerSocket(port);
}
public void start() throws IOException {
System.out.println("服务器启动");
//创建一个线程池
ExecutorService executorService= Executors.newCachedThreadPool();
while(true){
//先从内核中获取到TCP连接
Socket clientSocket=serverSocket.accept();
//处理这个连接
//我们可以在这个地方加上一个线程池
executorService.execute(new Runnable() {
@Override
public void run() {
processConnection(clientSocket);
}
});
}
}
private void processConnection(Socket clientSocket) {
//获取地址和端口
System.out.printf("[%s:%d] 客户端上线 \n",clientSocket.getInetAddress().toString(),clientSocket.getPort());
try(BufferedReader buffer
文章评论