网络编程之:UDP从初识到使用

一.前(正)言(文):

没有前言,直接上正文!(大雾)

上一章了解完网络通信的基本原理及过程后,这一章就来实际的进入到我们程序员的回合吧!在上一章中我们曾说过,对于网络通信模型而言,真正需要程序员去操作的,其实就是应用层,而想要真正发送数据,仅仅有应用层程序也肯定是远远不够的,在网络通信模型中,上层协议和下层协议之间是需要有一个交互过程的,转变成应用层数据报的数据并不是直接就扔给传输层的,而是要通过上层协议调用下层协议来完成传递的,这就需要使用到传输层协议为应用层专门提供的一组api——socket api。而在这么多种api中系统主要提供的是基于UDP的api和基于TCP的api,两个协议之间有着本质的区别,故而它们的api也有着很大的区别。那么本章介绍的就是UDP协议。

二.UDP协议的特点(概述):

1.无连接——什么叫无连接呢?此处的无连接并非指我们现实意义的连接,而是指,通信双方记录着彼此的信息。举个栗子,你和你的女票结婚了,你的女票的结婚证上记录着他的老公是你,你的结婚证上也记录着你的老婆是她,这就是我们在此处所指的建立连接,而UDP协议并不具备这种特性。

2.不可靠传输——什么是不可靠传输呢?在明确这个概念前,我们先来明确什么是可靠传输。按照正常人的逻辑来讲,可靠传输也许就是指百分百能传达给对方的传输,但是在程序员的世界里并非如此,可靠传输绝不是指百分百能成功的传输,而是指会尽力去确保传输成功,但是不论传输成功或是失败,你这里都能感知到,而不可靠传输则不会管那么多,他只管往外发,他并不在意接收方是否有接收到,也不在意传输是否成功,就算失败了,你也感知不到。

3.面向数据报——UDP传输数据是以UDP数据报为基本单位的,具体含义和形式在后面详解网络原理是会详细总结。

4.全双工——全双工就是指UDP协议不仅可以用来接收数据还可以用来发送数据,两者可以同时产生。

三.UDP的api:

UDP的api主要为我们提供了两个类,分别是DatagramSocket()和DatagramPacket(),对于后者其实我们已经了解过了,在上一章中,我们曾提到过数据报的由来,它其实就是由载荷和报头所构成的,而这个DatagramPacket()就是一个UDP数据报对象,那么,DatagramSocket()指的又是什么呢?这里注意Socket,其实它就表明了DatagramSocket()就是一个socket对象,那么什么是socket对象呢?它其实是网卡中的一个特殊的文件——socket文件(这里的文件是一个广义的概念,和平时提到的不一样)。网络通信需要有一个socket对象,基于对socket对象的控制来控制网卡进行数据通信(因为直接控制网卡太难)。所以,我们不论是读数据还是写数据其实本质上都是让网卡接收和发送数据。

对于DatagramSocket()有两个版本,分别是带参数和不带参数的,带参数的一般用于服务器,不带参数的一般用于客户端,区别就在于参数的含义——这里的参数就是端口号。服务器是需要指明端口号的,因为它要明确它在当前主机中所处的位置,而客户端则不需要,只要让系统自动去分配就好了(举例环节:服务器就是一家饭店,端口号就表明了这家饭店在那个堂口,是必须明确的,而客户端就是座位号,是随时可能改变的)。

DatagramSocket()是用来对网卡进行控制的,控制它的传输和接收,那么,它要传输和接收的内容又是谁呢?正是我们的DatagramPacket()。前面提到,DatagramPacket()是一个数据报对象,而数据报也就是我们在进行网络通信时所传递的数据,那么,DatagramPacket()又如何使用呢,这也是我们必须要掌握的。

在DatagramPacket()中包含两个构造方法

对于第一个方法,它所接收的参数中并不带有地址信息,所以它一般是接收信息时使用,对于第二个方法来说,则是需要传入地址信息的,一般是发送数据时使用。

有了以上了解,其实就可以动手写一个最简单的基于UDP的客户端服务器的程序了。

四.UDP初试之回显程序:

1.服务器实现:

首先,我们既然要写一个UDP的客户端服务器程序,就必然需要一个socket对象进行接收和发送等工作。其次,我们还需要一个packet对象来保存待发送(或接收到的)的数据,明确了以上两点之后就可以动手尝试了,先搭出一个大体的结构。

第一点一定是创建出一个UDP服务器类,并在类中定义一个socket对象,这个socket对象就是用来发送和接收数据的(本质上是在控制网卡进行接收和发送操作)。

