JavaWeb 基础知识 -- 网络编程(基础知识+回显服务器应用)

JavaWeb 基础知识 – 网络编程


本文内容大纲
请添加图片描述

1.为什么要网络编程?


通过代码来控制,让两个主机的进程之间能够进行数据交互

  我们所说的网络编程,就是通过网络来进行通信,那么具体来说就是网络连接的不同主机,在具体来说连接的是两个主机之间的进程

比如:

  我使用qq发送了一个消息,这个消息就通过我电脑上的qq客户端进程,先发送给了腾讯的服务器(对应的服务器进程),再由腾讯的服务器进程,把这个消息转发给对方电脑的qq进程

  这是我们通过网络编程做的最基础的一件事情,通过网络编程就可以达到一个天涯若比邻的效果,就能够让互联网上的很多主机进行配合工作,那么此时我们写的程序能做的事情就太多太多了…所以网络编程就是一个非常常见的需求场景。


2.什么是网络编程


  网络编程,指网络上的主机,通过不同的进程,以编程的方式实现网络通信(或称为网络数据传输)。

  当然,我们只要满足进程不同就行;所以即便是同一个主机,只要是不同进程,基于网络来传输数据,也属于网络编程。特殊的,对于开发来说,在条件有限的情况下,一般也都是在一个主机中运行多个进程来完成网络编程。

但是,我们一定要明确,我们的目的是提供网络上不同主机,基于网络来传输数据资源:

进程A:编程来获取网络资源
进程B:编程来提供网络资源

在这里插入图片描述

那么我们再具体进行网络编程时怎么用程序进行网络编程呢?

  操作系统就把网络编程的一些相关操作封装起来了,提供了一组API供程序员来使用

这些相关操作都是操作系统提供的功能,访问网络核心的硬件设备:网卡

  网卡也是归操作系统来管理的,所以操作系统将网卡做好管理之后,并且给程序员提供一些API,我们直接使用API就可以进行网路通信的操作了,这就是网络编程的基本情况

操作系统提供的网络编程API,我们给起了一个名字,叫 scoket (套接字) API,就是操作系统提供的一组关于网络通信的API接口


socket 的英文原义 :插槽


  由于操作系统提供的socket API 是 C语言风格的接口,所以在Java当中是没办法直接使用的, JDK就针对C语言的socket API 进行了封装, 在标准库中就有了一组类,这组类就能够让我们进行网络编程,但是我们要知道,这组类本质上仍然是调用的操作系统提供的scoket API

那么我们这里有一个问题?

Java能够调用C语言的函数吗?

可以的,不同的语言之间,很多都可以进行相互调用。要想跨语言调用,核心原理在于了解对应的语言的ABI(二进制编程接口)二进制指令规则等


3.网络编程中的基本概念


(1)发送端和接收端


在一次网络数据传输时:

发送端:数据的发送方进程,称为发送端。发送端主机即网络通信中的源主机。

接收端:数据的接收方进程,称为接收端。接收端主机即网络通信中的目的主机。

收发端:发送端和接收端两端,也简称为收发端。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iTZGQhhU-1642314827172)(24F9E0A8BCD249B8AF662ABD2EF9E4B1)]

注意:发送端和接收端只是相对的,只是一次网络数据传输产生数据流向后的概念。


(2)请求和响应


一般来说,获取一个网络资源,涉及到两次网络数据传输:

第一次:请求数据的发送
第二次:响应数据的发送。

好比在快餐店点一份炒饭:
先要发起请求:点一份炒饭,再有快餐店提供的对应响应:提供一份炒饭

在这里插入图片描述


(3)客户端和服务端


服务端:在常见的网络数据传输场景下,把提供服务的一方进程,称为服务端,可以提供对外服务。

客户端:获取服务的一方进程,称为客户端。

对于服务来说,一般是提供:

客户端获取服务资源
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sC4qWoqx-1642314827173)(576723A77754444EBBBDC4FE53068051)]

好比在银行办事:

银行提供存款服务:用户(客户端)保存资源(现金)在银行(服务端)

银行提供取款服务:用户(客户端)获取服务端资源(银行替用户保管的现金)

(4)常见的客户端服务端模型


最常见的场景,客户端是指给用户使用的程序,服务端是提供用户服务的程序:


1.客户端先发送请求到服务端

2.服务端根据请求数据,执行相应的业务处理

3.服务端返回响应:发送业务处理结果

4.客户端根据响应数据,展示处理结果(展示获取的资源,或提示保存资源的处理结果)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IJGiM5I3-1642314827174)(979D66E20BDF4DAF82F9233102CF82EE)]

这个客户端服务器模型在网络编程中非常常见,我们必须熟悉过程。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2y3z3tF8-1642314827174)(7AE53F1554174A008040FFC3BD508E9F)]

  为了理解这里的每个过程,咱们还是把一家餐馆比作是一个服务器


  然后呢,我现在来到这家餐馆吃饭,首先我要点菜吧,我说来一个回锅肉盖饭,这就是发送请求,然后餐馆把菜给端上来相当于把响应给返回了,而从点餐到上菜之间的这段时间相当于餐馆要去做这个饭,要注意,一家餐馆好不好不就相取决于做饭的过程吗,那做的好生意才好,这个做菜的过程就是根据请求计算响应,菜做好了服务员把菜端到我面前这就是响应显示。


  好了,这就是我们给大家介绍的一些关于网络编程的基础知识。我们具体写一个回显服务器及客户端的一个应用场景


4.回显服务器代码


import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

public class UdpEchoSever {
    
    

