UDP通信(一发一收,多发多收)、TCP通信(一发一收,多发多收、同时接收多个客户端的消息)、B/S架构的原理

UDP通信

1、只要用了java.net.DatagramSocket包底层就会自动用UDP通信

2、UDP通信中服务端一直打开,客户端可以关闭

UDP通信相关的API:

UDP单发单收

java 复制代码
package com.itheima.udp1;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

public class UDPClientDemo1 {
    public static void main(String[] args) throws  Exception{
        System.out.println("客户端启动...");
        //目标:完成UDP通信一发一收,客户端开发
        //1、创建发送端对象(代表抛韭菜的人)
        DatagramSocket socket = new DatagramSocket();//默认分配端口
        //2、创建数据包对象封装要发送的数据(代表韭菜盘子)
        byte[] bytes = "我是客户端".getBytes();//转成字节
        /**
         * 参数一:发送的数据,字节数据()芥韭菜
         * 参数二:发送的字节长度
         * 参数三:服务端程序的IP地址
         * 参数四:服务端程序的的端口号
         */
        DatagramPacket packet = new DatagramPacket(bytes, bytes.length, InetAddress.getLocalHost(), 10086);
        //3、让发送端对象发送数据包的数据
        socket.send(packet);
        
			 socket.close();
    }
}

//=================分界线=================

package com.itheima.udp1;

import java.net.DatagramPacket;
import java.net.DatagramSocket;

public class UDPServicerDemo2 {
    public static void main(String[] args) throws Exception {
        System.out.println("服务端启动了");
        //目标:完成UDP通信一发一收,服务端开发
        //1、创建接收端对象,注册端口(接韭菜的人)
        DatagramSocket socket = new DatagramSocket(10086);
        //2、创建数据包对象,封装接收到的数据(韭菜盘子)
        byte[] bytes = new byte[1024*64];
        DatagramPacket packet = new DatagramPacket(bytes, bytes.length);
        //3、让接收端对象,将接收到的数据封装到数据包对象中
        socket.receive(packet);
        //4、看看数据是否收到了
        //获取当前收到的数据长度
        int length = packet.getLength();
        String data = new String(bytes,0,length);
        System.out.println("服务端收到了" + data);

        //获取对方的IP对象和程序端口
        String ip = packet.getAddress().getHostAddress();
        int port = packet.getPort();
        System.out.println("服务端收到来自" + ip + ":" + port);
        
        socket.close();
    }
}

UDP多发多收

java 复制代码
package com.itheima.demo3udp2.udp1;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner;

public class UDPClientDemo1 {
    public static void main(String[] args) throws  Exception{
        System.out.println("客户端启动...");
        //目标:完成UDP通信多发多收,客户端开发
        //1、创建发送端对象(代表抛韭菜的人)
        DatagramSocket socket = new DatagramSocket();//默认分配端口

        Scanner sc = new Scanner(System.in);
        while (true) {
            //2、创建数据包对象封装要发送的数据(代表韭菜盘子)
            System.out.println("请输入要发送的数据:");
            String data = sc.nextLine();//输入数据

            //如果用户输入exit,则退出
            if ("exit".equals(data)) {
                System.out.println("客户端退出...");
                socket.close();
                break;
            }

            byte[] bytes = data.getBytes();//转成字节
            DatagramPacket packet = new DatagramPacket(bytes, bytes.length, InetAddress.getLocalHost(), 10086);
            //3、让发送端对象发送数据包的数据
            socket.send(packet);
        }
    }
}

//=================分界线=================

package com.itheima.demo3udp2.udp1;

import java.net.DatagramPacket;
import java.net.DatagramSocket;

