Java网络编程(两种聊天室:TCP和UDP)

2023-05-16

网络编程

您的导航

  • 网络编程
    • 网络编程基础知识
      • 一、网络编程三要素
        • IP地址
        • 端口
        • 协议
      • 二、IP地址与InetAddress类
        • IP地址分类
        • InetAddress类
        • 三、端口(Port)与 InetSocketAddress
        • InetSocketAddress
      • 四、通信协议
        • 传输层协议
        • TCP/IP协议族
        • TCP协议
        • UDP协议
    • TCP
      • 一、TCP实现聊天
        • 客户端
        • 服务端
      • 二、TCP实现文件上传
        • 客户端
        • 服务端
    • UDP
      • 一、UDP消息发送
        • 发送端
        • 接收端
      • 二、UDP实现聊天
        • 发送端
        • 接收端
      • 三、多线程UDP聊天程序
        • 发送端
        • 接收端
        • 完成

网络编程基础知识

一、网络编程三要素

  1. IP地址

  2. 端口

  3. 协议

    通信:TCP、UDP协议。(传输层)

IP地址

  • 是网络中设备的唯一标识
  • 分为两大类
    1. IPv4
    2. IPv6

端口

  • 是设备上应用程序的唯一标识。

  • 端口号:用两个字节表示整数,它的取值范围是0~65535。其中,周知端口: 0~1023之间的端口用于一些知名的网络服务和应用。

    注册端口: 普通的应用程序需要使用1024~49151的端口号。如果端口号被另外一个服务或应用所占用,会导致当前程序启动失败。

    **动态端口:**49152~65535,之所以称为动态端口,是因为它一般不固定分配某种进程,而是动态分配。

协议

  • 计算机网络中,连接和通信的规则被称为网络通信协议
  • 传输层的两种协议:
    1. UDP协议
    2. TCP协议

二、IP地址与InetAddress类

  • IP地址可以唯一定位一台网络上的计算机
  • 127.0.0.1表示的是本机localhost

IP地址分类

  • IPv4 / IPv6

    • IPv4

      如 127.0.0.1 ,四个字节组成。每个字节 0~255,一共有42亿个IPv4地址,早已用尽。

    • IPv6

      如 ABCD:EF01:2345:6789:ABCD:EF01:2345:6789,128位,由八个无符号整数组成,号称可以为全世界的每一粒沙子编上一个地址。

InetAddress类

此类表示Internet协议(IP)地址。

通过一些静态方法可以返回一个InetAddress对象代表IP。

// 方法很多,不用记忆,查文档就可以。这个是最常用的
// 获得InetAddress对象
InetAddress ip = InetAddress.getByName("www.baidu.com"); // 虽然是填名字,但是也可以填ip地址比如"127.0.0.1"

三、端口(Port)与 InetSocketAddress

计算机与外界通讯交流的出口

有以下几个特点:

  • 不同的进程有不同的端口号,用来区分软件,单个协议下,端口号不能冲突
  • TCP、UDP 端口号范围为0~65535
  • 端口分类:
    • 公有端口:0~1023
      • HTTP:80
      • HTTPS:443
      • FTP:21
      • SSH:22
      • Telent:23
    • 程序注册端口:1024~49151,分配给用户或程序
      • Tomcat:8080
      • MySQL:3306
      • Oracle:1521
    • 动态、私有端口:49152~65535

可以通过一些命令来查看端口占用情况:

cmd命令:

netstat -ano	#查看所有端口
netstat -ano | findstr "14420"	#查看指定端口
tasklist | findstr "11420"	#查看指定端口的进程

InetSocketAddress

知道了IP地址,我们可以定位一台主机,

知道了端口号,我们可以定位一个进程,

现在知道了IP地址和端口号,我们可以定位某台计算机上的某个进程。

于是,之前InetAddress的代码就可以做一些改进

InetSocketAddress ip = new InetSocketAddress("www.baidu.com", 443);
//第一个参数可以填hostname 也可以填 InetAddress对象,第二个参数填端口号(int类型)。