    private DatagramSocket socket = null;

//    port 表示端口号
//    服务器在启动的时候,需要关联上一个端口号
//    收到数据的时候,就会根据这个端口号决定把数据交给哪个进程
//    虽然此处port写的类型式 int,但是实际上端口号是一个两个字节的无符号整数
//    范围: 0~65535

    public UdpEchoSever(int port) throws SocketException {
    
    
        this.socket = new DatagramSocket(port);
    }

//    通过这个方法来启动服务器
    public void start() throws IOException {
    
    

        System.out.println("服务器启动!!");
//    服务器一般都是持续运行的 (7*24)
        while (true) {
    
    
//        1.读取请求,当前服务器不知道客户端啥时候发来请求。receive 方法也会阻塞
//            如果真的有请求过来,此时receive就会返回
//            receive 参数DatagramPacket 是一个输出型参数,socket 中读到的数据会设置到这个参数的对象中
//            DatagramPacket 在构造的时候需要指定一个缓冲区,通常使用 byte[]
            DatagramPacket requestPacket = new DatagramPacket(new byte[4096],4096);
//            这个数据包对象要程序员自定义缓冲区大小
            socket.receive(requestPacket);

//       把 reuqestPacket 对象里面的内容给取出来,作为一个字符串
            String request = new String(requestPacket.getData(),0,requestPacket.getLength());

//        2.根据请求来计算响应
            String response = process(request);


//        3.把响应写回给客户,这时候也需要构造一个 DatagramPacket
//            此处给DatagramPacket 设置的长度,必须是“字节的个数”
//            如果直接去response.length() 此处得到的式,字符串的长度,也就是“字符的个数”
//            当前的 responsePacket 在构造的时候,还需要指定这个包要发给谁
//            其实发送的目标,就是发送请求的一方
            DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),
                    response.getBytes().length,
                    requestPacket.getSocketAddress()
            );
            socket.send(responsePacket);

//            4.日志打印
            String log = String.format("[%s:%d]  req: %s   resp: %s",
                    requestPacket.getAddress().toString(),
                    requestPacket.getPort(),
                    request,response);
        }

    }

//     这里的process 方法负责的功能,就是根据请求来计算响应
//     当前是一个回显服务器,就是把客户端的请求直接返回回去
    private String process(String request) {
    
    
        return request;
    }

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

}


服务器代码注意事项

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SzPS9tlQ-1642314827175)(135E90F60877425A86AE274B6BD1CC3E)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nGOqOtc0-1642314827175)(E10720BB98C44DF3B981B0515E18C291)]


5.回显客户端代码


import java.io.IOException;
import java.net.*;
import java.util.Scanner;

public class UdpEchoClient {
    
    
    private DatagramSocket socket = null;
    private String severIp;
    private int port;

    public UdpEchoClient(String severIp,int port) throws SocketException {
    
    
        this.socket = new DatagramSocket(); // 这里不需要指定端口号,由系统自己分配
        this.severIp = severIp;
        this.port = port;
    }

    public void start() throws IOException {
    
    
    
        Scanner scanner = new Scanner(System.in);
        System.out.println("客户端启动!");
        
        while(true){
    
    
        
//            1.从标准输入读入一个数据,构造请求
            System.out.print("->");
            String request = scanner.nextLine();
            if(request.equals("exit")){
    
    
                System.out.println("客户端下线!");
                return;
            }
            
//           2. 将这个字符串构造成一个Udp请求,并发送数据
//            这里的 DatagramPacket 中,既要包含具体的数据,又要包含这个数据要发给谁
            DatagramPacket requestPacket = new DatagramPacket(
                    request.getBytes(),
                    request.getBytes().length,
                    InetAddress.getByName(severIp), port
            );
            socket.send(requestPacket);
            
//            3.尝试从服务器读取相应
            DatagramPacket responsePacket = new DatagramPacket(new byte[4096],4096);
            socket.receive(responsePacket);
            String response = new String(responsePacket.getData(),0,responsePacket.getLength());
            
//            4.打印日志
            String log = String.format("req: %s  resp: %s",request,response);
            System.out.println(log);
        }
    }

    public static void main(String[] args) throws IOException {
    
    

//        环回IP : 127.0.0.1 环回Ip 就相当于主机本身
//        当前,客户端和服务器在同一台主机上,所以客户端中写的服务器 Ip是 127.0.0.1 即可
//        如果在不同主机上,此时就需要写成对应主机的服务器Ip上
        UdpEchoClient udpEchoClient = new UdpEchoClient("127.0.0.1",9090);
        udpEchoClient.start();
    }
}


客户端代码注意事项

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PS73qked-1642314827176)(E5C6C153FC9C4443A33F8BB993015E07)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uc9UpUTi-1642314827176)(08E978AD287E424A84E167B33BF6EBB0)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qwGDOTBS-1642314827177)(ED0F8C28DCA74882889E5897A7D14FCC)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-S3Eqt4eC-1642314827177)(507248A58A724C578BE52663FB88E3B7)]

由于是客户端先给服务器发请求(客户端服务器概念的定义决定的)

既然是客户端先发,客户端这边就得先知道服务器的 IP 和 端口号

服务器如果收到了客户端的数据,服务器也就知道了客户端的IP 和端口号

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KBdl8ove-1642314827178)(85E74D3839F84E10A9C4D4C03788562C)]

运行展示


在客户端依次输入结果,打印请求和响应,同时服务器打印客户端主机和端口号及请求响应,客户端输入 exit,客户端下线…

在这里插入图片描述


  今天网络编程就讲到这里,希望大家多多练习,谢谢大家的阅读与欣赏…

猜你喜欢

转载自blog.csdn.net/rain67/article/details/122523024