public class UDPServicerDemo2 {
    public static void main(String[] args) throws Exception {
        System.out.println("服务端启动了");
        //目标:完成UDP通信多发多收,服务端开发
        //1、创建接收端对象,注册端口(接韭菜的人)
        DatagramSocket socket = new DatagramSocket(10086);
        //2、创建数据包对象,封装接收到的数据(韭菜盘子)
        byte[] bytes = new byte[1024*64];
        DatagramPacket packet = new DatagramPacket(bytes, bytes.length);

        while (true) {
            //3、让接收端对象,将接收到的数据封装到数据包对象中
            socket.receive(packet);//等待式接收数据

            //4、看看数据是否收到了
            //获取当前收到的数据长度
            int length = packet.getLength();
            String data = new String(bytes,0,length);
            System.out.println("服务端收到了" + data);

            //获取对方的IP对象和程序端口
            String ip = packet.getAddress().getHostAddress();
            int port = packet.getPort();
            System.out.println("服务端收到来自" + ip + ":" + port);

            System.out.println("========================================");
        }

    }
}

UDP的接收端为什么可以接收很多发送端的消息?

接收端只负责接收数据包,无所谓是哪个发送端的数据包

TCP通信

只要用了java.net.Socket类底层就会自动用UDP通信

TCP通信一发一收

TCP一发一收通信中客户端开发

TCP一发一收通信中客户端开发流程:

客户端是通过java.net包下的Socket类来实现的

TCP一发一收通信中客户端开发

TCP一发一收通信中服务端开发流程:

服务端是通过java.net包下的ServerSocket类来实现的

java 复制代码
package com.itheima.demo4tcp1;

import java.io.DataOutputStream;
import java.io.OutputStream;
import java.net.Socket;

public class ClientDemo1 {
    public static void main(String[] args) throws Exception {
        //目标:实现TCP通信下一发一收,客户端开发
        System.out.println("客户端启动...");

        //1、创建Socket管道对象,请求与服务端的Socket连接,可靠连接
        Socket socket = new Socket("192.168.1.xx", 10086);

        //2、从Socket通信管道中得到一个字节输出流
        OutputStream os = socket.getOutputStream();

        //3、特殊数据流
        DataOutputStream dos = new DataOutputStream(os);
        dos.writeInt(1);
        dos.writeUTF("纳兰家   纳兰嫣然");

        //4、关闭资源
        socket.close();
    }
}

//=================分界线=================

package com.itheima.demo4tcp1;

import java.io.DataInputStream;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class ServerDemo2 {
    public static void main(String[] args) throws Exception {
        //目标:实现TCP通信下一发一收:服务端开发
        System.out.println("服务端启动了");

        //1、创建服务端ServerSocket对象,绑定端口号,监听客户端连接
        ServerSocket ss = new ServerSocket(10086);

        //2、调用accept方法,阻塞等待客户端连接,一旦有客户端连接会返回一个Socket对象
        Socket socket = ss.accept();

        //3、获取输入流,读取客户端发送的数据
        InputStream is = socket.getInputStream();

        //4、把字节输入流包装成特殊数据输入流
        DataInputStream dis = new DataInputStream(is);

        //5、读取数据
        int id = dis.readInt();
        String msg = dis.readUTF();
        System.out.println("id=" + id + "收到的客户端msg = " + msg);

        //6、客户端的ip和端口(谁发给我的)
        System.out.println("客户端的ip = " + socket.getInetAddress().getHostAddress());
        System.out.println("客户端的端口 = " + socket.getPort());
    }
}

TCP一发一收小结

1、TCP通信,客户端的代表是谁?

Socket类

2、TCP通信,如何使用Socket管道发送、接收数据?

分别使用输入流socket.getInputStream()和输出流socket.getOutputStream()发送和接收数据

3、TCP通信服务端用的类是谁?

ServerSocket类

TCP多发多收

如何实现多发多收?

1、客户端使用循环反复地发送消息

2、服务端使用循环反复地接收消息

改造一发一收的代码

java 复制代码
package com.itheima.demo5tcp2.demo4tcp1;

import java.io.DataOutputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;