四、通信协议

协议就是一些规则,通信协议就是关于通信的规则,如果要通信,那么就必须遵守这些规则

传输层协议

最重要的有TCP协议和UDP协议。

TCP/IP协议族

是指能够在多个不同网络间实现信息传输的协议簇。TCP/IP协议不仅仅指的是TCP和IP两个协议,而是指一个由FTP、SMTP、TCP、UDP、IP等协议构成的协议簇, 只是因为在TCP/IP协议中TCP协议和IP协议最具代表性,所以被称为TCP/IP协议。

TCP协议

传输控制协议

  • 客户端和服务端要建立连接,“三次握手,四次挥手”,提供了两台计算机之间可靠无差错的数据传输。可保证传输数据的安全,应用广泛,如上传文件,下载文件,浏览网页。
  • 连接、发送数据都需要确认,且传输完毕后,还需要释放已建立的连接,通信效率较低。

UDP协议

  • 用户数据报协议
  • 客户端和服务端不建立连接,通信效率高,消耗资源小,通常用于音频、视频和普通数据的传输,每次数据包。

TCP

一、TCP实现聊天

因为是C/S架构,所以要有一个客户端和一个服务端。

客户端

public static void main(String[] args) {
    // 首先要有一个目标,向哪一个服务端发送消息
    InetSocketAddress severIP = new InetSocketAddress("127.0.0.1", 8888);
    // 有了目标主机后,就可以建立连接
    Socket socket = new Socket();
    socket.connect(serverIP);
    // 已经连接上了,就可以发送数据,利用IO流
    OutPutStream os = socket.getOutPutSteam();
    PrintStream ps = new PrintStream(os);	// 发文字消息用打印流更方便
    Scanner in = new Scanner(System.in);
    while (true) {
        System.out.println("请输入消息:");
        ps.println(in.nextLine());
        ps.flush();
    }
}

服务端

public static void main(String[] args) {
    // 要能呗客户端所连接,端口号要一致
    ServerSocket server = new ServerSocket(8888);
    // 建立连接
    Socket socket = server.accept();
    // 建立好连接后,就可以接受数据了
    InputStream is = socket.getInPutStream();
    BufferedReader rd = new BufferedRead(new InputStreamReader(is));	// 字符缓冲流读文字消息更方便
	String s;
    while ((s = rd.readLine()) != null) {
        System.out.println(new Date + "\n" + s);
    }
}

二、TCP实现文件上传

文件上传依然是IO流传输数据

消息是一种数据,图片也是一种数据,万物都是数据,所以步骤都是一样的

客户端

public static void main(String[] args) {
    // 1. 建立连接
    Socket socket = new Socket();
    socket.connect(new InetSocketAddress("127.0.0.1", 9957));
    // 2. 传文件 要有文件流
    FileInputStream fos = new FileInputStream("地址");
    // 3. 要有字节流 传输数据给服务端
    BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
    byte[] buffer = new byte[1024];
    int len;
    // 4. 发送文件
    while ((len = fos.read(buffer)) != -1) {
        bos.write(buffer, 0, len);
    }
    // 5. 获取服务端响应
    socket.shutdownOutput();	// 关闭输出管道
    BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
    System.out.println(bufferedReader.readLine());
    // 6. 关闭通道
    fis.close();
    bos.close();
    socket.close();
}

服务端

public static void main(String[] args) {
    // 1. 设置端口,让客户端找到
    ServerSocket server = new ServerSocket(9957);
   	// 2. 监听客户端的连接
    Socket socket = server.accept();
    // 3. 文件输出 要文件流 接受数据 要字节流
    FileOutputStream fos = new FileOutputStream("地址");
    BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
    // 4. 文件输出
    byte[] buffer = new byte[1024];
    int len;
    while ((len = bis.read(buffer)) != -1) {
        fos.write(buffer, 0, len);
    }
    // 5. 给予客户端回应
    PrintStream ps = new PrintStream(socket.getOutputStream());
    ps.println("接收完毕!");
    //关闭通道
    fos.close();
    bis.close();
    socket.close();
}

