网络编程——详解UDP协议的实现(SocketAPI),本机的服务器和客户端连接

基础概念

客户端: 主动发起请求的一方,客户端给服务器发送的数据“请求”(Request)
服务器: 被动接受请求的一方,服务器给客户端发回的数据“响应”(Response)

服务器不知道什么时候会受到客户端请求,服务器只能一直准备等待7-24小时

通信中的五个概念

源IP: 发件人地址
在这里插入图片描述
32位的整数,用三个.分成四个部分,每一个部分一个字节(0-255)
源端口: 发件人姓名
0-65535之间,(占两个字节的整数),一个服务器需要关联一个固定的端口号
目的IP: 收件人地址
目的端口:收件人姓名
协议类型
在这里插入图片描述
IP是用来决定互联网上的某个主机的位置. 端口是决定数据交给这个主机.上的哪个程序.

Socket API

socket对象本质上是一个文件,文件是网卡的抽象

Java标准库中提供了两种风格
1、【UDP】DatagramSocket
面向数据报(发送接收数据,必须以一定的数据包为单位进行传输)
最简单的客户端服务器
客户端给服务器发送一个字符串, 服务器把这个字符串原封不动的返回. (回显服务器echo server)相当于服务器开发中的hello world

如果Packet用于receive,只指定缓冲区就可以,(地址是接收数据的时候由内核填充的)
如果这个Packet用户send,除了指定缓冲区,还需要指定发给谁(用户手动设定). 一种是直接设定InetAddress对象(里面同时包含了IP和port),还可以把IP和port分开设置.

2、【TCP】ServerSocket
面向字节流

服务器必须绑定端口
一个端口号通常情况下,只能被一个进程绑定
服务器绑定了端口之后,客户端才能访问. (买东西的时候需要填好你的收件人地址和收件人姓名,卖家才能发货)

客户端必须不绑定端口(由操作系统自动分配一个空闲端口)
客户端不能绑定的原因:如果客户端绑定了的话,一个主机上就只能启动一个客户端了

扫描二维码关注公众号,回复: 11458506 查看本文章

UDP协议

服务器

最简单的客户端服务器,客户端给服务器发送一个字符串,服务器原封不动返回(回显服务器 echoserver)

1、进行初始化,实例化Socket对象
2、进入主循环(死循环,一直接受请求)
a、读取数据并解析
b、根据请求计算响应
c、把结果写回到客户端

1、进行初始化,实例化Socket对象在这里插入图片描述

  private DatagramSocket socket = null;
    public UdpEchoServer(int port) throws SocketException {
        socket = new DatagramSocket(port);
        //new这个Socket对象就会让当前的socket对象和一个ip地址以及一个端口号关联(绑定端口)
        //未来客户端就按照这个ip+端口访问服务器
        //如果在构造socket的时候没写ip默认是0.0.0.0(特殊ip会关联到这个主机的所有网卡)
    }

new这个Socket对象就会让当前的socket对象和一个ip地址以及一个端口号关联(绑定端口),未来客户端就按照这个ip+端口访问服务器
如果在构造socket的时候没写ip默认是0.0.0.0(特殊ip会关联到这个主机的所有网卡)