public class ClientDemo1 {
    public static void main(String[] args) throws Exception {
        //目标:实现TCP通信下多发多收,客户端开发
        System.out.println("客户端启动...");

        //1、创建Socket管道对象,请求与服务端的Socket连接,可靠连接
        Socket socket = new Socket("192.168.1.xx", 10086);

        //2、从Socket通信管道中得到一个字节输出流
        OutputStream os = socket.getOutputStream();

        //3、特殊数据流
        DataOutputStream dos = new DataOutputStream(os);
        Scanner sc = new Scanner(System.in);
        while (true) {
            System.out.println("请输入要发送的数据:");
            String data = sc.nextLine();
            if("exit".equals( data)){
                System.out.println("客户端退出...");
                socket.close();
                break;
            }

            dos.writeUTF(data);//发送数据
            dos.flush();//刷新
        }

    }
}

//=================分界线=================

package com.itheima.demo5tcp2.demo4tcp1;

import java.io.DataInputStream;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class ServerDemo2 {
    public static void main(String[] args) throws Exception {
        //目标:实现TCP通信下多发多收:服务端开发
        System.out.println("服务端启动了");

        //1、创建服务端ServerSocket对象,绑定端口号,监听客户端连接
        ServerSocket ss = new ServerSocket(10086);

        //2、调用accept方法,阻塞等待客户端连接,一旦有客户端连接会返回一个Socket对象
        Socket socket = ss.accept();

        //3、获取输入流,读取客户端发送的数据
        InputStream is = socket.getInputStream();

        //4、把字节输入流包装成特殊数据输入流
        DataInputStream dis = new DataInputStream(is);

        while (true) {
            //5、读取数据
            String msg = dis.readUTF();//等待读取客户端发送的数据
            System.out.println("收到的客户端msg = " + msg);

            //6、客户端的ip和端口(谁发给我的)
            System.out.println("客户端的ip = " + socket.getInetAddress().getHostAddress());
            System.out.println("客户端的端口 = " + socket.getPort());
            System.out.println("========================================");
        }
    }
}

TCP通信同时接收多个客户端的消息

如何实现TCP通信支持与多个客户端同时通信?

1、使用主线程定义了循环负责与每个客户端Socket连接(也就是主线程通过阻塞管道不断地等待每一个客户端,然后把数据接收部分丢给每一个子线程)

2、每来一个客户端Socket就分配一个独立的服务端子线程来接收数据

客户端不用改造,服务端改造,并加入了线程类

java 复制代码
package com.itheima.demo6tcp3.demo4tcp1;

import java.io.DataOutputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;

public class ClientDemo1 {
    public static void main(String[] args) throws Exception {
        //目标:实现TCP通信下多发多收,支持多个客户端开发
        System.out.println("客户端启动...");

        //1、创建Socket管道对象,请求与服务端的Socket连接,可靠连接
        Socket socket = new Socket("192.168.1.77", 10086);

        //2、从Socket通信管道中得到一个字节输出流
        OutputStream os = socket.getOutputStream();

        //3、特殊数据流
        DataOutputStream dos = new DataOutputStream(os);
        Scanner sc = new Scanner(System.in);
        while (true) {
            System.out.println("请输入要发送的数据:");
            String data = sc.nextLine();
            if("exit".equals( data)){
                System.out.println("客户端退出...");
                socket.close();
                break;
            }

            dos.writeUTF(data);//发送数据
            dos.flush();//刷新
        }

    }
}

//=================分界线=================

package com.itheima.demo6tcp3.demo4tcp1;

import java.io.DataInputStream;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class ServerDemo2 {
    public static void main(String[] args) throws Exception {
        //目标:实现TCP通信下多发多收:服务端开发
        System.out.println("服务端启动了");

        //1、创建服务端ServerSocket对象,绑定端口号,监听客户端连接
        ServerSocket ss = new ServerSocket(10086);

        while (true) {
            //2、调用accept方法,阻塞等待客户端连接,一旦有客户端连接会返回一个Socket对象
            Socket socket = ss.accept();
            System.out.println("一个客户端上线了:" + socket.getInetAddress().getHostAddress());

            //3、把这个客户端管道交给一个独立的子线程专门负责接收这个管道的消息
            new ServerReader(socket).start();
        }
    }
}

//=================分界线=================

package com.itheima.demo6tcp3.demo4tcp1;

