9. Linux下实现简单的UDP请求

本文简单介绍了UDP传输层协议,并在Linux下实现简单的socket通讯

一、UDP

UDP(User Datagram Protocol,用户数据报协议)是一种无连接的传输层协议,它不保证数据包的可靠性和顺序。UDP在IP协议的基础上增加了简单的差错检测功能,但是没有流量控制、拥塞控制等复杂机制。
相对于TCP,UDP具有以下优点:

  • 速度快:由于没有建立连接和维护状态等额外开销,在网络带宽较好时UDP可以实现更高的吞吐量和更低的延迟。
  • 简单:UDP协议非常简单,只提供了最基本的数据传输功能,因此实现起来比TCP更容易。
  • 支持广播和多播:UDP支持向多个主机发送同一份数据报文,可以用于组播或广播应用中。
  • 实时性强:由于没有拥塞控制等机制,UDP能够实现较为精准地时间同步、音视频传输等实时应用场景。

总之,在需要快速传输数据且可靠性要求不高的情况下,选择使用UDP会比TCP更合适。
要完成一个完整的 UDP 下的 DNS 网络通信过程,需要使用以下一系列函数:

  1. socket():创建套接字。

  2. sendto():向指定服务器发送 DNS 请求报文。

  3. recvfrom():从服务器接收 DNS 响应报文。

  4. close():关闭套接字,释放资源。

在具体实现时,还需要考虑处理 UDP 数据包丢失、超时重传、DNS 报文的解析和组装等问题。
int recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);

  • sockfd:需要接收数据的套接字描述符。
  • buf:存储接收到数据的缓冲区。
  • len:buf缓冲区的长度。
  • flags:控制recvfrom函数的行为。
  • src_addr:发送方地址信息结构体指针,用于保存发送方IP地址和端口号等信息。如果不需要获取此信息,则可设置为NULL。
  • addrlen:上述地址信息结构体长度。
    在这里插入图片描述

二、实现简单的socket通讯

注意本文是在Linux下实现,若要在window下实现socket需要链接库,参考Windows的socket通讯

在这里插入图片描述

#include <stdio.h>
#include <time.h>
#include <string.h>
#include <stdlib.h>

//window下的头文件
// #include <winSock2.h>
// #include <windows.h>
// #include <sys/types.h>  

//Linux下的头文件
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>


#define DNS_SERVER_PORT     53
#define DNS_SERVER_IP      "114.114.114.114"
#define DNS_HOST			0x01
#define DNS_CNAME			0x05
#define socklen_t           int

struct dns_header{
    unsigned short id;
    unsigned short flags;

    unsigned short questions;
    unsigned short answer;

    unsigned short authority;
    unsigned short additional;
};

struct dns_question{
    int length;
    unsigned short qtype;
    unsigned short qclass;
    unsigned char *name;
};

struct dns_item {
	char *domain;
	char *ip;
};

//创建头部
int dns_create_header(struct dns_header *header){
    if (header == NULL) return -1;
    memset(header,0,sizeof(struct dns_header));

    srand(time(NULL));
    header->id=rand();

    //htons()用于将16位整数由主机字节序转换为网络字节序
    header->flags=htons(0x0100);
    header->questions=htons(1);

    return 0;
}

创建正文
int dns_create_question(struct dns_question *question,const char *hostname){
    if (question == NULL || hostname == NULL) return -1;
    memset(question,0,sizeof(struct dns_question));

    //因为hostname末尾还有结束标记
    question->name=(char *)malloc(strlen(hostname)+2);
    if (question->name == NULL){
        return -2;
    }

    question->length=strlen(hostname)+2;

    question->qtype=htons(1);
    question->qclass=htons(1);

    //若hostname=www.baidu.com,则question->name(查询名)为3www5baidu3com0
    const char delim[2]="."; //C语言中字符串以空字符'\0'作为结尾,因此在定义字符数组时需要额外留出一个元素来存储结尾标志。
    char *qname=question->name;

    char *hostname_dup=strdup(hostname);//将字符串复制到新的内存空间,并返回指向该空间的指针
    char *token=strtok(hostname_dup,delim);

    while (token != NULL){
        size_t len=strlen(token);

        //qname第一位放len,如3
        *qname=len; 
        qname++;

        //放入字母,如www\0,len+1代表加上结束符\0
        strncpy(qname,token,len+1);
        qname+=len;

        token=strtok(NULL,delim);//在后续调用时可直接传入NULL作为第一个参数继续处理上一次未处理完的字符串。

    }

    free(hostname_dup);
}

