UDP Socket低层次网络编程

简介: UDP(用户数据报协议)就像日常生活中的邮件投递,是不能保证可靠地寄到目的地。UDP是无连接 的,对系统资源的要求较少,UDP可能丢包不保证数据顺序。但是对于网络游戏和在线视频等要求传 输快、实时性高、质量可稍差一点的数据传输,UDP还是非常不错的。UDP Socket网络编程比TCP Socket编程简单多,UDP是无连接协议,不需要像TCP一样监听端口,建 立连接,然后才能进行通信。

一、DatagramSocket类


java.net包中提供了两个类:DatagramSocket和DatagramPacket用来支持UDP通信。这里先介绍一下DatagramSocket类,DatagramSocket用于在程序之间建立传送数据报的通信连接。

DatagramSocket常用的构造方法:


DatagramSocket():创建数据报DatagramSocket对象,并将其绑定到本地主机上任何可用的端 口。

DatagramSocket(int port):创建数据报DatagramSocket对象,并将其绑定到本地主机上的指定端 口。

DatagramSocket(int port, InetAddress laddr):创建数据报DatagramSocket对象,并将其绑定到指定 的本地地址。

DatagramSocket其他的常用方法有:


void send(DatagramPacket p):从发送数据报包。

void receive(DatagramPacket p):接收数据报包。

int getPort():返回DatagramSocket连接到的远程端口。

int getLocalPort():返回DatagramSocket绑定到的本地端口。

InetAddress getInetAddress():返回DatagramSocket连接的地址。

InetAddress getLocalAddress():返回DatagramSocket绑定的本地地址。

boolean isClosed():返回DatagramSocket是否处于关闭状态。

boolean isConnected():返回DatagramSocket是否处于连接状态。

void close():关闭DatagramSocket。

DatagramSocket也实现了AutoCloseable接口,通过自动资源管理技术关闭DatagramSocket。


二、DatagramPacket类


DatagramPacket用来表示数据报包,是数据传输的载体。DatagramPacket实现无连接数据包投递服务, 每投递数据包仅根据该包中信息从一台机器路由到另一台机器。从一台机器发送到另一台机器的多个 包可能选择不同的路由,也可能按不同的顺序到达,不保证包都能到达目的。

DatagramPacket的构造方法:


DatagramPacket(byte[] buf, int length):构造数据报包,buf包数据,length是接收包数据的长度。

DatagramPacket(byte[] buf, int length, InetAddress address, int port):构造数据报包,包发送到指定主机上的指定端口号。

DatagramPacket(byte[] buf, int offset, int length):构造数据报包,offset是buf字节数组的偏移量。

DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port):构造数据报包,包发 送到指定主机上的指定端口号。

DatagramPacket常用的方法:


InetAddress getAddress():返回发往或接收该数据报包相关的主机的IP地址。

byte[] getData():返回数据报包中的数据。

int getLength():返回发送或接收到的数据(byte[])的长度。

int getOffset():返回发送或接收到的数据(byte[])的偏移量。

int getPort():返回发往或接收该数据报包相关的主机的端口号。


三、案例:文件上传工具


服务器端UploadServer代码如下:

import java.io.*;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
/**
 * @author : 蔡政洁
 * @email :caizhengjie888@icloud.com
 * @date : 2020/2/20
 * @time : 3:56 下午
 */