import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;

public class ServerReader extends  Thread{
    private Socket socket;
    public ServerReader(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        //读取管道的消息
        try {
            //3、获取输入流,读取客户端发送的数据
            InputStream is = socket.getInputStream();

            //4、把字节输入流包装成特殊数据输入流
            DataInputStream dis = new DataInputStream(is);

            while (true) {
                //5、读取数据
                String msg = dis.readUTF();//等待读取客户端发送的数据
                System.out.println("收到的客户端msg = " + msg);

                //6、客户端的ip和端口(谁发给我的)
                System.out.println("客户端的ip = " + socket.getInetAddress().getHostAddress());
                System.out.println("客户端的端口 = " + socket.getPort());
                System.out.println("========================================");
            }
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("客户端下线了" + socket.getInetAddress().getHostAddress());
        }
    }
}

B/S架构的原理

1、B/S架构不用写客户端,浏览器就是客户端,用了HTTP协议

2、写数据时必须严格按照HTTP协议格式书写

java 复制代码
package com.itheima.demo7tcp4;

import com.itheima.demo6tcp3.demo4tcp1.ServerReader;

import java.net.ServerSocket;
import java.net.Socket;

public class ServerDemo {
    public static void main(String[] args) throws Exception {
        //目标:BS架构的原理理解
        System.out.println("服务端启动了");

        //1、创建服务端ServerSocket对象,绑定端口号,监听客户端连接
        ServerSocket ss = new ServerSocket(10086);

        while (true) {
            //2、调用accept方法,阻塞等待客户端连接,一旦有客户端连接会返回一个Socket对象
            Socket socket = ss.accept();
            System.out.println("一个客户端上线了:" + socket.getInetAddress().getHostAddress());

            //3、把这个客户端管道交给一个独立的子线程专门负责接收这个管道的消息
            new ServerReader(socket).start();
        }
    }
}

//=================分界线=================

package com.itheima.demo7tcp4;

import java.io.*;
import java.net.Socket;

public class ServerReader extends Thread{
    private Socket socket;
    public ServerReader(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        //读取管道的消息
        try {
            //给当前对应的浏览器管道相应一个网页数据回去
            OutputStream os= socket.getOutputStream();
            //通过字节输出流包装写出去数据给浏览器
            //把字节输出流包装成打印流
            PrintStream ps = new PrintStream(os);
            //写相应的网页数据出去
            ps.println("HTTP/1.1 200 OK");
            ps.println("Content-Type:text/html;charset=UTF-8");
            ps.println();//必须换行
            ps.println("<html>");
            ps.println("<body>");
            ps.println("<h1>hello world</h1>");
            ps.println("</body>");
            ps.println("佛怒火莲");
            ps.println("</html>");
            ps.close();
            socket.close();
        } catch (Exception e) {
            System.out.println("客户端下线了" + socket.getInetAddress().getHostAddress());
        }
    }
}
相关推荐
弹简特2 小时前
【Java-阔怕的JVM】JVM
java·开发语言·jvm
小冷coding2 小时前
工作流是什么呢?
java·面试
像少年啦飞驰点、2 小时前
零基础入门 Redis:从缓存原理到 Spring Boot 集成实战
java·spring boot·redis·缓存·编程入门
虾说羊2 小时前
Springboot中配置欢迎页的方式
java·spring boot·后端
qq_12498707532 小时前
基于Spring Boot的长春美食推荐管理系统的设计与实现(源码+论文+部署+安装)
java·前端·spring boot·后端·毕业设计·美食·计算机毕业设计
九皇叔叔2 小时前
【02】SpringBoot3 MybatisPlus 加入日志功能
java·mysql·mybatis·日志·mybatisplus
java1234_小锋2 小时前
什么是Java可重入锁?
java·开发语言
不会c+2 小时前
Spring和Springboot的区别
java·spring boot·spring
懈尘2 小时前
基于Spring Boot与LangChain4j的AI驱动新闻系统设计与工程实现
java·大数据·人工智能·spring boot·后端·langchain