UDP

UDP不需要连接,知道了目标的地址就可以发消息。

一、UDP消息发送

发送端

  1. 创建发送端的Socket对象 DatagramSocket
  2. 创建数据,并将数据打包
  3. 调用DatagramSocket对象的方法发送数据
  4. 关闭发送端
public class Client {
    public static void main(String[] args) throws IOException {
        // 1. 建立socket连接
        DatagramSocket datagramSocket = new DatagramSocket();
        // 2. 创建数据
        String s = "hello udp";
        DatagramPacket packet = new DatagramPacket(s.getBytes(), 0, s.getBytes().length, InetAddress.getByName("127.0.0.1"), 9957);
        // 3. 调用方法发送
        datagramSocket.send(packet);
        //4. 关闭资源
        datagramSocket.close();
    }
}

接收端

  1. 创建接收端的Socket对象 DatagramSocket
  2. 创建一个数据包,用于接收数据
  3. 调用DatagramSocket对象的方法接收数据
  4. 解析数据包,并把数据再控制台显示
  5. 关闭接收端
public static void main(String[] args) throws IOException {
    // 1. 建立socket连接
    DatagramSocket datagramSocket = new DatagramSocket(9957);
    // 2. 创建数据包
    byte[] buffered = new byte[1024];
    DatagramPacket packet = new DatagramPacket(buffered, 0, buffered.length);
    // 3. 调用方法接受数据
    datagramSocket.receive(packet);
    // 4. 解析数据包
    System.out.println(packet.getAddress());
    System.out.println(new String(packet.getData(),0, packet.getLength()));
    // 5. 关闭资源
    datagramSocket.close();
}

二、UDP实现聊天

只需要把上面的代码稍微改一下即可。

循环输入,以及持久监听。

发送端

public static void main(String[] args) throws IOException {
    DatagramSocket socket = new DatagramSocket();
    DatagramPacket packet;
    Scanner in = new Scanner(System.in);
    String s;
    do {
        System.out.print("Say:");
        s = in.nextLine();
        packet = new DatagramPacket(s.getBytes(), 0, s.getBytes().length, new InetSocketAddress("127.0.0.1", 9957));
        socket.send(packet);
    } while (s.compareTo("exit") != 0);
    socket.close();
}

接收端

public static void main(String[] args) throws IOException {
    DatagramSocket socket = new DatagramSocket(9957);

    DatagramPacket packet;
    byte[] bufferd = new byte[1024];
    while (true) {
        packet = new DatagramPacket(bufferd, 0, bufferd.length);
        socket.receive(packet);
        System.out.println(new String(packet.getData(), 0, packet.getLength()));
    }
}

三、多线程UDP聊天程序

发数据需要一个发送端,收数据需要一个接收端

于是先把接收端和发送端的类。

发送端

// 因为需要多线程,所以需要实现Runnable接口
public class TalkSend implements Runnable {
   	// UDP发送数据必要的socket连接以及packet和一个字符缓冲流读取消息
    private DatagramSocket socket;
    private DatagramPacket packet;
    private BufferedReader reader;
    // 设置目标 IP,port以及自己的用户名
    private String toIP;
    private int toPort;
    private String name;
    
