文章目录
- UDP和TCP
- 七层网络架构
- Omnipeek抓包分析举例
- 图片备注
- code
- 参考code
- HTTP协议的构成
UDP和TCP
UDP(User Datagram Protocol,用户数据报协议) 和 TCP(Transmission Control Protocol,传输控制协议) 是网络传输层协议
而我们的今天的主角HTTP(HyperText Transfer Protocol,超文本传输协议) 则是应用层协议
七层网络架构
Omnipeek抓包分析举例
图片备注
code
struct ip_header {
uint8_t iph_ihl:4, iph_ver:4;
uint8_t iph_tos;
uint16_t iph_len; // 2
uint16_t iph_id;
uint16_t iph_offset;
uint8_t iph_ttl;
uint8_t iph_protocol;
uint16_t iph_checksum;
uint32_t iph_saddr;
uint32_t iph_daddr;
}__attribute__((__packed__));
struct udp_header {
uint16_t udp_sport; // 源端口
uint16_t udp_dport; // 目的端口
uint16_t udp_len; // UDP数据包长度
uint16_t udp_checksum; // UDP校验和
};
// 伪头部结构(UDP校验和计算需要)
struct pseudo_header {
uint32_t source_address;
uint32_t dest_address;
uint8_t placeholder;
uint8_t protocol;
uint16_t udp_length;
};
/// 求校验和Function
static uint16_t checksum(void *b, int len) {
uint16_t *buf = b;
uint32_t sum = 0;
uint16_t result;
// 计算 16 位反码加和
for (sum = 0; len > 1; len -= 2)
sum += *buf++;
if (len == 1)
sum += *(unsigned char *)buf;
sum = (sum >> 16) + (sum & 0xFFFF);
sum += (sum >> 16);
result = ~sum;
return result;
}
int main(void)
{
int ret = 0;
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr("192.168.1.1"); ;
addr.sin_port = htons(0); // 端口号对原始套接字通常没有意义 无论设置多少,都是会接受所有本地址下的所有端口信息
int socket_raw = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
ret = bind(socket_raw , (struct sockaddr *)&addr, sizeof (struct sockaddr));
// ……
}
使用原始套接字:发送:需要自己从传输层协议包构造,接受:是从IP包开始的
参考code
// struct ip_header *ip = (struct ip_header *)packet;
// ip->iph_ihl = 5; // IP 头部长度(单位是32位,即4字节)
// ip->iph_ver = 4; // 使用 IPv4
// ip->iph_tos = 0x10; // 服务类型(低延迟)
// ip->iph_len = htons(sizeof(struct ip_header) + sizeof(struct udp_header) + snd_len); // IP数据包 总长度 (是包括传输层的数据的)
// ip->iph_id = htons(54300); // ID(标识一个数据包 不分段,所以可以随便设置)
// ip->iph_offset = 0; // 偏移量(不分段)
// ip->iph_ttl = 64; // TTL(最大跳转路由数)
// ip->iph_protocol = IPPROTO_UDP; // 协议类型 UDP
// ip->iph_saddr = inet_addr(OEMINFO_DEFAULT_NET_IP); // 源 IP
// ip->iph_daddr = cli_addr.sin_addr.s_addr; // 目标 IP
// ip->iph_checksum = 0;
// 地址 https://docs.pingcode.com/baike/1281537
// 填充 UDP 头部
// struct udp_header *udp = (struct udp_header *)(packet + sizeof(struct ip_header));
struct udp_header *udp = (struct udp_header *)(packet);
if (prohdr->cid == 1 || prohdr->cid == 2 || prohdr->cid == 3 || prohdr->cid == 6 || prohdr->cid == 7 || prohdr->cid == 8 || prohdr->cid == 10 || prohdr->cid == 11) {
udp->udp_sport = htons(OEMINFO_DEFAULT_BASE_PORT); // 源端口
} else {
udp->udp_sport = htons(OEMINFO_DEFAULT_BASE_PORT + 1); // 源端口
}
udp->udp_dport = htons(cli_addr.sin_port); // 目标端口
udp->udp_len = htons(sizeof(struct udp_header) + snd_len); // UDP 数据包长度
udp->udp_checksum = 0; // 初始值 0,稍后计算 (伪头部、UDP头部、数据部分)
// 伪头部结构 ( 只为计算校验和 )
struct pseudo_header psh;
psh.source_address = inet_addr(OEMINFO_DEFAULT_NET_IP);
psh.dest_address = cli_addr.sin_addr.s_addr;
psh.placeholder = 0; // 占位符
psh.protocol = IPPROTO_UDP;
psh.udp_length = htons(sizeof(struct udp_header) + snd_len); // UDP 头 + 数据
int psize = sizeof(struct pseudo_header) + sizeof(struct udp_header) + snd_len;
// 先填充
// uint8_t *data = packet + sizeof(struct ip_header) + sizeof(struct udp_header); // 往后挪,移出位置写数据
uint8_t *data = packet + sizeof(struct udp_header); // 往后挪,移出位置写数据
memcpy(data, buffer, snd_len);
// 验证数据是否在数据本该的位置上
printf("data[0] = %02x, data[1] = %02x, data[2] = %02x, data[3] = %02x\r\n", data[0], data[1], data[2], data[3]);
pseudo_packet = malloc(psize);
if (pseudo_packet == NULL) {
printf("Memory allocation failed\n");
return;
}
memcpy(pseudo_packet, (uint8_t *)&psh, sizeof(struct pseudo_header));
memcpy(pseudo_packet + sizeof(struct pseudo_header), udp, sizeof(struct udp_header));
memcpy(pseudo_packet + sizeof(struct pseudo_header) + sizeof(struct udp_header), data, snd_len);
// 计算 UDP 校验和
// ip->iph_checksum = checksum(packet, sizeof(struct ip_header)); // IP 头部校验和
udp->udp_checksum = checksum(pseudo_packet, psize);
// 后填充数据
// memcpy(packet, ip, sizeof(struct ip_header)); // 往后挪,移出位置写IP头
// memcpy(packet + sizeof(struct udp_header), buffer, snd_len); // 往后挪,移出位置写UDP头
free(pseudo_packet);
pseudo_packet = NULL;
int ret = -1;
while(ret <= 0) {
ret = sendto(sock, packet, sizeof(struct udp_header) + snd_len, 0, (struct sockaddr *)&cli_addr, addr_len);
}
if (ret > 0) {
printf("send success ,send size is %d\r\n", ret);
}
memset(packet, 0, sizeof(packet));
HTTP协议的构成
HTTP包,即HTTP报文,是HTTP协议中传输数据的基本单位。
结构: 头 \r\n\r\n 正文
栗子:
POST http://example.com/upload HTTP/1.0
Host: example.com
Accept-Encoding: ''
Content-Type: image/jpeg
Content-Length: 10240
deviceId: 1234567890
deviceToken: abcdefg
action: start
sid: 123
stream: 1
同下:
uint8_t http_uri[1024] = {0};
sprintf(http_uri, "POST %s HTTP/1.0\r\nHost: %s\r\nAccept-Encoding: ''\r\nContent-Type: image/jpeg\r\nContent-Length: %d\r\n"
"deviceId: %s\r\ndeviceToken: %s\r\naction: start\r\nsid: %d\r\nstream: 1\r\n\r\n", ……);
先到这,关注一下,下一节继续讲HTTP~