计算机网络原理

目录

前言

网络初识

认识协议

协议分层

封装和分用

网络编程套接字

socket套接字

TCP和UDP 

UDP

Tcp

网络原理

应用层

TCP基本特性

确认应答机制

超时重传机制 

连接管理机制(安全机制)

 滑动窗口(效率机制)

流量控制

拥塞控制

延迟应答

捎带应答

面向字节流

网络层

IP协议


前言

这篇博客介绍的是计算机网络的一些内容!!!

网络初识

认识协议

协议就是一种约定,发送方和接收方约定好,按照特定的格式来进行传输 

协议分层

上层协议调用下层协议,下层协议给上层协议提供服务,不能隔层调用

 TCP/IP

应用层:应用程序
传输层:端到端的传输

网络层:点到点的传输

数据链路层:相邻节点之间的传输
物理层:底层基础设施

封装和分用

 封装的过程中加入本层使用的协议以及关于协议的关键内容

 从下到上进行拆包,区分不同的协议进行分用处理。分用就是封装的逆过程。

网络编程套接字

socket套接字

由系统提供用于网络通信的技术,是基于TCP/IP协议的网络通信的基本操作单元

TCP和UDP 

TCPUDP
有连接无连接
可靠传输不可靠传输
面向字节流面向数据报
有接收缓冲区,也有发送缓冲区有接收缓冲区,无发送缓冲区
大小不限大小受限:一次最多传输64k
全双工全双工

全双工:一个通道,双向通信

半双工:一个通道,单向通信

UDP

DatagramSocket 构造方法

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

DatagramSocket 方法(代表一个UDP数据报,也就是一次发送/接收的基本单位)

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

 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()
获取数据报中的数据

 InetSocketAddress构造方法

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

 一个最简单的UDP版本的客户端服务器程序:回显服务器

服务器

public class UdpEchoServer {
    DatagramSocket socket=null;

    //参数的端口表示服务器要绑定的端口
    public UdpEchoServer(int port) throws SocketException {
        socket=new DatagramSocket(port);
    }