2、进入主循环(死循环,一直接受请求)
a、读取数据并解析
b、根据请求计算响应
c、把结果写回到客户端
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
DatagramPacket这个类是udp发送接收数据基本单位
IP是用来决定互联网上的某个主机的位置. port是决定数据交给这个主机.上的哪个程序.

    public void start() throws IOException {
        System.out.println("服务器启动");
        while (true){
            //a. 读取请求并解析
            DatagramPacket requestPacket = new DatagramPacket(new byte[4096],4096);//udp发送接收数据基本单位
           //实例化中传入一个缓冲区
            //socket对象本质上是一个文件,文件是网卡的抽象
            socket.receive(requestPacket);//程序启动马上执行receive,读取从客户端收到的请求
            //receive被堵塞,知道真的有数据来,receive就会把数据放入到刚刚new的缓冲区当中

            //把收到的请求数据转成一个String(本来是一个byte[]),用trim()是为了去掉数组中的空白部分,提高效率
            String request = new String(requestPacket.getData(),
                    0,requestPacket.getLength()).trim();

            //b。根据请求计算响应
            String response = process(request);

            //c。把响应传回给客户端,传回的相应就是response变量,需要包装成Packet对象传回
            DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),
                    response.getBytes().length, requestPacket.getSocketAddress());
            //利用String中的getBytes()方法把response构造成Packet对象,之后是数据的长度,
            // 还有包要发送给谁,requestPacket.getSocketAddress(),获取客户端地址
            //此处的地址就是客户端的地址和端口,这两个信息就包含在requePacket内部
            //response.getBytes().length是字节数、response.getBytes().length()是字符数
            socket.send(responsePacket);


            //方便观察打印日志
            System.out.printf("[%s:%d] 目的IP :%s; 目的端口号: %s\n",requestPacket.getAddress().toString(),
                   requestPacket.getPort(),request,response );
        }
    }
    public String process (String s){
        //因为是echo server回显服务器,所以process方法只要接收然后直接返回就行,不需要其他操作
        //如果更复杂,在这个方法里面可以包含更多的其他操作
        return s;
    }

服务器运行:

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

public class UdpEchoServer {
    //1、进行初始化,实例化Socket对象
    //2、进入主循环(死循环,一直接受请求)
    //a、读取数据并解析
    //b、根据请求计算响应
    //c、把结果写回到客户端
    private DatagramSocket socket = null;
    public UdpEchoServer(int port) throws SocketException {
        socket = new DatagramSocket(port);
        //new这个Socket对象就会让当前的socket对象和一个ip地址以及一个端口号关联(绑定端口)
        //未来客户端就按照这个ip+端口访问服务器
        //如果在构造socket的时候没写ip默认是0.0.0.0(特殊ip会关联到这个主机的所有网卡)
    }
    public void start() throws IOException {
        System.out.println("服务器启动");
        while (true){
            //a. 读取请求并解析
            DatagramPacket requestPacket = new DatagramPacket(new byte[4096],4096);//udp发送接收数据基本单位
           //实例化中传入一个缓冲区
            //socket对象本质上是一个文件,文件是网卡的抽象
            socket.receive(requestPacket);//程序启动马上执行receive,读取从客户端收到的请求
            //receive被堵塞,知道真的有数据来,receive就会把数据放入到刚刚new的缓冲区当中

            //把收到的请求数据转成一个String(本来是一个byte[]),用trim()是为了去掉数组中的空白部分,提高效率
            String request = new String(requestPacket.getData(),
                    0,requestPacket.getLength()).trim();

            //b。根据请求计算响应
            String response = process(request);

            //c。把响应传回给客户端,传回的相应就是response变量,需要包装成Packet对象传回
            DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),
                    response.getBytes().length, requestPacket.getSocketAddress());
            //利用String中的getBytes()方法把response构造成Packet对象,之后是数据的长度,
            // 还有包要发送给谁,requestPacket.getSocketAddress(),获取客户端地址
            //此处的地址就是客户端的地址和端口,这两个信息就包含在requePacket内部
            //response.getBytes().length是字节数、response.getBytes().length()是字符数
            socket.send(responsePacket);


            //方便观察打印日志
            System.out.printf("[%s:%d] 目的IP :%s; 目的端口号: %s\n",requestPacket.getAddress().toString(),
                   requestPacket.getPort(),request,response );
        }
    }
    public String process (String s){
        //因为是echo server回显服务器,所以process方法只要接收然后直接返回就行,不需要其他操作
        //如果更复杂,在这个方法里面可以包含更多的其他操作
        return s;
    }

    public static void main(String[] args) throws IOException {
        UdpEchoServer udpEchoServer = new UdpEchoServer(9090);//指定一个端口号
        udpEchoServer.start();
    }
}