//构建查询请求
//将DNS消息头部分和查询问题部分按照规定格式打包到缓冲区中,形成一个完整的DNS查询请求报文。
int dns_bulid_requestion(struct dns_header *header,struct dns_question *question,char *request,int rlen){
    if (header == NULL || question == NULL || request == NULL) return -1;
    memset(request,0,rlen);

    //复制header到request
    memcpy(request,header,sizeof(struct dns_header));
    int offset=sizeof(struct dns_header); 

    //复制question到request
    memcpy(request+offset,question->name,sizeof(struct dns_question));
    offset+=question->length;

    memcpy(request+offset,&question->qtype,sizeof(question->qtype));
    offset+=sizeof(question->qtype);

    memcpy(request+offset,&question->qclass,sizeof(question->qclass));
    offset+=sizeof(question->qclass);

    //返回长度
    return offset;
}

//'''''''''''''''''''''''''以下三个函数用于解析'''''''''''''''''''''''''''''''''''''''''''''''
//判断一个DNS记录中的域名是否使用了指针(Pointer)
static int is_pointer(int in) {
	return ((in & 0xC0) == 0xC0);
}

//将DNS查询或响应报文中的域名字段解析成普通字符串。
static void dns_parse_name(unsigned char *chunk, unsigned char *ptr, char *out, int *len) {

	int flag = 0, n = 0, alen = 0;
	char *pos = out + (*len);

	while (1) {

		flag = (int)ptr[0];
		if (flag == 0) break;

		if (is_pointer(flag)) {
			
			n = (int)ptr[1];
			ptr = chunk + n;
			dns_parse_name(chunk, ptr, out, len);
			break;
			
		} else {

			ptr ++;
			memcpy(pos, ptr, flag);
			pos += flag;
			ptr += flag;

			*len += flag;
			if ((int)ptr[0] != 0) {
				memcpy(pos, ".", 1);
				pos += 1;
				(*len) += 1;
			}
		}
	
	}
	
}

//用于解析从服务器返回的DNS响应报文,并提取出其中包含的信息,如IP地址等。
static int dns_parse_response(char *buffer, struct dns_item **domains) {

	int i = 0;
	unsigned char *ptr = buffer;

	ptr += 4;
	int querys = ntohs(*(unsigned short*)ptr);

	ptr += 2;
	int answers = ntohs(*(unsigned short*)ptr);

	ptr += 6;
	for (i = 0;i < querys;i ++) {
		while (1) {
			int flag = (int)ptr[0];
			ptr += (flag + 1);

			if (flag == 0) break;
		}
		ptr += 4;
	}

	char cname[128], aname[128], ip[20], netip[4];
	int len, type, ttl, datalen;

	int cnt = 0;
	struct dns_item *list = (struct dns_item*)calloc(answers, sizeof(struct dns_item));
	if (list == NULL) {
		return -1;
	}

	for (i = 0;i < answers;i ++) {
		
		bzero(aname, sizeof(aname));
		len = 0;

		dns_parse_name(buffer, ptr, aname, &len);
		ptr += 2;

		type = htons(*(unsigned short*)ptr);
		ptr += 4;

		ttl = htons(*(unsigned short*)ptr);
		ptr += 4;

		datalen = ntohs(*(unsigned short*)ptr);
		ptr += 2;

		if (type == DNS_CNAME) {

			bzero(cname, sizeof(cname));
			len = 0;
			dns_parse_name(buffer, ptr, cname, &len);
			ptr += datalen;
			
		} else if (type == DNS_HOST) {

			bzero(ip, sizeof(ip));

			if (datalen == 4) {
				memcpy(netip, ptr, datalen);
				inet_ntop(AF_INET , netip , ip , sizeof(struct sockaddr));

				printf("%s has address %s\n" , aname, ip);
				printf("\tTime to live: %d minutes , %d seconds\n", ttl / 60, ttl % 60);

				list[cnt].domain = (char *)calloc(strlen(aname) + 1, 1);
				memcpy(list[cnt].domain, aname, strlen(aname));
				
				list[cnt].ip = (char *)calloc(strlen(ip) + 1, 1);
				memcpy(list[cnt].ip, ip, strlen(ip));
				
				cnt ++;
			}
			
			ptr += datalen;
		}
	}

	*domains = list;
	ptr += 2;

	return cnt;
	
}
//使用DNS协议向指定域名domain发送查询请求,并接收并处理响应结果。
int dns_client_commit(const char *domain){

    //Socket是一种提供网络通信功能的编程接口或API,它允许不同的计算机之间通过网络进行数据传输。
    // 创建socket
    int sockfd=socket(AF_INET,SOCK_DGRAM,0);
    if (sockfd <0){
        return -1;
    }

    // 绑定地址和端口号
    struct sockaddr_in servaddr={0};
    servaddr.sin_family =AF_INET;
    servaddr.sin_port=htons(DNS_SERVER_PORT);
    servaddr.sin_addr.s_addr = inet_addr(DNS_SERVER_IP);

    //连接服务器
   int ret= connect(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr));
   printf("connect:%d\n",ret);

    // 打包构建查询请求
    struct dns_header header={0};
    dns_create_header(&header);

    struct dns_question question={0};
    dns_create_question(&question,domain);

    char request[1024]={0};
    int length=dns_bulid_requestion(&header,&question,request,1024);

    // 发送消息
    sendto(sockfd,request,length,0,(struct sockaddr *)&servaddr,sizeof(struct sockaddr));

    //接收回复
    char response[1024]={0};
    struct sockaddr_in addr;
    size_t addr_len=sizeof(struct sockaddr_in);
    int n=recvfrom(sockfd,response,sizeof(response),0,(struct sockaddr *)&addr,(socklen_t*)&addr_len);

    //解析
	struct dns_item *dns_domain = NULL;
	dns_parse_response(response, &dns_domain);

	free(dns_domain);
    return n;
}

