当前位置: 首页 > article >正文

【高级编程】网络编程 基于 TCPUDP 协议的 Socket 编程

文章目录

    • IP地址
    • Socket
      • 基于 TCP 协议的 Socket 编程
      • 基于 UDP 协议的 Socket 编程

IP地址

IP地址(Internet Protocol):唯一标识网络上的每一台计算机

IP地址的组成:32位,由4个8位二进制数组成

11000000.10101000.00000001.11001000 ==> 192.168.1.200

IP地址 = 网络地址 + 主机地址

  • 网络地址:标识计算机或网络设备所在的网段
  • 主机地址:标识特定主机或网络设备
8位 + 24位前8位取值范围
A类网络 主机 主机 主机1~126
B类网络 网络 主机 主机128~191
C类网络 网络 网络 主机192~223
D类用于组播通信224~239
E类用于科研240~255

IP地址的配置和检测

查看IP地址,检测网络是否畅通

  • 查看本机的IP地址:ipconfig

  • 测试网络是否通畅:ping 目标IP地址

DNS域名解析

DNS:Domain Name System,域名系统

网络服务器

通常指在网络环境下,具有较高计算能力,能够提供用户服务功能的计算机

网络服务器 ===> 邮件服务器 Web服务器

  • Microsoft IIS
  • APACHE
  • Apache Tomcat

网络通信协议

为了在网络中不同的计算机之间进行通信而建立的规则、标准或约定的集合

OSI七层网络模型TCP/IP四层概念模型协议
应用层应用层规定了数据的传输格式HTTP FTP TFTP NFS WAIS SMTP
表示层Telnet Rlogin SNMP
会话层SMTP DNS
传输层传输层端口到端口的连接通信TCP UDP
网络层网络层引入一套新的地址来区分不同的广播域ICMP IP ARP RARP AKP UUCP
数据链路层数据链路层定义电信号的分组方式FDDI Ethernet Arpanet PDN SLIP PPP
物理层基于电器特征的高低电压(电信号)高电压代表1 低电压代表0IEEE 802.1A IEEE 802.2到IEEE 802.11

TCP VS UDP

TCPUDP
是否连接面向连接面向非连接
传输可靠性可靠不可靠
速度
在这里插入图片描述在这里插入图片描述

TCP可靠性源于序列号和确认序列号,UDP不能保证数据交付的可靠性

Socket

Socket 是一种网络通信的基础工具,它用于在计算机网络上进行数据传输。简而言之,Socket 提供了应用程序与网络之间的接口,使得应用程序可以通过网络发送和接收数据。

Socket 的底层机制复杂,Java 平台提供了一些简单的 API,可以更简单有效的使用Socket 开发而无需了解底层机制

  • 通信链路的端点就被称为 " 套接字 "(英文名Socket)

  • 是提供给应用程序的接口

Socket 类是 java.net 包的一部分,用于实现基于TCP的网络通信。

java.net包

  • Socket

  • ServerSocket

  • DatagramPacket

  • DatagramSocket

  • InetAddress

基于 TCP 协议的 Socket 编程

基于TCP 协议的 Socket 网络通信:用来实现双向安全连接网络通信

三次握手(建立连接)四次挥手(关闭连接)
A -> B 携带syn数据包A -> B 发送fin包
B -> A 同意连接 发送syn+ackB -> A 发送ack包 进入等待关闭状态
A -> B 再次发送syn数据包B -> A 发送fin包 进入最后确认状态
A -> B 回复ack包 进入超时等待状态 等待结束关闭A B收到ack后立即关闭连接

Socket 通信模型

  • 进行网络通信时,Socket 需要借助数据流来完成数据的传递工作

Socket 网络编程一般可以分成如下步骤进行

0、创建Socket 网络编程模型:客户端/服务器(C/S)

  • 服务器端创建一个 ServerSocket 对象 指定端口号 ServerSocket 负责监听客户端的连接请求。客户端创建一个 Socket 对象 指定服务器的地址和端口号 Socket 负责与服务器建立连接

1、建立连接

  • 服务器端使用 ServerSocketaccept() 方法等待客户端连接。这个方法会阻塞直到一个客户端连接进来

2、打开 Socket 关联的输入输出流

  • 服务器端通过 Socket 对象获取输入输出流(InputStreamOutputStream) 用于接收和发送数据。客户端通过 Socket 对象获取输入输出流 用于与服务器通信

3、数据流中读写信息

  • 使用 InputStreamOutputStream(或者更高层次的 BufferedReaderPrintWriter)进行数据传输 服务器和客户端可以通过这些流进行数据的读取和写入