    // 初始化
    public TalkSend(String toIP, int toPort, String name) {
        this.toIP = toIP;
        this.toPort = toPort;
        this.name = name;
        try {
            reader = new BufferedReader(new InputStreamReader(System.in));
            socket = new DatagramSocket();
            socket.connect(new InetSocketAddress(toIP, toPort));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    // 多线程run方法
    @Override
    public void run() {
        // 死循环持续读取键盘输入流,持续发送
        String s;
        while (true) {
            try {
                s = name + ": " + reader.readLine();
                packet = new DatagramPacket(s.getBytes(), 0, s.getBytes().length);
                socket.send(packet);
                if (s.endsWith(": bye")) {
                    break;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        socket.close();
    }
}

接收端

public class TalkReceive implements Runnable {
   // UDP接收数据需要socket连接和packet
    private DatagramSocket socket;
    private DatagramPacket packet;
    // 接收的端口
    private int myPort;
    
    //初始化
    public TalkReceive(int myPort) {
        this.myPort = myPort;
        try {
            socket = new DatagramSocket(myPort);
        } catch (Exception e){
            e.printStackTrace();
        }
    }
    
    // 多线程run方法
    @Override
    public void run() {
        // 死循环持续监听消息
        String s;
        byte[] buffered = new byte[1024];
        while (true) {
            try {
                packet = new DatagramPacket(buffered, 0, buffered.length);
                socket.receive(packet);
            } catch(Exception e) {
                e.printStackTrace();
            }
            s = new String(packet.getData(), 0, packet.getLength());
            System.out.println(s);
            if (s.endsWith(": bye")) {
                break;
            }
        }
    }
}

完成

有了发送端和接收端,我们就可以把他们都丢进线程池中,这样我们就可以和另一台主机通信了。

public class Demo {
    public static void main(String[] args) {
        ExecutorService pool = new ThreadPoolExecutor(4, 8, 2, TimeUnit.MINUTES, new ArrayBlockingQueue<>(4), Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy());
        Scanner in = new Scanner(System.in);
        System.out.println("设置接收端口:");
        int port = in.nextInt();
        System.out.println("目标IP:");
        String toIP = in.next();
        System.out.println("目标端口:");
        int toPort = in.nextInt();
        System.out.println("您的昵称:");
        String name = in.next();
        pool.execute(new TalkReceive(port));
        pool.execute(new TalkSend(toIP, toPort, name));
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java网络编程(两种聊天室:TCP和UDP) 的相关文章

  • STM32 串口

    文章目录 USART 通信协议RS 232与TTL电平 串口通信数据包组成USART功能框图讲解引脚寄存器状态寄存器 USART SR 数据寄存器 USART DR 控制寄存器 USART CR 波特比率寄存器 USART BRR 发送过程
  • printf二进制数据

    基于之前这篇文章的代码改进了下 xff1a http blog csdn net xzongyuan article details 28889063 之前打印的数字没有补0 我打印了内存信息 xff0c 结果是这样的 xff0c 不能对齐
  • 分析MySQL数据类型的长度

    分析MySQL数据类型的长度 MySQL有几种数据类型可以限制类型的 34 长度 34 xff0c 有CHAR Length VARCHAR Length TINYINT Length SMALLINT Length MEDIUMINT L
  • 为vscode配置clangd

    目录 安装clangd 后端安装clangd 前端修改基础配置生成compile commands json文件基本效果补全warning提醒自动修改存在问题 注意事项 clangd能提供更好的补全和提示 xff0c 自带检查一些warni
  • 论文笔记(十九)RGB-D Object Tracking: A Particle Filter Approach on GPU

    RGB D Object Tracking A Particle Filter Approach on GPU 文章概括摘要1 介绍2 贡献3 粒子滤波器4 可能性评估5 实施细节6 实验A 物体模型B 合成序列C 真实序列 7 结论8 鸣
  • Ubuntu 命令行 访问网页

    安装w3m 1 进入 root apt get install w3m 2 测试是否成功 xff1a w3m https blog csdn net x xx xxx xxxx article details 92574331
  • 代码管理中Trunk、Branches、Tags的区别和联系

    我们可以将这三者想象成一棵树的组成部分 trunk为树干branches为树枝tags为整棵树 trunk用于主线开发 branches用于定制版本 修复bugs 并行开发等使用 tags用于存放release版本 xff0c 阶段性代码
  • linux使用curl请求(带参数)

    1 2 3 curl G d 34 c 61 amp a 61 34 http www net index php
  • 惯导系列(二):滤波相关的算法

    前言 我又消失了一段时间 xff0c 这段时间研究了惯性导航有关的算法 xff0c 整理了不少博客 xff0c 字数比较多 xff0c 图片比较多 学到了很多知识 目录 前言 本节介绍 一 Mahony算法 1 1 PID控制算法 1 2
  • STM32 CAN 设置多个过滤器接收多ID方法

    1 标识符列表模式 xff0c 32位模式下 void MX CAN Init void 这里是实现了两个地址的接收 一个是用来接收广播信息 一个用来接收私有地址 如果想实现多个地址可以添加多个过滤器组 stm32103 有0 13 共14
  • linux下运行动态库问题 cannot open shared object file: No such file or directory

    如果动态库不在同一级目录下 xff0c 则需要将以上文件的目录加载到动态库搜索路径中 xff0c 设置的方式有以下几种 一 将动态库路径加入到LD LIBRARY PATH环境变量 1 在终端输入 xff1a export LD LIBRA
  • 几个串口通信协议的整理

    一 UART UART是一个大家族 xff0c 其包括了RS232 RS499 RS423 RS422和RS485等接口标准规范和总线标准规范 它们的主要区别在于其各自的电平范围不相同 嵌入式设备中常常使用到的是TTL TTL转RS232的
  • 单片机中断的过程

    1 根据响应的中断源的中断优先级 使相应的优先级状态触发器置1 xff1b 2 把当前程序计数器PC的内容压入堆栈 xff0c 保护断点 xff0c 寻找中断源 xff1b 3 执行硬件中断服务子程序调用 xff1b 4 清除相应的中断请求
  • Ruby学习札记(3)- Ruby中gem的安装与卸载

    Ruby 学习札记 3 Ruby 中 gem 的安装与卸载 在 Ruby 中有 gem 包这种概念 xff0c 类似 PHP 中的 pear xff0c 相当于一种插件 具体可以 Google 一下 xff08 1 xff09 查看已经安装
  • 【linux】ubuntu20.04 运行软件 提示找不到过时的库 libQtCore.so.4、libQtGui.so.4、libpng12.so.0

    先上结果 1 nxView运行起来 环境 硬件 xff1a Jetson Xavier NX 套件 系统 xff1a Ubuntu 20 04 软件 xff1a nxView 43 libQtCore so 4 解决 0 现象 运行软件提示
  • rtt相关问题总结

    1 总结RT Thread的启动流程 xff08 启动文件部分跳过 xff09 关中断 rt hw interrupt disable 板级初始化 xff1a 需在该函数内部进行系统堆的初始化 rt hw board init 打印 RT
  • FTP 客户端C实现

    使用 Socket 通信实现 FTP 客户端程序 FTP 概述 文件传输协议 xff08 FTP xff09 作为网络共享文件的传输协议 xff0c 在网络应用软件中具有广泛的应用 FTP的目标是提高文件的共享性和可靠高效地传送数据 在传输
  • Qt编写串口通信程序全程图文讲解

    说明 我们的编程环境是windows xp下 xff0c 在Qt Creator中进行 xff0c 如果在Linux下或直接用源码编写 xff0c 程序稍有不同 xff0c 请自己改动 在Qt中并没有特定的串口控制类 xff0c 现在大部分
  • VLC播放器调试经验总结

    一 前言 在使用VS学习VLC源码时 xff0c 可以打断点分析变量数据 xff0c 跟踪代码流程 xff0c 方便我们理解源码 但是在定位音视频卡顿 延时等疑难问题时 xff0c 这一招就不管用了 xff0c 因为打上断点就会导致实时计算
  • http协议如何解决粘包问题

    在讲粘包问题之前 xff0c 首先得明白这个包是应用层的数据包 当数据在传输层时 xff0c 由于TCP是面向字节流的 xff0c 所以它看到的数据是按照顺序一个个放在缓冲区中的 xff0c 而对于应用层而言 xff0c 看到的只是一连串的

随机推荐