socket API-UDP(客户端-服务器交互原理)

服务器端程序

public class UdpEchoServer {
    //对于一个服务器程序来说,核心流程也是要分成两步:
    /*
    * 1:进行初始化操作(实例化Socket对象)
    * 2:进入主循环,接收并处理请求,(主循环就是一个“死循环”,因为服务器需要24小时不间断工作)
    *    a)读取请求并解析
    *    b)根据请求计算响应
    *    c)把响应结果写回到客户端*/
    private DatagramSocket socket = null;//使用DatagramSocket类创建一个socket对象,先让它为null
    public UdpEchoServer(int port) throws SocketException {//定义一个构造方法,传入端口号
         socket = new DatagramSocket(port);//利用传进来的端口实例化socket对象,这是第一步进行初始化操作
        /*这个构造方法的作用是:当new这个socket对象的时候,就会让当前的socket对象和一个ip地址以及一个
        * 端口号关联起来(绑定端口),我们这个地方没有写ip,就默认是0.0.0.0,这是一个特殊的ip,因为一台主机
        * 可能有多个网卡,每个网卡对应一个ip,这个特殊的ip会关联到这个主机的所有的网卡的ip,写服务器一般都是
        * 用这个全0的特殊ip,未来客户端就按照这个ip+端口来访问服务器。
        * 另外补充一点,socket对象本质上可以看做一个文件,这个文件是网卡的抽象,当我们要对网卡进行
        * 操作的时候,可以通过socket来处理,“一切皆文件”*/
    }
    public void start() throws IOException {//这个方法用来启动服务器
        System.out.println("服务器启动");
        while (true){//进入主循环,在这个主循环里去处理a,b,c这三个事情
            // a)读取请求并解析
            DatagramPacket requestPacket = new DatagramPacket(new byte[4096],4096);//通过DatagramPacket这个类来new一个requestPacket对象,同时关联了一个缓冲区
            //准备完缓冲区之后,开始读取请求
            socket.receive(requestPacket);
            /*这个操作会读取从socket上收到的一切来自客户端请求,程序启动之后马上就能执行到receive操作,
            * 服务器启动了之后,客户端也发送请求了吗,显然不一定,客户端啥时候发请求无法确定,大概率的情况是
            * ,调用receive的时候,客户端还没谱呢,还没发任何数据,此时receive操作就会阻塞,一直阻塞到真的有请求
            * 过来了为止(此处的阻塞时间完全不可预期),当真的有客户端的数据过来了之后,此时receive就会把收到的
            * 数据放到DatagramPacket对象的缓存区中*/
            String request = new String(requestPacket.getData(),0,requestPacket.getLength()).trim();
            /*把存放在缓冲区里的请求数据从byte[]类型,转换为String类型,String方便后续操作,因为用户发送的数据所占空间大小可能远远小于4096,而此处
            * getLength得到的长度就是4096,通过trim就可以干掉不必要的空白字符*/
            //b)根据请求计算响应
            String response = process(request);//使用一个方法来处理响应
            //c)把响应结果写回到客户端,响应数据就是response,需要把它包装成DatagramPacket类的一个对象
            DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),response.getBytes().length,requestPacket.getSocketAddress());
            /*response.getBytes():将response的字符串类型转化为字节类型
            * response.getBytes().length:得到的是字节数量,这里不能写成response.length,在字符串类型的基础上.length得到的是字符数量,这里传输就是字节类型
            * requestPacket.getSocketAddress():这个包要发给谁(目的ip和端口是谁),此处的地址就是客户端的ip和端口,这两个信息就包含在requestPacket内部了*/
            socket.send(responsePacket);//把响应结果发送到客户端
            //打印一条请求的日志,这一步其实不需要
            System.out.printf("[%s:%d] req: %s; resp: %s\n",requestPacket.getAddress().toString(),requestPacket.getPort(),request,response);
        }
    }

    private String process(String request) {
        /*这里做一个简单的echo server,请求内容是啥,响应内容就是啥。
        * 如果是一个更复杂的服务器,此处就需要包含很多的业务逻辑来进行具体的计算*/
        return request;
    }

    public static void main(String[] args) throws IOException {
        UdpEchoServer server = new UdpEchoServer(9090);//1.进行初始化操作
        server.start();//2.进入主循环,接收并处理请求
    }
}

客户端程序

public class UdpEchoClient {
    //对于一个客户端程序来说,核心流程也是要分成两步
    /*1:进行初始化操作
     * 2:进入主循环:
     *   a)从用户这里读取输入的数据
     *   b)构造请求并发送给服务器
     *   c)读取服务器的响应
     *   d)把响应写会显示到桌面上*/
    private DatagramSocket socket = null;//使用DatagramSocket类创建一个socket对象,先让它为null
    private String serverIp;
    private int serverPort;
    //需要在启动客户端的时候来指定需要连接哪个服务器
    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){//进入主循环,在这个主循环里去处理a,b,c,d这四个步骤
            //a)从用户这里读取输入的数据
            String request = scanner.nextLine();//读取控制台输入的一行数据作为请求
            if(request.equals("exit")){
                break;//跳出循环
            }
            //b)构造请求并发送给服务器
            DatagramPacket requestPacket = new DatagramPacket(request.getBytes(),request.getBytes().length, InetAddress.getByName(serverIp),serverPort);//客户端构造请求
            socket.send(requestPacket);//把请求发送给服务器
            //c)读取服务器的响应
            DatagramPacket responsePacket = new DatagramPacket(new byte[4096],4096);
            /*通过DatagramPacket这个类创建一个缓冲区,创建完缓冲区之后再从服务器读取响应,这段代码和服务器读取请求是一模一样的*/
            socket.receive(responsePacket);
            String response = new String(responsePacket.getData(),0,responsePacket.getLength());
            //d)把响应写回显示到桌面上
            System.out.println(response);
        }
    }

    public static void main(String[] args) throws IOException {
        UdpEchoClient client = new UdpEchoClient("127.0.0.1",9090);
        /*127.0.0.1这是一个特殊的IP,称为环回IP,自己访问自己,
        * 如果服务器和客户端都在同一台主机上,此处的ip就是环回ip
        * 如果服务器和客户端不在同一台主机上,此处的ip就要写成服务器的ip
        * */
        client.start();
    }
    /*站在客户端角度,理解此处通信的五元组:
    * 协议类型:UDP
    * 源ip:客户端的ip(客户端所在主机的ip)
    * 源端口:客户端的端口(操作系统自动分配的端口)
    * 目的ip:服务器的ip(服务器和客户端在同一个主机上,ip就是127.0.0.1)
    * 目的端口:9090(服务器启动的时候绑定的端口)*/
}

体会客户端-服务器基本工作流程

在这里插入图片描述

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值