4、关闭所有的数据流和 Socket

  • 在通信完成后,双方应当关闭流和 socket 连接,以释放资源
// 传递对象信息 序列化
ObjectOutputStream oos = new ObjectOutputStream(...);
oos.writeObject(...);

ObjectInputStream ois = new ObjectInputStream(...);
Object = ois.readObject();

举例

使用 java.net 包来创建一个简单的 TCP 客户端-服务器应用程序。

UserServer 代表服务器端,UserClient 代表客户端。

public class UserServer {
    public static void main(String[] args) {
        // 假设服务器端口号为 8888
        System.out.println("---服务器---");
        try {
            // 构建服务器
            ServerSocket serverSocket = new ServerSocket(8888);
            // 监听客户端发送来的信息 获得socket
            Socket socket = serverSocket.accept();
            // 获得字节流
            InputStream is = socket.getInputStream();

            // 读取数据
            StringBuilder sb = new StringBuilder();
            byte[] buffer = new byte[1024];
            int bytesRead;
            while ((bytesRead = is.read(buffer)) != -1) {
                sb.append(new String(buffer, 0, bytesRead, "UTF-8"));
            }

            // 输出消息
            String msg = sb.toString();
            System.out.println("收到的消息: " + msg);

            // 释放资源
            is.close();
            socket.close();
            serverSocket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
public class UserClient {
    public static void main(String[] args) {
        System.out.println("---客户端---");
        try {
            // 建立 socket 连接 指定服务器 IP + 端口号
            Socket socket = new Socket("localhost",8888);
            // 获得字节流
            OutputStream os = socket.getOutputStream();
            os.write("hello TCP!!!".getBytes());
            // 释放资源
            os.close();
            socket.close();mm
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

TCP 对象传递

public class Student implements Serializable {
    private static final long serialVersionUID = 1L;
    private String name;
    private String age;

    public Student(String name, String age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{name='" + name + "', age='" + age + "'}";
    }
}

public class StudentServer {
    public static void main(String[] args) {
        System.out.println("---服务器---");
        ServerSocket serverSocket = null;
        Socket socket = null;
        InputStream is = null;
        ObjectInputStream ois = null;

        try {
            // 构建服务器
            serverSocket = new ServerSocket(8888);

            // 监听客户端发送来的信息 获得socket
            socket = serverSocket.accept();

            // 获得字节流
            is = socket.getInputStream();
            ois = new ObjectInputStream(is);

            // 读取 Student 对象
            Student student = (Student) ois.readObject();
            System.out.println(student.toString());
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            // 关闭资源
            ......
        }
    }
}

public class StudentClient {
    public static void main(String[] args) {
        System.out.println("---客户端---");
        Socket socket = null;
        ObjectOutputStream oos = null;

        try {
            // 建立 socket 连接 指定服务器 IP + 端口号
            socket = new Socket("127.0.0.1", 8888);
            oos = new ObjectOutputStream(socket.getOutputStream());

            // 创建并发送 Student 对象
            oos.writeObject(new Student("张三", "18"));
            System.out.println("发送成功!");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 关闭资源
            if (oos != null) {
                try {
                    oos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (socket != null) {
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

多线程处理多请求 实现多客户请求

采用多线程的方式;一个专门负责监听的应用主服务程序;一个专门负责处理请求的线程程序。

public class LoginServer {
    public static void main(String[] args) {
        try {
            System.out.println("\n服务端--------------------");
            ServerSocket serverSocket = new ServerSocket(8888);
            Socket socket = null;
            while (true) {
                socket = serverSocket.accept();
                // 创建一个线程来处理登录请求
                new LoginThread(socket).start();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

public class LoginThread extends Thread {
    private Socket socket;

    public LoginThread(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        try {
            InputStream is = socket.getInputStream();
            byte[] b = new byte[50];
            is.read(b);
            String msg = new String(b);
            System.out.println(msg);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            try {
                if (socket != null && !socket.isClosed()) {
                    socket.close();
                }
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }
}

public class LoginClient01 {
    public static void main(String[] args) {
        System.out.println("---客户端01---");
        try {
            Socket socket = new Socket("127.0.0.1", 8888);
            OutputStream os = socket.getOutputStream();
            os.write("用户名:张三,密码:123456".getBytes());
            os.flush(); // 确保数据已经发送出去
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

基于 UDP 协议的 Socket 编程

DatagramSocket:用于发送和接收数据报(UDP数据包)。它提供了用于创建 UDP 套接字并通过其发送和接收数据报的功能。

DatagramPacket:表示要发送或接收的 UDP 数据包。它包含了数据及其目的地地址或来源地址。

基于 UDP 协议的 Socket 网络编程步骤

1、利用 DatagramPacket 对象封装数据包

2、利用 DatagramSocket 发送数据包

3、利用 DatagramSocket 接收数据包

4、利用 DatagramPacket 处理数据包

DatagramSocketsend(DatagramPacket packet) 方法将 DatagramPacket 数据包发送到网络上。DatagramSocketreceive(DatagramPacket packet) 方法从网络中接收数据,并将数据填充到 DatagramPacket 中。

public class UserServer {
    public static void main(String[] args) {
        System.out.println("---我是服务端---");
        DatagramSocket ds = null;
        DatagramPacket dp = null;
        Scanner scanner = new Scanner(System.in);
        try {
            //接收信息
            ds = new DatagramSocket(8888);
            while (true){
                byte info[] = new byte[1024];
                //创建数据包 空包
                dp = new DatagramPacket(info,info.length);
                //监听
                ds.receive(dp);
                //拆包 获得数据
                String msg = new String(dp.getData(), 0, dp.getLength()).trim();
                System.out.println("客户端说:"+msg);
                //发送
                System.out.print("服务端请输入:");
                String content = scanner.next();
                //获得上个数据包的发送地址
                SocketAddress sa = dp.getSocketAddress();
                //封装数据包
                dp = new DatagramPacket(
                        content.getBytes(),
                        content.getBytes().length,
                        sa
                );
                ds.send(dp);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

public class UserClient {
    public static void main(String[] args) {
        //发送信息
        Scanner scanner = new Scanner(System.in);
        System.out.println("---我是客户端---");
        DatagramPacket dp = null;
        DatagramSocket ds = null;
        try {
            while(true){
                System.out.print("客户端请输入:");
                String msg = scanner.next();
                //封装数据包 字节数组  字节数组长度  地址对象InetAddress 端口号
                dp = new DatagramPacket(
                        msg.getBytes() ,
                        msg.getBytes().length,
                        InetAddress.getByName("127.0.0.1"),
                        8888
                );
                //创建发送接收对象
                ds = new DatagramSocket();
                //发送数据包
                ds.send(dp);

                //接收
                byte info[] = new byte[1024];
                //创建数据包 空包
                dp = new DatagramPacket(info,info.length);
                //监听
                ds.receive(dp);
                //拆包 获得数据
                String content = new String(dp.getData(), 0, dp.getLength()).trim();
                System.out.println("服务器说:"+ content);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            ds.close();
        }
    }
}

http://www.kler.cn/news/311800.html

相关文章:

  • Remix 学习 - @remix-run/react 中的主要组件
  • 网络-内核是如何与用户进程交互
  • MySQL从入门到精通
  • MyBatis 数据处理:主键获取、批量删除与动态表名
  • Linux 磁盘清理重新格式化挂载脚本及问题解决
  • flink doris批量sink
  • 我可真厉害,3分钟让你成为AI高手:提示词(prompt)制作及调优(免费教你,别再被割了)
  • 企业EMS -能源管理系统-能源管理系统源码-能源在线监测平台
  • Linux进阶系列(四)——awk、sed、端口管理、crontab
  • 好菜每回味不同——建造者模式
  • GEE教程:对降水数据进行重投影(将10000m分辨率提高到30m)
  • ESP32配网接入Wifi
  • Spring Boot从0到1 -day02
  • 【踩坑】装了显卡,如何让显示器从主板和显卡HDMI都输出
  • QTAndroid编译环境配置
  • Linux基础命令——文件系统的日常管理
  • TaskRes: Task Residual for Tuning Vision-Language Models
  • vue项目中——如何用echarts实现动态水球图
  • 828华为云征文 | 华为云X实例监控与告警管理详解
  • 【Linux入门】基本指令(一)
  • 服务器上PFC配置丢失问题排查与解决方案
  • Python | Leetcode Python题解之第412题Fizz Buzz
  • 简评2024.9.16北京大运河音乐节
  • Prompt最佳实践|指定输出的长度
  • 深度学习自编码器 - 收缩自编码器(CAE)篇
  • 74、Python之函数式编程:深入理解惰性求值与生成器
  • MySql 初次见面
  • Java 基础知识九(网络编程)
  • 二叉树(下)
  • Conda Config修改