int main(int argc ,char *argv[]){
    if (argc<2) return -1;
    dns_client_commit(argv[1]);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/23597.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Spring Authorization Server 系列(二)获取授权码

Spring Authorization Server 系列&#xff08;二&#xff09;获取授权码 概述获取授权码获取授权码的url逻辑解析匹配url参数解析 概述 Spring Authorization Server 是基于 OAuth2.1 和 OIDC 1.0 的。 只有 授权码&#xff0c;刷新token&#xff0c;客户端模式。 获取授权码…

Revit建模|Revit风管怎么绘制?

​绘制风管是机电工程重要的一环&#xff0c;对于不少刚接触Revit的小伙伴来说似乎还无从下手&#xff0c;今天就让小编来告诉大家在Revit中绘制风管的方法。 一、在Revit绘制风管 第一步&#xff1a;首先我们先在revit的界面中项目文件找到风管。 第二步&#xff1a;打开后我…

Mysql 学习(十 三)InnoDB的BufferPool

为什么要有缓存&#xff1f; 我们知道每次获取数据我们都需要从磁盘获取&#xff0c;磁盘的运行速度又慢的不行&#xff0c;对于这一个问题我们要怎么解决呢&#xff1f;我们把查询结果存储起来不就行了&#xff0c;因为当需要访问某个页的数据时&#xff0c;就会把完整的页的…

dvwa靶场通关(一)

第一关&#xff1a;Brute force low 账号是admin&#xff0c;密码随便输入 用burp suite抓包 爆破得出密码为password 登录成功 Medium 中级跟low级别基本一致&#xff0c;分析源代码我们发现medium采用了符号转义&#xff0c;一定程度上防止了sql注入&#xff0c;采用暴力破…

简析java JNI技术

前言 认识JNI(Java Native Interface)技术&#xff0c;了解Java调用本地C/C库的简单原理以及一些基本的知识点&#xff1b;自己编写一个自定义的JNI接口。 一、简介 JNI是Java Native Interface的缩写&#xff0c;通过使用 Java本地接口书写程序&#xff0c;可以确保代…

Linux命令(22)之chage

Linux命令之chage 1.chage介绍 chage命令用来更改linux用户密码到期信息&#xff0c;包括密码修改间隔最短、最长日期、密码失效时间等等。 2.chage用法 chage [参数] 用户名 chage常用参数 参数说明-m密码可更改的最小天数&#xff0c;为0表示可以随时更改-M密码有效期最大…

神经网络语言模型(NNLM)

神经网络语言模型【NNLM】 1 为什么使用神经网络模型&#xff1f;2 什么是神经网络模型&#xff1f;3. 代码实现3.1 语料库预处理代码3.2 词向量创建3.3 NNLM模型类3.4 完整代码 1 为什么使用神经网络模型&#xff1f; 解决独热编码无法解决词之间相似性问题 使用神经网络语言…

Blazor实战——Known框架增删改查导

本章介绍学习增、删、改、查、导功能如何实现&#xff0c;下面以商品资料作为示例&#xff0c;该业务栏位如下&#xff1a; 类型、编码、名称、规格、单位、库存下限、库存上限、备注 1. 前后端共用 1.1. 创建实体类 在KIMS项目Entities文件夹下创建KmGoods实体类该类继承Ent…

【C++】类和对象的应用案例 2 - 点和圆的关系

欢迎来到博主 Apeiron 的博客&#xff0c;祝您旅程愉快 &#xff01;时止则止&#xff0c;时行则行。动静不失其时&#xff0c;其道光明。 目录 1、缘起 2、分析 3、示例代码 1 4、代码优化 4.1、point.h 4.2、point.c 4.3、circle.h 4.4、circle.c 4.4、main.c …

Netty 源码分析系列(十八)一行简单的writeAndFlush都做了哪些事?

文章目录 前言源码分析ctx.writeAndFlush 的逻辑writeAndFlush 源码ChannelOutBoundBuff 类addMessage 方法addFlush 方法AbstractNioByteChannel 类 小结 前言 对于使用netty的小伙伴来说&#xff0c;我们想通过服务端往客户端发送数据&#xff0c;通常我们会调用ctx.writeAn…

实时聊天组合功能,你了解吗?

你有兴趣安装实时聊天组合功能吗&#xff1f;如果您选择了SaleSmartly&#xff08;ss客服&#xff09;&#xff0c;您的实时聊天插件可以不仅仅只是聊天通道&#xff0c;还可以有各种各样的功能&#xff0c;你不需要包含每一个功能&#xff0c;正所谓「宁缺勿滥」&#xff0c;功…

再获认可!腾讯连续三年被Gartner列为CWPP供应商之一

随着云的快速发展&#xff0c;企业的工作负载已经从服务器发展到虚拟机、容器、serverless等&#xff0c;部署的模式也日益复杂&#xff0c;包括公有云、混合云和多云等。在此背景下&#xff0c;传统的主机安全防护已无法满足需求&#xff0c;CWPP&#xff08;云工作负载保护平…

C#,码海拾贝(23)——求解“复系数线性方程组“的“全选主元高斯消去法“之C#源代码,《C#数值计算算法编程》源代码升级改进版

using System; namespace Zhou.CSharp.Algorithm { /// <summary> /// 求解线性方程组的类 LEquations /// 原作 周长发 /// 改编 深度混淆 /// </summary> public static partial class LEquations { /// <summary&g…

day20 - 绘制物体的运动轨迹

在我们平常做目标检测或者目标追踪时&#xff0c;经常要画出目标的轨迹图。绘制轨迹图的一种方法就是利用光流估计来进行绘制。 本期我们主要来介绍视频中光流估计的使用和效果&#xff0c;利用光流估计来绘制运动轨迹。 完成本期内容&#xff0c;你可以&#xff1a; 掌握视…

网站部署与上线(1)虚拟机

文章目录 .1 虚拟机简介2 虚拟机的安装 本章将搭建实例的生产环境&#xff0c;将所有的代码搭建在一台Linux服务器中&#xff0c;并且测试其能否正常运行。 使用远程服务器进行连接&#xff1b; 基本的Linux命令&#xff1b; 使用Nginx搭建Node.js服务器&#xff1b; 在服务器端…

一、预约挂号详情

文章目录 一、预约挂号详情1、需求分析 2、api接口2.1 添加service接口2.2 添加service接口实现2.2.1 在ScheduleServiceImpl类实现接口2.2.2 在获取科室信息 2.3 添加controller方法 3、前端3.1封装api请求3.2 页面展示 二、预约确认1、api接口1.1 添加service接口1.2 添加con…

通过python采集整站lazada商品列表数据,支持多站点

要采集整站lazada商品列表数据&#xff0c;需要先了解lazada网站的结构和数据源。Lazada是东南亚最大的电商平台之一&#xff0c;提供各种商品和服务。Lazada的数据源主要分为两种&#xff1a;HTML和API。 方法1&#xff1a;采集HTML数据 步骤1&#xff1a;确定采集目标 首先…

一、CNNs网络架构-基础网络架构(LeNet、AlexNet、ZFNet)

目录 1.LeNet 2.AlexNet 2.1 激活函数&#xff1a;ReLU 2.2 随机失活&#xff1a;Droupout 2.3 数据扩充&#xff1a;Data augmentation 2.4 局部响应归一化&#xff1a;LRN 2.5 多GPU训练 2.6 论文 3.ZFNet 3.1 网络架构 3.2 反卷积 3.3 卷积可视化 3.4 ZFNet改…

Java的Arrays类的sort()方法(41)

目录 sort&#xff08;&#xff09;方法 1.sort&#xff08;&#xff09;方法的格式 2.使用sort&#xff08;&#xff09;方法时要导入的类 3.作用 4.作用的对象 5.注意 6.代码及结果 &#xff08;1&#xff09;代码 &#xff08;2&#xff09;结果 sort&#xff08;&…

【Netty】字节缓冲区 ByteBuf (六)(上)

文章目录 前言一、ByteBuf类二、ByteBuffer 实现原理2.1 ByteBuffer 写入模式2.2 ByteBuffer 读取模式2.3 ByteBuffer 写入模式切换为读取模式2.4 clear() 与 compact() 方法2.5 ByteBuffer 使用案例 总结 前言 回顾Netty系列文章&#xff1a; Netty 概述&#xff08;一&…