第二点,我们既然创建类了一个服务器类,就需要有一个构造方法,同时我们还要考虑到,前面我们在介绍DatagramSocket()时提到过,DatagramSocket()有两个版本,一个是带参数的,一个是不带参数的,带参数的版本我们一般用在服务器端上,不带的则是用在客户端上,此处我们要写的是一个服务器端,所以需要带参数的版本也就是需要我们指定服务器的的端口号。这个端口号可以在服务器类的构造方法中传给socket,也可以直接作为成员实例化传入,此处我选择的方法是在服务器类的构造方法中完成socket的实例化,这样一来,我们就需要自己去写一个构造方法了,在参数列表中添加一个参数即可。

紧接着,有了以上准备,就到了真正的服务器逻辑实现的环节了!

第三点,实现服务器逻辑(这里我们可以写一个方法对这串逻辑代码进行封装,方便以后使用),既然要写一个服务器逻辑,那就要先弄清楚这个服务器是干嘛的,此处我们要实现的是一个回显服务器,其功能就是客户端发送一个数据,经过服务器接收后再返还给客户端,所以此处我们的服务器涉及到的功能就是一个接收和发送的功能,所以,我们要先准备一个packet对象来接收客户端的请求,接收请求,我们使用无需地址参数的版本即可,有了packet对象,接下来要做的就是将客户端发来的信息存储到packet对象中,这里我们需要使用到socket的接收方法,也就是receive(packet)方法。这里值得注意的是,receive方法是一个输出型参数的方法,意思就是说,它可以直接将socket接收到的信息(其实实际上是网卡接收到的信息)写入到packet中,接下来我们就可以对获取到的信息进行解析,由于是一个回显服务器,所以我们默认解析内容都是文本内容直接用String类型的变量接收即可。完成接收的操作后,再将接收到的信息存储到一个新的packet对象中进行发送即可完成回显服务器的逻辑实现,不过要怎样存储到一个新的packet中呢?又要怎样发送呢?随便发送都能成功吗?当然不是的,这时就要用到packet的另一个版本了——带地址参数的版本,我们要指定好接收方的IP地址和端口号,不过由于这里是回显服务器,所以只要使用一个方法即可——packet.getSocketAddress()。这个方法中自带了发送方的IP和端口号,这样一来,记录有地址信息和待发送数据的packet对象就完成了,只需要调用socket的send方法发送出去即可。代码展示如下:

    private DatagramSocket socket = null;
    public UDP_server(int port) throws SocketException {
        socket = new DatagramSocket(port);
    }
    public void start() throws IOException {
        while(true) {
            //接收客户端信息
            DatagramPacket packet = new DatagramPacket(new byte[4096], 4096);
            socket.receive(packet);//将客户端信息接收并写入packet数据报中,此时packet数据报记录了客户端的请求内容和ip地址以及端口号
            //将请求内容解析为字符串,注意,由于是解析为字符串,所以,这里必须是客户端的请求内容本身就是文本内容,否则会出现问题
            String str = new String(packet.getData(), 0, packet.getLength());
            //将待反馈的结果封装成一个数据报,其中包含要反馈的内容和接收方(客户端)的IP地址和端口号
            DatagramPacket str_packet = new DatagramPacket(str.getBytes(), str.getBytes().length, packet.getSocketAddress());
            //传回给客户端
            socket.send(str_packet);
            System.out.print(str);
        }
    }

这里还需要注意一个问题,在封装成一个待发送数据报的时候,参数不能直接传入String类型的对象,因为packet构造方法的第一个参数的类型是byte[],而不是String,所以后续传入数据长度时也不能直接使用String变量. length,而是要用第一个参数的数据长度。

2.客户端实现:

只有一个服务器还是不够的,我们还需要一个客户端来进行配合。其实两者间的差异并不大,我们来进行对比实现。

第一点,对于客户端而言,它也同样需要一个socket对象进行接收和发送,也需要一个packet对象作为发送和接收信息的载体,只不过,与服务器相较不同的是,客户端不需要我们手动指定端口号了,但是它需要获取到服务器的端口号和IP地址,这样在后续发送的时候才能准确发送到我们的服务器上。

第二点,客户端的请求数据是直接在用户输入框中读取的,也就是我们的控制台,所以,客户端要做的第一件事就是直接读取我们输入的信息,然后再将它封装成一个记录着服务器IP地址和端口号的packet对象,并发送即可。这个过程和服务器发送数据的过程完全一致,包括接下来,客户端接收到服务器返回的响应结果,这个接收的过程也是和服务器的一样的,最后将返回的响应结果解析成一个文本内容并打印就好了。代码展示如下:

    private DatagramSocket socket = null;
    public String s_IP = null;
    public int s_port = 0;
    public UDP_client(String IP, int port) throws SocketException {
        s_IP = IP;
        s_port = port;
        socket = new DatagramSocket();
    }
    public void start() throws IOException {
        Scanner scanner = new Scanner(System.in);
        while(true) {
            String require =  scanner.next();
            DatagramPacket c_packet = new DatagramPacket(require.getBytes(), require.getBytes().length, InetAddress.getByName(s_IP), s_port);
            socket.send(c_packet);
            DatagramPacket cs_packet = new DatagramPacket(new byte[4096], 4096);
            socket.receive(cs_packet);
            String result = new String(cs_packet.getData(), 0, cs_packet.getLength());
            System.out.println(result);
        }
    }