服务器启动

服务器一直在等待客户端的请求
在这里插入图片描述

客户端

站在客户端角度理解此处通信的五元组
协议类型: UDP
源IP: 客户端的IP (客户端所在的主机IP)
源端口: 客户端的端口(操作系统自动分配的端口)
目的IP: 服务器的IP (服务器和客户端在同一个主机上. IP就是127.0.0.1)
目的端口: 9090 (服务器启动的时候绑定的端口)

构造,构造的时候需要传入要连接的服务器ip和端口
客户端的执行分四步
1、从用户读取输入数据
2、构造请求发送给服务器
3、从服务器读取响应
4、把响应写回给客户端

构造,构造的时候需要传入要连接的服务器ip和端口
在这里插入图片描述

  private DatagramSocket socket = null;
    private String serverIp;
    private int serverPort;

    //构造的时候需要传入要连接的服务器ip和端口
    public UdpEchoClient(String serverIp, int serverPort) throws SocketException {
        this.serverIp = serverIp;
        this.serverPort = serverPort;
        socket = new DatagramSocket();//构建客户端socket时不用绑定端口号
        //服务器必须绑定端口,客户端必须不绑定端口(由操作系统自动分配一个空闲端口)
        //如果客服端绑定,一个主机上就只能启动一个客户端了
    }

客户端的执行分四步
在这里插入图片描述

 public void start() throws IOException {
        Scanner scanner = new Scanner(System.in);
        while (true) {
            //1、读取用户输入的数据
            System.out.println("输入数据:");
            String request = scanner.nextLine();
            if(request.equals("退出")){
                break;
            }
            //2、构造请求发送给服务器
            //里面传入缓冲区,还有缓冲区长度,还有服务器ip 服务器端口号
            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(requestPacket);
            //response中放入请求
            String response = new String(responsePacket.getData(),0,
                    responsePacket.getLength());

            //4、显示响应数据
            System.out.println(response);

        }

客户端总代码

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

public class UdpEchoClient  {
    //构造,构造的时候需要传入要连接的服务器ip和端口
    //客户端的执行分四步
    //1、从用户读取输入数据
    //2、构造请求发送给服务器
    //3、从服务器读取响应
    //4、把响应写回给客户端
    private DatagramSocket socket = null;
    private String serverIp;
    private int serverPort;

    //构造的时候需要传入要连接的服务器ip和端口
    public UdpEchoClient(String serverIp, int serverPort) throws SocketException {
        this.serverIp = serverIp;
        this.serverPort = serverPort;
        socket = new DatagramSocket();//构建客户端socket时不用绑定端口号
        //服务器必须绑定端口,客户端必须不绑定端口(由操作系统自动分配一个空闲端口)
        //如果客服端绑定,一个主机上就只能启动一个客户端了
    }
    public void start() throws IOException {
        Scanner scanner = new Scanner(System.in);
        while (true) {
            //1、读取用户输入的数据
            System.out.println("输入数据:");
            String request = scanner.nextLine();
            if(request.equals("退出")){
                break;
            }
            //2、构造请求发送给服务器
            //里面传入缓冲区,还有缓冲区长度,还有服务器ip 服务器端口号
            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);
            //response中放入请求
            String response = new String(responsePacket.getData(),0,
                    responsePacket.getLength());

            //4、显示响应数据
            System.out.println(response);

        }
    }

    public static void main(String[] args) throws IOException {
        UdpEchoClient udpEchoClient = new UdpEchoClient("127.0.0.1",9090);
        //127.0.0.1表示环回ip,自己访问自己,刚刚所写的服务器和客户端都是在一个主机上,所以使用这个ip
        //如果不在同一个主机上,此处的ip就要写成要访问的服务器ip,9090就是上面写的服务器的端口号
        udpEchoClient.start();

    }

}

运行服务器和客户端

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/char_m/article/details/106887494