9. Linux下实现简单的socket通讯

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

一、UDP

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

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

总之,在需要快速传输数据且可靠性要求不高的情况下,选择使用UDP会比TCP更合适。
在这里插入图片描述

二、实现简单的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]);
}

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

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

相关文章

TCP是面向字节流的协议

TCP字节流 之所以会说 TCP 是面向字节流的协议&#xff0c;UDP 是面向报文的协议&#xff0c;是因为操作系统对 TCP 和 UDP 协议的发送方的机制不同&#xff0c;也就是问题原因在发送方。 为什么 UDP 是面向报文的协议&#xff1f; 当用户消息通过 UDP 协议传输时&#xff0c;…

《Java 核心技术面试》课程笔记(十)

如何保证集合是线程安全的? 典型回答 Java 提供了不同层⾯的线程安全支持。 在传统集合框架内部&#xff0c;除了 Hashtable 等同步容器&#xff0c;还提供了所谓的同步包装器&#xff08;Synchronized Wrapper&#xff09;&#xff0c;我们可以调用 Collections 工具类提供…

Android java层hook------xposed框架的使用

xposed曾经是android平台上最好的java层hook和调试工具&#xff0c;由于已经不再更新&#xff0c;当前支持的android系统版本比较老旧&#xff0c;目前只能支持到android6.0&#xff0c;故已经逐渐落伍&#xff0c;目前android上最广泛使用的hook工具是frida&#xff0c;这是另…

C语言函数大全-- _w 开头的函数(5)

C语言函数大全 本篇介绍C语言函数大全-- _w 开头的函数 1. _wspawnl 1.1 函数说明 函数声明函数功能int _wspawnl(int mode, const wchar_t* cmdname, const wchar_t* arglist, ...);启动一个新的进程并运行指定的可执行文件 参数&#xff1a; mode &#xff1a; 启动命令的…

用爬虫分析沪深300指数超长走势

我们知道&#xff0c;一个股市里面有非常多的股票&#xff0c;我们如何能够量化整个股市整体的行情呢&#xff0c;答案是通过一些综合性的指数。本文所选用的沪深300就是这类指数中的一个。我们先来看一下百度百科对于沪深300的解释。 由于股票价格起伏无常&#xff0c; 投资者…

蓝桥杯拿到一等奖,并分享经验

昨天和群里的小伙伴在群里聊&#xff0c;有的小伙伴竟然说蓝桥杯一等奖没有含量&#xff0c;我也是醉了&#xff01; 就像去年看了一个号主写的&#xff1a;研究生遍地都是! 放眼全国14亿人口&#xff0c;别说研究生了&#xff0c;本科生占比有多少? “蓝桥杯是我人生中得到…

数慧时空20年磨一剑:推出智能遥感云平台DIEY,自然资源多模态大模型“长城”,为地理信息产业提速

作者 | 伍杏玲 出品 | CSDN 据中国地理信息产业发展报告公布的数据&#xff0c;截至2020年末&#xff0c;行业从业单位13.8万家&#xff0c;从业人数336.6万&#xff0c;到2021年末&#xff0c;从业单位增加到16.4万家&#xff0c;从业人数增加到398万&#xff0c;产业规模越…

Go colly爬虫框架精简高效【杠杠的】入门到精通

1 前言 1.1 Go Colly 爬虫介绍 爬虫框架中&#xff0c;各中流行的编程语言都有自己热门框架&#xff0c;python中的selenium、Scrapy、PySpider等&#xff0c;Java中的Nutch、Crawler4j、WebMagic、WebCollector等。golang中colly使用Go语言编写的功能强大的爬虫框架&#xf…

pdf如何删除其中一页?不妨试试这些办法

PDF格式是一种非常常见的文档格式&#xff0c;它可以在各种系统和设备上使用&#xff0c;而且无论在哪里打开&#xff0c;都可以保持格式的一致性。有时候&#xff0c;我们需要删除PDF文档中的一页&#xff0c;无论是为了更改文档的结构&#xff0c;还是为了删除错误的信息。在…

拥抱生成式大模型 --- 提示工程篇

本文为系列的第二篇&#xff0c;主要是学习和总结chatgpt类模型的提示工程。感谢吴恩达老师的开源课程。 引言 随着大型语言模型&#xff08;LLM&#xff09;的发展&#xff0c;LLM 大致可以分为两种类型&#xff0c;即基础LLM和指令微调LLM。基础LLM是基于文本训练数据&…

