工作中学到的一些小点

news/2024/5/18 13:38:54 标签: udp, 网络, 网络协议, 组播

1.结构体对齐
记得之前面试的时候被问过这个问题【汗】
这个结构体占多大

struct sExample {
	char c;
	int n;
};

占8字节,问有没有办法让它占5个字节?

#pragma pack(push)	//保存对齐状态
#pragma pack(1)		//设定为1字节对齐

struct sExample {
	char c;
	int n;
};
#pragma pack(pop)	//恢复对齐状态

为什么要加保存和恢复对齐状态?为了不影响别人

#pragma pack(2)

#pragma pack(push)	//保存对齐状态
#pragma pack(1)		//设定为1字节对齐

struct sExample {
	char c;
	int n;
};
#pragma pack(pop)	//恢复对齐状态

struct sDemo {
	char c;
	int n;
};

原来是按2字节对齐的,对sExample设置完后,不要改变原来的对齐方式
这里sizeof(sExample)是5,sizeof(sDemo)是6
(反思,为什么当时学c语言的时候没学过这个?)
2.组播
组播和单播不一样,所以为什么叫组播呢?这个问题其实当时在学校写计算机网络课设的时候就碰到了,当时写的聊天室,实验要求是用组播,但我用的单播,课设也过了,所以这个问题不了了之。

组播技术指的是单个发送者对应多个接收者的一种网络通信。
IP 组播技术有效地解决了单点发送多点接收的问题,实现了 IP 网络中点到多点的高效数据传送,能够大量节约网络带宽、降低网络负载。

组播地址是分类编址的IPV4地址中的D类地址,又叫多播地址,他的前四位必须是1110,所以网络地址的取值范围是224-239(11100000-11101111)

224.0.0.0~224.0.0.255为预留的组播地址(永久组地址),地址224.0.0.0保留不做分配,其他地址供路由协议使用
224.0.1.0~224.0.1.255是公用组播地址,可以用于Internet
224.0.2.0~238.255.255.255为用户可用的组播地址(临时组地址),全网范围内有效
239.0.0.0~239.255.255.255为本地管理组播地址,仅在特定的本地范围内有效

一个例子:
服务端:

#include <iostream>
#include <winsock2.h>
#include <WS2tcpip.h>
#include <stdio.h>

#pragma comment(lib, "Ws2_32.lib")

#define DEFAULT_BUFLEN 512

using namespace std;

int main()
{
	//Declare and initialize variables
	WSADATA wsaData = { 0 };
	int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
	if (iResult != 0) {
		wprintf(L"WSAStartup failed: %d\n", iResult);
		return -1;
	}

	//创建socket
	SOCKET sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if (sock == INVALID_SOCKET) {
		wprintf(L"socket function failed with error = %d\n", WSAGetLastError());
		WSACleanup();
		return -1;
	}

	sockaddr_in addr;
	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = INADDR_ANY;
	addr.sin_port = htons(8889);

	iResult = bind(sock, (sockaddr*)(&addr), sizeof(addr));
	if (iResult == SOCKET_ERROR) {
		wprintf(L"bind function failed with error: %ld\n", WSAGetLastError());
		WSACleanup();
		return -1;
	}

	struct ip_mreq imr;
	imr.imr_multiaddr.s_addr = inet_addr("234.2.2.2");
	imr.imr_interface.s_addr = INADDR_ANY;
	//加入组播组
	iResult = setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&imr, sizeof(imr));
	if (iResult == SOCKET_ERROR) {
		wprintf(L"setsockopt function failed with error: %ld\n", WSAGetLastError());
		WSACleanup();
		return -1;
	}

	char recvbuf[DEFAULT_BUFLEN] = "";
	int recvbuflen = DEFAULT_BUFLEN;

	sockaddr_in client;
	int clientLen;
	clientLen = sizeof(client);
	memset(&client, 0, clientLen);

	iResult = recvfrom(sock, recvbuf, recvbuflen, 0, (sockaddr*)(&clientLen), &clientLen);
	if (iResult > 0)
		printf("Bytes received: %d\n%s\n", iResult, recvbuf);
	else if (iResult == 0)
		printf("Connection closed\n");
	else
		printf("recv failed: %d\n", WSAGetLastError());

	closesocket(sock);
	WSACleanup();

	system("pause");
	return 0;
}