//服务器端
public class UploadServer {
    public static void main(String[] args) {
        System.out.println("服务器端运行。。。。。");
//        创建一个子线程
        Thread thread = new Thread(() ->{
            try (
//                    创建DatagrameSocket对象,指定端口8080
                    DatagramSocket socket = new DatagramSocket(8080);
//                    创建文件输出流,并创建缓冲输出流
                    FileOutputStream fout = new FileOutputStream("/Users/caizhengjie/Desktop/qq/e/56.txt");
                    BufferedOutputStream out = new BufferedOutputStream(fout);
                    ){
//                准备一个缓冲区
                byte[] buffer = new byte[1024];
//                循环接受数据报包
                while (true){
//                    创建数据报包对象,用来接收数据
                    DatagramPacket packet = new DatagramPacket(buffer,buffer.length);
//                    接收数据包
                    socket.receive(packet);
//                    接收数据长度
                    int len = packet.getLength();
                    if(len == 3){
//                        获得结束标志
                        String flag = new String(buffer,0,3);
//                        判断结束标志,如果是bye,则结束接收
                        if (flag.equals("bye")){
                            break;
                        }
                    }
//                    写入数据
                    out.write(buffer,0,3);
                }
                System.out.println("接收完成!");
            } catch (SocketException | FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        });
//        启动线程
        thread.start();
    }
}

创建一个子线程,因为socket.receive(packet)方法会阻塞主线程了。

客户端UploadClient代码如下:

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
/**
 * @author : 蔡政洁
 * @email :caizhengjie888@icloud.com
 * @date : 2020/2/20
 * @time : 4:31 下午
 */
//客户端
public class UploadCilent {
    public static void main(String[] args) {
        System.out.println("客户端运行。。。");
        try(
                //        创建DatagrameSocket对象,由系统分配可以使用的端口
                DatagramSocket socket = new DatagramSocket();
                FileInputStream fin = new FileInputStream("/Users/caizhengjie/Desktop/qq/123.txt");
                BufferedInputStream in = new BufferedInputStream(fin);
                ){
//            创建远程主机IP地址对象
            InetAddress address = InetAddress.getByName("localhost");
//            准备一个缓冲区
            byte[] buffer = new byte[1024];
//            首次从文件流读数据
            int len = in.read(buffer);
            while (len != -1){
//                创建数据报包对象
                DatagramPacket packet = new DatagramPacket(buffer,len,address,8080);
//                发送数据报包
                socket.send(packet);
//                再次从文件流中读取数据
                len = in.read(buffer);
            }
//                创建数据报包对象
            DatagramPacket packet = new DatagramPacket("bye".getBytes(),3,address,8080);
//            发送结束标志
            socket.send(packet);
            System.out.println("上传完成");
        } catch (SocketException | FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

上述是上传文件客户端,发送数据不会堵塞线程,因此没有使用子线程。


四、案例:聊天工具


服务器端ChatServer代码如下:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
/**
 * @author : 蔡政洁
 * @email :caizhengjie888@icloud.com
 * @date : 2020/2/20
 * @time : 7:57 下午
 */
//服务器端
public class ChatServer {
    public static void main(String[] args) {
        System.out.println("服务器端运行。。。。");
//        创建一个子线程
        Thread thread = new Thread(() ->{
            try (
//                    创建DatagrameSocket对象,指定端口8080
                    DatagramSocket socket = new DatagramSocket(8080);
                    BufferedReader keyboardIn = new BufferedReader(new InputStreamReader(System.in));
                    ){
                while (true){
//                    接收数据报
//                    准备一个缓冲区
                    byte[] buffer = new byte[1024];
                    DatagramPacket packet = new DatagramPacket(buffer,buffer.length);
                    socket.receive(packet);
//                    接收数据长度
                    int len = packet.getLength();
                    String str = new String(buffer,0,len);
//                    打印接收的数据
                    System.out.printf("从客户端接收的数据:【%s】\n",str);
//                    发送数据
//                    从客户端传来的数据包中得到客户端的地址
                    InetAddress address = packet.getAddress();
//                    从客户端传来的数据包中得到客户端端口号
                    int port = packet.getPort();
//                    读取键盘输入的字符串
                    String keyboardInputString = keyboardIn.readLine();
//                    读取键盘的字节数组
                    byte[] b = keyboardInputString.getBytes();
//                    创建 DatagramPacket对象,用于客户端的发送数据
                    packet = new DatagramPacket(b,b.length,address,port);
//                    向客户端发送数据
                    socket.send(packet);
                }
            } catch (SocketException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        });
//        启动线程
        thread.start();
    }
}

创建一个子线程,因为socket.receive(packet)方法会阻塞主线程了。

客户端ChatClient代码如下:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
/**
 * @author : 蔡政洁
 * @email :caizhengjie888@icloud.com
 * @date : 2020/2/20
 * @time : 8:25 下午
 */
//客户端
public class ChatClient {
    public static void main(String[] args) {
        System.out.println("客户端运行。。。。");
//        创建一个子线程
        Thread thread = new Thread(() ->{
            try (
//                    创建DatagrameSocket对象,由系统自动分配端口
                    DatagramSocket socket = new DatagramSocket();
                    BufferedReader keyboardIn = new BufferedReader(new InputStreamReader(System.in));
            ){
                while (true){
//              发送数据
//                    准备一个缓冲区
                    byte[] buffer = new byte[128];
//                    服务器IP地址
                    InetAddress address = InetAddress.getByName("localhost");
//                    服务器端口号
                    int port = 8080;
//                    读取键盘输入的端口号
                    String keyboardInputString = keyboardIn.readLine();
//                    退出循环,结束线程
                    if (keyboardInputString.equals("bye")){
                        break;
                    }
//                    读取键盘输入的字节数组
                    byte[] b = keyboardInputString.getBytes();
                    //                    创建 DatagramPacket对象,用于客户端的发送数据
                     DatagramPacket packet = new DatagramPacket(b,b.length,address,port);
//                     发送
                    socket.send(packet);
//                    接收数据报
                    packet = new DatagramPacket(buffer,buffer.length);
                    socket.receive(packet);
//                    接受数据长度
                    int len = packet.getLength();
                    String str  = new String(buffer,0,len);
//                    打印接收数据
                    System.out.printf("从客户端接收的数据:【%s】\n",str);
                }
            } catch (SocketException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        });
//        启动线程
        thread.start();
    }
}

注意的是ChatClient可 以通过键盘输入bye,退出循环结束线程。

先运行服务器端,再运行客户端

运行结果:


1.png2.png

相关文章
|
1月前
|
机器学习/深度学习 人工智能
类人神经网络再进一步!DeepMind最新50页论文提出AligNet框架:用层次化视觉概念对齐人类
【10月更文挑战第18天】这篇论文提出了一种名为AligNet的框架,旨在通过将人类知识注入神经网络来解决其与人类认知的不匹配问题。AligNet通过训练教师模型模仿人类判断,并将人类化的结构和知识转移至预训练的视觉模型中,从而提高模型在多种任务上的泛化能力和稳健性。实验结果表明,人类对齐的模型在相似性任务和出分布情况下表现更佳。
60 3
|
30天前
|
Web App开发 缓存 网络协议
不为人知的网络编程(十八):UDP比TCP高效?还真不一定!
熟悉网络编程的(尤其搞实时音视频聊天技术的)同学们都有个约定俗成的主观论调,一提起UDP和TCP,马上想到的是UDP没有TCP可靠,但UDP肯定比TCP高效。说到UDP比TCP高效,理由是什么呢?事实真是这样吗?跟着本文咱们一探究竟!
53 10
|
27天前
|
Kubernetes 网络协议 Python
Python网络编程:从Socket到Web应用
在信息时代,网络编程是软件开发的重要组成部分。Python作为多用途编程语言,提供了从Socket编程到Web应用开发的强大支持。本文将从基础的Socket编程入手,逐步深入到复杂的Web应用开发,涵盖Flask、Django等框架的应用,以及异步Web编程和微服务架构。通过本文,读者将全面了解Python在网络编程领域的应用。
24 1
|
30天前
|
Java
[Java]Socket套接字(网络编程入门)
本文介绍了基于Java Socket实现的一对一和多对多聊天模式。一对一模式通过Server和Client类实现简单的消息收发;多对多模式则通过Server类维护客户端集合,并使用多线程实现实时消息广播。文章旨在帮助读者理解Socket的基本原理和应用。
23 1
|
1月前
|
消息中间件 监控 网络协议
Python中的Socket魔法:如何利用socket模块构建强大的网络通信
本文介绍了Python的`socket`模块,讲解了其基本概念、语法和使用方法。通过简单的TCP服务器和客户端示例,展示了如何创建、绑定、监听、接受连接及发送/接收数据。进一步探讨了多用户聊天室的实现,并介绍了非阻塞IO和多路复用技术以提高并发处理能力。最后,讨论了`socket`模块在现代网络编程中的应用及其与其他通信方式的关系。
101 3
|
1月前
|
网络协议 Linux 应用服务中间件
Socket通信之网络协议基本原理
【10月更文挑战第10天】网络协议定义了机器间通信的标准格式,确保信息准确无损地传输。主要分为两种模型:OSI七层模型与TCP/IP模型。
|
1月前
|
网络协议 Linux 网络性能优化
Linux基础-socket详解、TCP/UDP
综上所述,Linux下的Socket编程是网络通信的重要组成部分,通过灵活运用TCP和UDP协议,开发者能够构建出满足不同需求的网络应用程序。掌握这些基础知识,是进行更复杂网络编程任务的基石。
124 1
|
2月前
|
监控 网络协议 网络性能优化
如何办理支持UDP协议的网络
在当今网络环境中,UDP(用户数据报协议)因传输速度快、延迟低而广泛应用于在线游戏、视频流媒体、VoIP等实时服务。本文详细介绍了办理支持UDP协议网络的方法,包括了解UDP应用场景、选择合适的ISP及网络套餐、购买支持UDP的设备并进行优化设置,以及解决常见问题的策略,帮助用户确保网络稳定性和速度满足实际需求。
|
2月前
|
网络协议 开发者 Python
网络编程小白秒变大咖!Python Socket基础与进阶教程,轻松上手无压力!
在网络技术飞速发展的今天,掌握网络编程已成为开发者的重要技能。本文以Python为工具,带你从Socket编程基础逐步深入至进阶领域。首先介绍Socket的概念及TCP/UDP协议,接着演示如何用Python创建、绑定、监听Socket,实现数据收发;最后通过构建简单的聊天服务器,巩固所学知识。让初学者也能迅速上手,成为网络编程高手。
76 1
|
1月前
|
网络协议 测试技术 网络安全
Python编程-Socket网络编程
Python编程-Socket网络编程
下一篇
无影云桌面