• 工作中学到的一些小点


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

    struct sExample {
    	char c;
    	int n;
    };
    
    • 1
    • 2
    • 3
    • 4

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

    #pragma pack(push)	//保存对齐状态
    #pragma pack(1)		//设定为1字节对齐
    
    struct sExample {
    	char c;
    	int n;
    };
    #pragma pack(pop)	//恢复对齐状态
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

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

    #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;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

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

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

    组播地址是分类编址的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为本地管理组播地址,仅在特定的本地范围内有效
    
    • 1
    • 2
    • 3
    • 4

    一个例子:
    服务端:

    #include 
    #include 
    #include 
    #include 
    
    #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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74

    客户端:

    #include 
    #include 
    #include 
    #include 
    
    #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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    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)
    
    • 1
    • 2
    • 3
    • 4

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

    //一种判断主机大小端的方法:
    short j = 0x1234;
    if (reinterpret_cast(j) == 0x12) {
    	cout << "The byte order is big-endian" << endl;
    }
    else {
    	cout << "The byte order is little-endian" << endl;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    4.get_value

    template
    T getValue(const char *buf, T offset) {
    	return *((T*)(buf + offset));
    }
    
    int main()
    {
    	const char *buf = "123";
    	char value = getValue(buf, 0);
    	short sValue = getValue(buf, 1);
    
    	cout << value << endl;
    	cout << sValue << endl;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    输出:

    1
    13106
    
    • 1
    • 2

    5.QByteArray

    QByteArray byteArray("gaoyuelong");
    qDebug() << byteArray.size();
    
    • 1
    • 2

    输出: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;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    输出:

    15
    11
    [ g ]
    [ a ]
    [ o ]
    [ y ]
    [ u ]
    [ e ]
    [ l ]
    [ o ]
    [ n ]
    [ g ]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

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

  • 相关阅读:
    如何将Python打包后的exe还原成.py?
    SpringBoot 中如何操作事务?
    【C++】模板template
    HiEV独家 | 接棒余承东,华为光产品线总裁靳玉志出任车BU CEO
    Rust嵌入式编程---panic处理和异常处理
    oracle实验四
    对于Oracle,MySQL,SQL Server重复数据删除
    计算机网络中的一些基本概念
    安卓恶意应用识别(三)(批量反编译与属性值提取)
    【阿旭机器学习实战】【6】普通线性线性回归原理及糖尿病进展预测实战
  • 原文地址:https://blog.csdn.net/gaoyuelon/article/details/127931356