客户端:

#include <iostream>
#include <winsock2.h>
#include <WS2tcpip.h>
#include <stdio.h>

#pragma comment (lib, "Ws2_32.lib")

#define DEFAULT_BUFLEN 512

using namespace std;

int main()
{
	//Declare and initialize variables
	WSADATA wsaData = { 0 };
	int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
	if (iResult != 0) {
		wprintf(L"WSAStartup failed: %d\n", iResult);
		return -1;
	}

	//创建socket
	SOCKET sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if (sock == INVALID_SOCKET) {
		wprintf(L"socket function failed with error = %d\n", WSAGetLastError());
		WSACleanup();
		return -1;
	}

	sockaddr_in addr;
	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = inet_addr("234.2.2.2");
	addr.sin_port = htons(8889);

	const char *sendbuf = "hello, server";
	iResult = sendto(sock, sendbuf, (int)strlen(sendbuf), 0, (sockaddr*)(&addr), sizeof(addr));
	if (iResult == SOCKET_ERROR) {
		wprintf(L"send failed with error: %d\n", WSAGetLastError());
		closesocket(sock);
		WSACleanup();
		return -1;
	}
	printf("send: %d\n", iResult);

	closesocket(sock);
	WSACleanup();

	system("pause");
	return 0;
}

3.网络传输中大小端问题
大小端也是面试中经常被问到的问题。
网络传输中确实需要注意大小端的问题,最近在极客时间上看的网络编程实战中也提到了这个问题。发送端发送时需要将发送的内容转成网络字节序的形式,因为你也不知道接受的主机是大端还是小端,如果你俩都是小端机,万事大吉。但如果发和收的字节序不一样就会出现问题,因此双方都应该以网络字节序交流。
那怎么理解这个事呢?比如现在要发送0x0102,占两个字节,在我电脑(小端机)上内存存放如下:
在这里插入图片描述

先发送低地址的数据0x02,再发送高地址的数据0x01,但如果我的电脑是大端机,则会先发送0x01再发送0x02,效果截然不同。因此,系统中提供了一组函数用来转换:

uint16_t htons (uint16_t hostshort)
uint16_t ntohs (uint16_t netshort)
uint32_t htonl (uint32_t hostlong)
uint32_t ntohl (uint32_t netlong)

函数中的 n 代表的就是 network,h 代表的是 host,s 表示的是 short,l 表示的是 long,分别表示 16 位和 32 位的整数。
值得关注的是,占一个字节的char类型不需要转,因为它只占一个字节,而字节是最小的存储单位。

//一种判断主机大小端的方法:
short j = 0x1234;
if (reinterpret_cast<char &>(j) == 0x12) {
	cout << "The byte order is big-endian" << endl;
}
else {
	cout << "The byte order is little-endian" << endl;
}

4.get_value

template<typename T>
T getValue(const char *buf, T offset) {
	return *((T*)(buf + offset));
}

int main()
{
	const char *buf = "123";
	char value = getValue<char>(buf, 0);
	short sValue = getValue<short>(buf, 1);

	cout << value << endl;
	cout << sValue << endl;
}

输出:

1
13106

5.QByteArray

QByteArray byteArray("gaoyuelong");
qDebug() << byteArray.size();

