端口号的作用类似pid,用来标识进程的唯一性。只是为了与系统解耦,所以有了端口号。
通过ip来确定唯一主机,再通过端口号找到指定的进程。就可以让全网内唯一的两个进程通信了。
所以一个完整的报文至少要携带ip和端口号,ip是在网络层协议来维护的本章不做讲解,而端口号是在传输层协议中维护的,传输层协议常用的两种:UDP协议和TCP协议,本章将要讲解的是UDP协议,TCP协议在下一期进行讲解。
端口号:2字节(16个比特位)其中:
认识知名端口号 有些服务器是非常常用的, 为了使用方便, 人们约定一些常用的服务器), 都是用以下这些 固定的端口号:
执行下面的命令, 可以看到知名端口号:
cat /etc/services思考:
答1:一个进程是可以绑定多个端口号的,当一个进程提供多个不同服务时,就可以通过绑定多个端口来优化。只要一个端口能确定唯一进程就行。
答2:不能。要保证一个端口确定唯一进程。

UDP协议格式其实就是一个结构体,源码如下:
struct udphdr {
unsigned short src_port;
unsigned short dst_port;
unsigned short len;
unsigned short chksum;
};在把数据交付给传输层时,数据在应用层必做序列化和反序列化。当然可直接用结构体变量代替,但非常不推荐(结构体存在内存对齐有内存浪费,与其他语言不兼容,需要考虑字节序问题等等)。
注:即使通信双方操作系统完全不同,但网络内核部分一定相同(所以能够通信)。操作系统内核都是使用c语言实现。
在OS内部一定会同时存在大量的报文,而这些报文可分布在各个协议层,OS必须管理这些报文。如果管理?先描述,再组织。如何描述:内核源码中结构体 struct sk_buff,如下:
struct sk_buff
{
struct sk_buff * next; /* Next buffer in list */
struct sk_buff * prev; /* Previous buffer in list */
struct sk_buff_head * list; /* List we are on */
#if CONFIG_SKB_CHECK
int magic_debug_cookie;
#endif
struct sk_buff *link3; /* Link for IP protocol level buffer chains */
struct sock *sk; /* Socket we are owned by */
unsigned long when; /* used to compute rtt's */
struct timeval stamp; /* Time we arrived */
struct device *dev; /* Device we arrived on/are leaving by */
union
{
struct tcphdr *th;
struct ethhdr *eth;
struct iphdr *iph;
struct udphdr *uh;
unsigned char *raw;
/* for passing file handles in a unix domain socket */
void *filp;
} h;
union
{
/* As yet incomplete physical layer views */
unsigned char *raw;
struct ethhdr *ethernet;
} mac;
struct iphdr *ip_hdr; /* For IPPROTO_RAW */
unsigned long len; /* Length of actual data */
unsigned long csum; /* Checksum */
__u32 saddr; /* IP source address */
__u32 daddr; /* IP target address */
__u32 raddr; /* IP next hop address */
__u32 seq; /* TCP sequence number */
__u32 end_seq; /* seq [+ fin] [+ syn] + datalen */
__u32 ack_seq; /* TCP ack sequence number */
unsigned char proto_priv[16]; /* Protocol private data */
volatile char acked, /* Are we acked ? */
used, /* Are we in use ? */
free, /* How to free this buffer */
arp; /* Has IP/ARP resolution finished */
unsigned char tries, /* Times tried */
lock, /* Are we locked ? */
localroute, /* Local routing asserted for this frame */
pkt_type, /* Packet class */
pkt_bridged, /* Tracker for bridging */
ip_summed; /* Driver fed us an IP checksum */
#define PACKET_HOST 0 /* To us */
#define PACKET_BROADCAST 1 /* To all */
#define PACKET_MULTICAST 2 /* To group */
#define PACKET_OTHERHOST 3 /* To someone else */
unsigned short users; /* User count - see datagram.c,tcp.c */
unsigned short protocol; /* Packet protocol from driver. */
unsigned short truesize; /* Buffer size */
atomic_t count; /* reference count */
struct sk_buff *data_skb; /* Link to the actual data skb */
unsigned char *head; /* Head of buffer */
unsigned char *data; /* Data head pointer */
unsigned char *tail; /* Tail pointer */
unsigned char *end; /* End pointer */
void (*destructor)(struct sk_buff *); /* Destruct function */
__u16 redirport; /* Redirect port */
};在以上源码中我们主要关注两个部分:

该字段说明报文用了链表结构来维护,对报文进行操作的本质就是链表的增删改查。
怎么体现不同协议层报文呢?如下字段:


变量 | 指向位置 | 功能 |
|---|---|---|
head | 缓冲区的起始地址 | 指向整个数据包内存的头部(包括预留的“头空间”)。 |
data | 当前协议层头部 | 随着协议栈处理,动态移动: 接收时:从MAC头 → IP头 → TCP(或UDP)头 → 应用数据。 发送时:反向移动。 |
tail | 当前有效数据的末尾 | 标识实际数据的结束位置(如应用层数据的末尾)。 |
end | 缓冲区的结束地址 | 指向整个数据包内存的尾部(包括预留的“尾空间”)。 |
[head,end]:缓冲区大小。
[data,tail]:报文。封装和解包本质:移动data指针在缓冲区的位置,加减对应层协议长度。
非常感谢您能耐心读完这篇文章。倘若您从中有所收获,还望多多支持呀!