观察此处的代码,你就会发现,客户端和服务器的代码大体基本一致,两者只要搞清楚其中的数据传输方式和原理即可。

五.总结:

也没什么可总结的其实,主要的地方还是在于掌握UDP的api,不过,在实际开发中是基本不用UDP协议的,因为它的无连接和不可靠传输,再加上又是面向数据报的在接收数据时并不灵活,远不如TCP协议,所以有个大致的了解就行。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: UDP套接字编程是一种基于UDP协议的网络编程技术。UDP是一种无连接的协议,它不保证数据传输的可靠性和顺序性,但是具有传输速度快、实时性好等优点。在Python中,可以使用socket模块来实现UDP套接字编程,通过创建UDP套接字、绑定端口、发送和接收数据等操作,实现网络通信。UDP套接字编程常用于实时性要求较高的应用场景,如音视频传输、游戏等。 ### 回答2: UDP是一种无连接的协议,在网络编程中它被广泛应用于实时通讯和游戏等领域。Python语言提供了相应的库支持,使得开发者可以利用UDP套接字来实现高性能的数据传输。 UDP套接字编程主要包括创建UDP套接字、绑定端口、发送数据、接收数据和关闭套接字等步骤。 首先,我们需要在Python中创建UDP套接字,可以使用socket库下的socket()函数。该函数可以接收两个参数,第一个参数指定了IP地址族,通常采用AF_INET表示IPv4地址族,第二个参数指定了套接字类型,可以使用SOCK_DGRAM表示UDP套接字。 ``` import socket udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) ``` 接着,我们需要将套接字绑定到指定的端口上。可以使用bind()函数来完成该操作。bind()函数接收一个元组类型的参数,第一个参数指定了要绑定的IP地址,通常设置为本机IP地址。第二个参数指定了端口号。 ``` udp_socket.bind(('127.0.0.1', 8888)) ``` 数据的发送和接收都需要指定对方的IP地址和端口号。使用sendto()函数发送数据时,需要指定一个元组参数,第一个参数为要发送的数据,第二个参数为接收方的IP地址和端口号。 ``` udp_socket.sendto(b'Hello, World!', ('127.0.0.1', 9999)) ``` 使用recvfrom()函数接收数据时,需要指定一个整数参数,表示要接收的数据的最大长度。函数的返回值为一个元组,包含接收到的数据和对方的IP地址和端口号。 ``` data, addr = udp_socket.recvfrom(1024) print(data.decode(), addr) ``` 最后,我们需要在程序结束时关闭UDP套接字,使用close()函数即可。 ``` udp_socket.close() ``` 总的来说,Python网络编程UDP套接字编程是一种高效的数据传输方式,可以用于实时通讯和游戏等领域。开发者可以按照上述流程来利用socket库实现相应的UDP套接字编程。 ### 回答3: UDP是一种无连接的传输协议,它可以实现快速的数据传输,常用于实时性要求高的场合,例如视频直播、语音通话等。Python作为一种高级编程语言,它拥有良好的网络编程库,可以方便地进行UDP套接字编程。 Python的socket模块是实现套接字编程的核心模块。UDP套接字编程可以通过socket模块实现。下面是一个简单UDP套接字编程的例子: ```python import socket server_address = ('localhost', 10000) data = 'Hello, UDP!' sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.sendto(data.encode(), server_address) received_data, server = sock.recvfrom(4096) print(received_data.decode()) sock.close() ``` 在这个例子中,首先定义了一个服务端地址`server_address`,然后定义了要发送的数据`data`。接下来通过socket模块创建了一个UDP套接字对象`sock`,并调用`sendto()`方法将数据发送给服务端。`sendto()`方法的参数包括要发送的数据和服务端地址。当服务端接收到数据后,它会给客户端返回一个响应消息。客户端可以通过`recvfrom()`方法获取服务端返回的数据。最后,调用`close()`方法关闭套接字。 需要注意的是,在UDP套接字编程中,数据的发送和接收是非阻塞的。这意味着一旦调用`sendto()`方法或`recvfrom()`方法,程序就会立即返回,而不会等待服务端的响应。因此,UDP套接字编程需要自行处理超时、数据丢失等异常情况。 总之,Python的UDP套接字编程可以非常方便地实现快速数据传输,适用于需要处理实时性要求高的场合。开发者可以通过socket模块的UDP套接字编程接口轻松地构建自己的应用程序。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值