输出:10

    QByteArray byteArray;

    QDataStream dataStream(&byteArray, QIODevice::WriteOnly);
    dataStream << "gaoyuelong";
    //byteArray的size为15 包含开头用来记录长度的四字节
    qDebug() << byteArray.size();

    QDataStream dataStream1(&byteArray, QIODevice::ReadOnly);

    int iLength = 0;
    dataStream1 >> iLength;
    qDebug() << iLength;

    //data() + 4跳过开头的四字节
    char *data = byteArray.data() + 4;
    while (*data) {
        qDebug() << "[" << *data << "]";
        ++data;
    }

输出:

15
11
[ g ]
[ a ]
[ o ]
[ y ]
[ u ]
[ e ]
[ l ]
[ o ]
[ n ]
[ g ]

需要注意使用dataStream1读取时,开头有四个字节表示长度。


http://www.niftyadmin.cn/n/11181.html

相关文章

小红书标题怎样吸引人?小红书的标题技巧

小红书在发布笔记的时候除了封面很重要之外&#xff0c;小红书的标题也很重要。 小红书的标题承担着披露信息&#xff0c;吸引用户的重要职责。 现在流行的小红书标题大致有&#xff1a; 1.正常叙事&#xff0c;把标题专注于内容本身上 2.用感叹句或者疑问句来进行吸引 3.…

带你走进神奇的元宇宙的世界

&#x1f3e0;个人主页&#xff1a;黑洞晓威 &#x1f9d1;个人简介&#xff1a;大家好&#xff0c;我是晓威&#xff0c;一名普普通通的大二在校生&#xff0c;希望在CSDN中与大家一起成长。&#x1f381;如果你也在正在学习Java&#xff0c;欢迎各位大佬来到我的博客查漏补缺…

LinuxHadoop环境

Hadoop环境Hadoop集群拓扑1、集群拓扑2、角色分配一、虚拟机安装二、虚拟机克隆1、克隆类型&#xff08;1&#xff09;完整克隆&#xff08;2&#xff09;链接克隆2、克隆步骤&#xff08;1&#xff09;克隆出master虚拟机&#xff08;2&#xff09;克隆出slave1虚拟机&#xf…

11.23Spring 学习第三天

AOP AOP&#xff08;Aspect Oriented Programming&#xff09;意为&#xff1a;面向切面编程&#xff0c;通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。 AOP是OOP的延续&#xff0c;是软件开发中的一个热点&#xff0c;也是Spring框架中的一个重要内容&a…

总结java程序是如何运行部署的

我们写的代码写完并测试以后是如何部署给用户使用的? 部署好运行环境&#xff08;操作系统、jvm、中间件&#xff09;——配置好网络——把程序和相关服务打包成可执行文件——把可执行文件放到运行环境运行 我们自己在自己的电脑上写代码又是如何运行的&#xff1f; idea内置…

物联网安全年报漏洞情况

物联网 威胁分析漏洞篇物联网威胁分析—漏洞篇 引言 本章将从漏洞利用角度对物联网威胁进行分析。首先&#xff0c;我们分析了 NVD和 Exploit-DB中的物联网 年度漏洞及利用 1 变化趋势&#xff1b;之后统计了绿盟威胁捕获系统捕获到的物联网漏洞利用的整体情况&#xff1b;最…

Servlet API(HttpSerrvlet+HttpServletRequest+HttpServletResponse)

目录 &#x1f432; 1. HttpServlet &#x1f432; 2. HttpServletRequest HTTP请求 &#x1f984; 2.1 打印请求信息(创建 ShowRequest 类) &#x1f984; 2.2 获取 GET 请求中的参数(创建 GetParameter 类) &#x1f984; 2.3 获取 POST 请求中的参数(创建 PostParame…

MySQL数据库的约束

文章目录一、约束是什么&#xff1f;二、约束的具体操作Not NULLUNIQUE约束的组合使用PRIMARY KEYDEFAULTFOREIGN KEY一、约束是什么&#xff1f; 约束就是&#xff0c;在创建表的时候&#xff0c;对表设置一些规则&#xff0c;只有满足这些规则&#xff0c;才可以插入数据&am…