网络编程 lesson3 UDP基础编程

目录

UDP介绍

UDP编程

函数接口

recvfrom

sendto

 小练习:实现服务器和客户端相连(使用UDP实现)

client

server


UDP介绍

UDP(User Datagram Protocol,用户数据报协议)是一种在计算机网络中常用的传输层协议。与TCP(Transmission Control Protocol,传输控制协议)相比,UDP是一种无连接的协议,它提供了一种简单的、无状态的数据传输方式。

下面是UDP的一些关键特点和用途:

  1. 面向无连接:UDP在通信之前不需要建立连接,发送方直接将数据报发送到目标地址。这种无连接的特性使UDP的通信过程更加简洁高效,但也导致了它的不可靠性。

  2. 不可靠性:由于UDP缺乏数据确认、重传机制和流量控制,因此它无法保证数据的可靠性和顺序。数据报可能会丢失、重复、乱序或损坏。这使得UDP在一些对数据完整性要求较低、实时性较高的应用中更为适用,如音频、视频、实时游戏等。

  3. 低延迟:由于UDP的简单性和无连接性,它的处理延迟较低,适用于那些对实时性要求较高的应用场景。例如,音频和视频流媒体通常使用UDP来避免因TCP的拥塞控制机制而引入的较大延迟。

  4. 支持广播和多播:UDP可以向一个特定的IP广播地址或多播地址发送数据报,以实现将数据传输给多个接收方的应用场景,如视频会议、实时流广播等。

  5. 轻量级:相对于TCP,UDP的头部开销较小,传输的数据报包含更少的控制信息。这使得UDP在网络带宽较低或资源有限的环境中更加高效。

UDP被广泛用于许多网络应用中,例如音频和视频流媒体(如VoIP、视频聊天)、实时游戏、DNS(Domain Name System,域名系统)等。尽管UDP具有不可靠性和丢包的风险,但通过适当的应用层协议和机制,可以弥补这些缺点,使UDP在特定的应用场景中发挥重要作用。

UDP编程

 注:上图主要是一种思想,实际编程思路如下

udb流程 (类似短信)

server:
创建数据报套接字 sockt (SOCK_DGRAM)
绑定			bind 	绑定
接收			recvfrom 接收信息
关闭套接字		close

client:
创建数据包套接字 sockt (SOCK_DGRAM)
发送			sendto
关闭套接字		close

函数接口

recvfrom

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
					struct sockaddr *src_addr, socklen_t *addrlen);
功能:接收数据

参数:
	sockfd:套接字描述符
	buf:接收缓存区的首地址
	len:接收缓存区的大小
	flags:0
	src_addr:发送端的网络信息结构体的指针
	addrlen:发送端的网络信息结构体的大小的指针
  
返回值:
	成功接收的字节个数
	失败:-1
	0:客户端退出

sendto

ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
                  const struct sockaddr *dest_addr, socklen_t addrlen);
功能:发送数据

参数:
	sockfd:套接字描述符
	buf:发送缓存区的首地址
	len:发送缓存区的大小
	flags:0
	src_addr:接收端的网络信息结构体的指针
	addrlen:接收端的网络信息结构体的大小

返回值: 
	成功发送的字节个数
	失败:-1

 小练习:实现服务器和客户端相连(使用UDP实现)

client

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>

#define Port 2065
#define N 128
int main(int argc, char const *argv[])
{
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0)
    {
        perror("socket err.");
        return -1;
    }
    struct sockaddr_in saddr, caddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(Port);
    saddr.sin_addr.s_addr = INADDR_ANY;

    socklen_t len = sizeof(caddr);

    if (bind(sockfd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0)
    {
        perror("bind err.");
        return -1;
    }
    char buf[N];
    char q[N];
    //注意这里绑定得是发送段得结构体
    //注意循环位置
    while (1)
    {
        ssize_t recvid = recvfrom(sockfd, buf, N, 0, (struct sockaddr *)&caddr, &len); //这里得0不代表阻塞得意思
        if (recvid < 0)
        {
            perror("recvfrom err.");
            return -1;
        }
        printf("端口:%d\n", ntohs(caddr.sin_port));       //注意这里得转换函数
        printf("IP地址:%s\n", inet_ntoa(caddr.sin_addr)); //将网络字节序得二进制转换为字符型
        printf("%s\n", buf);
        sprintf(q, "recv:%s", buf);
        sendto(sockfd, q, N, 0, (struct sockaddr *)&caddr, len);
    }
    close(sockfd);
    return 0;
}

server

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>

#define Port 2065
#define N 128
int main(int argc, char const *argv[])
{
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0)
    {
        perror("socket err.");
        return -1;
    }
    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(Port);
    saddr.sin_addr.s_addr = INADDR_ANY;
    char buf[N];
    socklen_t len = sizeof(saddr);
    while (1)
    {
        fgets(buf, N, stdin);
        if (buf[strlen(buf) - 1] == '\n')
        {
            buf[strlen(buf) - 1] = '\0';
        }
        //绑定接受段得结构体
        sendto(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&saddr, sizeof(saddr));
        recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&saddr, &len);
        printf("%s\n",buf);
    }
    close(sockfd);
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
预处理是数据分析中非常重要的步骤,它是为了准确分析数据而对数据进行的一系列操作。对于lesson数据进行预处理的目的是为了优化数据的质量、可用性和可靠性,以便更好地支持后续的数据分析工作。下面我将从数据清洗、数据规范化、数据转换、数据集成四个方面说明如何对lesson数据进行预处理。 首先是数据清洗,这是预处理的第一个步骤。数据清洗的目的是识别和处理数据中的错误、不一致、重复和缺失等问题,使得数据能够精确无误地支持后续的分析工作。对于lesson数据,我们可以通过删除重复值、填充缺失值、调整不一致的数据格式等方式进行数据清洗。 其次是数据规范化,这是为了处理数据的格式和范围,使得数据更加一致性和可比性。对于lesson数据,我们可以进行数据标准化,如将日期格式统一为YYYY-MM-DD,将文本内容转化为小写或大写等等。 然后是数据转换,这是为了将数据从原始格式转化为适合分析的格式。对于lesson数据,我们可以进行数据转换,如将非数值数据转换为数值数据,对数据进行分类或聚类,进行分箱等等。 最后是数据集成,这是为了将多个数据集合并成一个更大、更全面和更一致的数据集。对于lesson数据,我们可以将不同的数据源集成,比如将学生的成绩、出勤记录、课程评价等数据集成在一起,以进行更全面的数据分析和挖掘。 综上所述,通过数据清洗、数据规范化、数据转换、数据集成等多方面预处理操作,我们可以提高lesson数据的质量、可用性和可靠性,为后续的数据分析工作提供有力的支持。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值