    //通过这个方法来启动服务器
    public void start() throws IOException {
        System.out.println("服务器启动!");
        while (true){
            //循环里面处理一次请求
            //1.读取请求并解析
            DatagramPacket requestPacket=new DatagramPacket(new byte[4096],4096);
            socket.receive(requestPacket);
            //把这个DatagramPacket对象转成字符串,方便打印
            String request=new String(requestPacket.getData(),0, requestPacket.getLength());
            //2.根据请求计算响应
            String response=process(request);
            //3.把响应写回到客户端
            DatagramPacket responsePacket=new DatagramPacket(response.getBytes(),response.getBytes().length,
                    requestPacket.getSocketAddress());
            socket.send(responsePacket);
            //4.打印一个日志,记录当前情况
            System.out.printf("[%s:%d] req:%s;resp:%s\n",requestPacket.getAddress().toString(),
                    requestPacket.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();
    }

}

客户端

public class UdpEchoClient {
    DatagramSocket socket=null;
    private String serverIp;
    private int serverPort;

    public UdpEchoClient(String serverIp,int serverPort) throws SocketException {
        socket=new DatagramSocket();
        this.serverIp=serverIp;
        this.serverPort=serverPort;
    }

    public void start() throws IOException {
        Scanner scanner=new Scanner(System.in);
        while (true){
            //1、从控制台读取用户输入的内容
            System.out.print("->");
            String request=scanner.next();
            //2、构造一个UDP请求,发送给服务器
            DatagramPacket requestPacket=new DatagramPacket(request.getBytes(),request.getBytes().length,
                    InetAddress.getByName(this.serverIp),this.serverPort);
            socket.send(requestPacket);
            //3、从服务器上读取UDP数据,并解析
            DatagramPacket responsePacket=new DatagramPacket(new byte[4096],4096);
            socket.receive(responsePacket);
            String response=new String(responsePacket.getData(),responsePacket.getData().length);
            //4、把服务器的响应显示到控制台上
            System.out.println(response);
        }
    }

    public static void main(String[] args) throws IOException {
        UdpEchoClient client=new UdpEchoClient("127.0.0.1",9090);
        client.start();
    }
}

DatagramPacket的构造方法

构造空的packet,不需要指定发送给谁
DatagramPacket requestPacket=new DatagramPacket(new byte[4096],4096);
构造有数据的packet,使用InetAddress描述发送给谁
DatagramPacket responsePacket=new DatagramPacket(response.getBytes(),response.getBytes().length,
        requestPacket.getSocketAddress());
构造有数据的packet,使用ip和端口来描述发送给谁
DatagramPacket requestPacket=new DatagramPacket(request.getBytes(),request.getBytes().length,
        InetAddress.getByName(this.serverIp),this.serverPort);

对于服务器来说,这三个步骤:读取请求并解析,根据请求计算响应,把响应写回给客户端,执行速度极快,这个时候如果有多个客户端发来请求,服务器也是可以响应的,但是在服务器这里本质上是三个请求串行处理的

Tcp

给服务器端使用的类

socketserver构造方法

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

socketserver方法

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

既给服务器端使用,也给客户端用 

socket构造方法

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

socket方法

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

服务器

public class TcpEchoServer {
    // 代码中会涉及到多个 socket 对象. 使用不同的名字来区分.
    private ServerSocket listenSocket = null;

    public TcpEchoServer(int port) throws IOException {
        listenSocket = new ServerSocket(port);
    }

    public void start() throws IOException {
        ExecutorService service=Executors.newCachedThreadPool();
        System.out.println("服务器启动!");
        while (true){
            //1、先用accept接收客户端的连接
            Socket clientSocket=listenSocket.accept();
            //2、处理这个连接,使用多线程,每个客户端连上来都分配一个新的线程负责处理
//            Thread t=new Thread(()->{
//                try {
//                    processConnection(clientSocket);
//                } catch (IOException e) {
//                    e.printStackTrace();
//                }
//            });
//            t.start();
            service.submit(new Runnable() {
                @Override
                public void run() {
                    try {
                        processConnection(clientSocket);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }

    private void processConnection(Socket clientSocket) throws IOException {
        System.out.printf("[%s:%d] 客户端上线!\n", clientSocket.getInetAddress().toString(), clientSocket.getPort());
        // 接下来就处理客户端的请求了.
        try (InputStream inputStream = clientSocket.getInputStream();
             OutputStream outputStream = clientSocket.getOutputStream()) {
            while (true) {
                // 1. 读取请求并解析.
                Scanner scanner = new Scanner(inputStream);
                if (!scanner.hasNext()) {
                    // 读完了, 连接可以断开了.
                    System.out.printf("[%s:%d] 客户端下线!\n", clientSocket.getInetAddress().toString(),
                            clientSocket.getPort());
                    break;
                }
                String request = scanner.next();
                // 2. 根据请求计算响应
                String response = process(request);
                // 3. 把响应写回给客户端
                PrintWriter printWriter = new PrintWriter(outputStream);
                printWriter.println(response);
                // 刷新缓冲区确保数据确实是通过网卡发送出去了.
                printWriter.flush();

                System.out.printf("[%s:%d] req: %s; resp: %s\n", clientSocket.getInetAddress().toString(),
                        clientSocket.getPort(), request, response);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 为啥这个地方要关闭 socket ? 而前面的 listenSocket 以及 udp 程序中的 socket 为啥就没 close??
            clientSocket.close();
        }
    }

    public String process(String request) {
        return request;
    }

    public static void main(String[] args) throws IOException {
        TcpEchoServer server = new TcpEchoServer(9090);
        server.start();
    }
}

客户端

public class TcpEchoClient {
    // 客户端需要使用这个 socket 对象来建立连接.
    private Socket socket = null;

    public TcpEchoClient(String serverIP, int serverPort) throws IOException {
        // 和服务器建立连接. 就需要知道服务器在哪了.
        // 这里和上节课写的 UDP 客户端差别较大了.
        socket = new Socket(serverIP, serverPort);
    }

    public void start() {
        Scanner scanner = new Scanner(System.in);
        try (InputStream inputStream = socket.getInputStream();
             OutputStream outputStream = socket.getOutputStream()) {
            while (true) {
                // 1. 从控制台读取数据, 构造成一个 请求
                System.out.print("-> ");
                String request = scanner.next();
                // 2. 发送请求给服务器
                PrintWriter printWriter = new PrintWriter(outputStream);
                printWriter.println(request);
                // 这个 flush 不要忘记. 否则可能导致请求没有真发出去.
                printWriter.flush();
                // 3. 从服务器读取响应
                Scanner respScanner = new Scanner(inputStream);
                String response = respScanner.next();
                // 4. 把响应显示到界面上
                System.out.println(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();
    }
}

网络原理

应用层

1、HTTP

后续会重点讲解

2、XML

格式是通过“标签”的形式来组织键值对数据的

标签名字就是key,标签里的内容就是value

缺点:数据多了编写复杂;这些数据要通过网络传输,消耗网络带宽

3、JSON

首先是一个{},{}里面包含了多组键值对,键值对之间使用,来分隔;键和值之间,使用:分隔;键只能是字符串类型;值可以是字符串,数字,数组,json

优点:可读性好;美观整洁;扩展性强

缺点:引入额外的字符串,传输数据量变大了,消耗更多的带宽

TCP基本特性

确认应答机制

TCP保证可靠性的最核心机制

 报文的序号是1,报文的长度是1000,TCP报头里只能存一个序号,最后一个字节的序号,是根据报文长度来算出来的

确诊应答报文中的确认序号是1001,表达的含义是,1001之前的数据

超时重传机制 

确认应答描述的是,数据报顺利到达对方,对方给了个响应,但是在传输过程中,如果丢包,就得进行超时重传

主机A在一个特定时间间隔内没有收到B发来的确认应答,就会进行重发 

主机A未收到B发来的确认应答,也可能是因为ACK丢失了 

连接管理机制(安全机制)

TCP建立连接(双方建立一个互相认同的关系)-三次握手

 TCP断开连接(双方取消相互认同的关系)

通信双方,各自向对方申请断开连接,在各自给对方回应

 滑动窗口(效率机制)

为了提高TCP传输效率的有效机制,每次批量的发送一波消息,然后再等一波ACK,再发一波消息

流量控制

在滑动窗口的基础之上,对发送速率做出限制的机制,,就是限制发送方的窗口大小不要太大

接收方使用接收缓冲区的剩余空间的大小,来作为发送方发送速率(窗口大小)的参考数值

拥塞控制

TCP引入慢启动机制,先发少量的数据,发送之初,网络情况前路未知,如果不丢包,就要放大拥塞窗口(拥塞控制下的那个窗口大小),开始的时候先指数增长(翻倍),达到阈值,就线性增长

延迟应答

提高传输效率的机制,又是基于流量控制,来引入的提高效率的机制

捎带应答

确认应答必须等到应用处理完数据并将作为回执的数据返回为止,才能进行捎带应答

面向字节流

面向字节流存在一个典型的问题-“粘包问题”

解决粘包问题,就是要在应用层协议进行区分,只要在定义应用层数据协议的时候,明确包和包之间的边界

1、xml;分隔符就相当于结束标签

2、json;分隔符就相当于}

3、protobuffer;里面通过声明长度的方式来确定边界

4、http;分隔符和长度两个都会用到

网络层

网络层所做的工作,就是在两点之间,规划出一个合理的路径,同时也需要对主机所处的位置,进行定义

1、地址管理;2、路由选择

IP地址的扩展:

1、动态分配IP地址

2、NAT机制

不在强制要求,每个主机都有独立的IP,把IP分成两大类:外网IP/公网IP;内网IP/私网IP/局域网IP

IP协议

网络号:标识网段,保证相互连接的两个网段(网络号)具有不同的标识

主机号:标识主机,同一网段内,主机之间具有相同的网络号,但是必须要有不同的主机号

同一局域网中,主机之间的网络号是相同的,主机号必须不同;在相邻的两个局域网中,要求网络号是不同的(同一路由器连接的)

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值