Python机器学习入门 - - 贝叶斯算法学习笔记

文章目录 前言一、贝叶斯算法简介二、贝叶斯算法的数学原理1. 条件概率2. 全概率公式3. 贝叶斯公式4. 朴素贝叶斯分类器5. 高斯朴素贝叶斯分类器和伯努利朴素贝叶斯分类器 三、Python实现朴素贝叶斯分类总结 前言 贝叶斯公式是我们高中就耳熟能详的统计概率定理&#xff0c;贝…

亚马逊云科技出海日6月9日盛夏盛启

向全球价值链上游奋进 中国企业增强国际竞争力的关键&#xff0c;是努力朝全球价值链上游奋进&#xff0c;发力技术出海。中国的出海新机遇&#xff0c;背后曾是疫情在全球按下数字互联和数字化升级的快进键&#xff0c;跨境电商、在线社交、移动支付、数字服务等数字经济迎来…

如何让你的 Jmeter+Ant 测试报告更具吸引力?

目录 引言 一、安装apache-Ant 二、Jmeter准备 3、生成测试报告 4、JMeter动态参数处理逻辑是什么&#xff1f; 5、JMeter是怎么做API自动化测试的&#xff1f; 结语 引言 想象一下&#xff0c;你辛苦搭建了一个复杂的网站&#xff0c;投入了大量的时间和精力进行开发和…

005 - STM32学习笔记 - 启动代码

005 - STM32学习笔记 - 启动代码 常用汇编指令 指令名称作用EQU给数字常量取一个符号名&#xff0c;相当于C语言中的#define&#xff1b;AREA汇编一个新的代码段或者数据段&#xff1b;SPACE分配内存空间&#xff1b;PRESERVE8当前文件栈需要按照8字节对齐&#xff1b;EXPORT…

k8s pv pvc的介绍|动态存储|静态存储

k8s pv pvc的介绍|动态存储|静态存储 1 emptyDir存储卷2 hostPath存储卷3 nfs共享存储卷4 PVC 和 PVNFS使用PV和PVC 4 搭建 StorageClass NFS&#xff0c;实现 NFS 的动态 PV 创建 1 emptyDir存储卷 当Pod被分配给节点时&#xff0c;首先创建emptyDir卷&#xff0c;并且只要该…

详解c++STL—函数对象

目录 1、函数对象 1.1、函数对象概念 1.2、函数对象的使用 2、谓词 2.1、谓词概念 2.2、一元谓词 2.3、二元谓词 3、内建函数对象 3.1、理解内建函数对象 3.2、算术仿函数 3.3、关系仿函数 3.4、逻辑仿函数 1、函数对象 1.1、函数对象概念 概念&#xff1a; 重载…

编译原理个人作业--第六章——基于 编译原理 国防工业出版社 第三版

2 对表达式((a)(b)) (1) 按照表6.4属性文法构造抽象语法树 (2) 按6.17翻译模式构造表达式抽象语法树 5(1) 下列文法对整型常数、实型常熟世家加法运算符 生成表达式&#xff0c;当两个整型数相加&#xff0c;结果为整形&#xff0c;否则结果为实型 E → E T ∣ T E\r…

面试技术点

一、对热修复、插件化、模块化、组件化有一定研究。 1、模块化 将共享部分或业务模块抽取出来形成独立module。 2、组件化 基于模块化&#xff0c;核心思想是角色的转换&#xff0c;在打包时是library&#xff0c;分离独立的业务组件如微信朋友圈。 3、热修复和插件化种类、…

电力系统机组组合优化调度(IEEE14节点、IEEE30节点、IEEE118节点)(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️❤️&#x1f4a5;&#x1f4a5;&#x1f4a5; &#x1f4dd;目前更新&#xff1a;&#x1f31f;&#x1f31f;&#x1f31f;电力系统相关知识&#xff0c;期刊论文&…

Java并发编程-synchronized

目录 1. synchronized在jdk 1.6中的优化 1.1 锁消除 1.2 锁粗化 1.2 锁升级/锁膨胀 1.2.1 锁升级原理 1.2.2 自适应自旋锁 2. synchronized实现原理 3. synchronized和Lock的对比 1. synchronized在jdk 1.6中的优化 在JDK1.5的时候,Doug Lee推出了ReentrantLock,lock的…
最新文章