数据结构
/**
* struct socket - general BSD socket
* @state: socket state (%SS_CONNECTED, etc)
* @flags: socket flags (%SOCK_ASYNC_NOSPACE, etc)
* @ops: protocol specific socket operations
* @fasync_list: Asynchronous wake up list
* @file: File back pointer for gc
* @sk: internal networking protocol agnostic socket representation
* @wait: wait queue for several uses
* @type: socket type (%SOCK_STREAM, etc)
*/
struct socket {
socket_state state; //socket 的状态
unsigned long flags; //socket 的标志位
const struct proto_ops *ops; //socket 的函数操作表
struct fasync_struct *fasync_list; //socket 的异步唤醒队列
struct file *file; // 与socket关联的文件指针
struct sock *sk; // 代表具体协议内容的 sock 结构指针
wait_queue_head_t wait; // 等待队列
short type; //socket 的类型
};
从 socket 结构体可以看出 socket 是通用的套接字结构体的公共部分,而其中的 sock 结构体则是与使用的具体协议相关的部分,可以理解成从 socket 中抽象出 sock 部分,sock 结构体是根据使用的协议挂入到 socket 中,下面了解下 sock 结构体。
struct sock {
/*
* Now struct inet_timewait_sock also uses sock_common, so please just
* don't add nothing before this first member (__sk_common) --acme
*/
struct sock_common __sk_common; // 与 inet_timewait_sock 共享使用
#define sk_family __sk_common.skc_family // 地址族
#define sk_state __sk_common.skc_state // 连接状态
#define sk_reuse __sk_common.skc_reuse // 确定复用地址
#define sk_bound_dev_if __sk_common.skc_bound_dev_if //绑定设备 ID
#define sk_node __sk_common.skc_node // 链入主哈希表
#define sk_bind_node __sk_common.skc_bind_node // 链入绑定哈希表
#define sk_refcnt __sk_common.skc_refcnt // 使用计数
#define sk_hash __sk_common.skc_hash // 哈希值
#define sk_prot __sk_common.skc_prot // 协议函数表
#define sk_net __sk_common.skc_net // 所属的网络空间
unsigned char sk_shutdown : 2, // 是否关闭,mask of %SEND_SHUTDOWN and/or %RCV_SHUTDOWN
sk_no_check : 2, // 是否检查数据包
sk_userlocks : 4; // 用户锁,%SO_SNDBUF and %SO_RCVBUF settings
unsigned char sk_protocol; // 使用协议族的哪一种协议
unsigned short sk_type; // socket 的类型,例如 SOCK_STREAM 等
int sk_rcvbuf; // 接受缓冲区的长度(字节数)
socket_lock_t sk_lock; // 用于同步
/*
* The backlog queue is special, it is always used with
* the per-socket spinlock held and requires low latency
* access. Therefore we special case it's implementation.
*/
struct {
struct sk_buff *head; // 记录最先接收到的数据包
struct sk_buff *tail; // 记录最后接收到的数据包
} sk_backlog; // 后备队列
wait_queue_head_t *sk_sleep; //sock 的等待队列
struct dst_entry *sk_dst_cache; // 路由项缓存
struct xfrm_policy *sk_policy[2]; //流策略
rwlock_t sk_dst_lock; // 路由项缓存锁
atomic_t sk_rmem_alloc; // 接受队列的字节数
atomic_t sk_wmem_alloc; // 发送队列的字节数
atomic_t sk_omem_alloc; // 可选择/其他 的字节数
int sk_sndbuf; // 发送缓存的总长度
struct sk_buff_head sk_receive_queue; //接收队列(接收到的数据包队列)
struct sk_buff_head sk_write_queue; //发送队列(正在发送的数据包队列)
struct sk_buff_head sk_async_wait_queue; //DMA 复制的数据包 TODO
int sk_wmem_queued; //全部数据包占用内存计数
int sk_forward_alloc; //记录可用内存长度
gfp_t sk_allocation; //分配模式
int sk_route_caps; //路由的兼容性标志位
int sk_gso_type; //GSO 通用分段类型 TODO
unsigned int sk_gso_max_size; //用于建立 GSO 通用分段的最大长度
int sk_rcvlowat; //SO_RCVLOWAT 设置
unsigned long sk_flags; //SO_BROADCAST、SO_KEEPALIVE、SO_OOBINLINE、SO_LINGER 设置
unsigned long sk_lingertime; //停留时间,确定关闭时间
struct sk_buff_head sk_error_queue; // 错误数据包队列
struct proto *sk_prot_creator; //sock 创建接口
rwlock_t sk_callback_lock; // 为后半部处理使用的锁
int sk_err, //出错码
sk_err_soft; //持续出现的错误
atomic_t sk_drops; //原始 socket 发送的计数器
unsigned short sk_ack_backlog; //当前监听到的连接数量
unsigned short sk_max_ack_backlog; //在 listen() 函数中监听到的连接数量
__u32 sk_priority; //优先级
struct ucred sk_peercred; // SO_PEERCRED 设置
long sk_rcvtimeo; // SO_RCVTIMEO 设置接受超时时间
long sk_sndtimeo; // SO_SNDTIMEO 设置发送超时时间
struct sk_filter *sk_filter; //sock 的过滤器
void *sk_protinfo; //私有区域,当不使用slab高速缓存时由协议族定义
struct timer_list sk_timer; //sock 的冲刷定时器
ktime_t sk_stamp; //最后接收数据包的时间
struct socket *sk_socket; //对应的 socket 指针
void *sk_user_data; //rpc 提供的数据
struct page *sk_sndmsg_page; // 发送数据块所在的缓冲页
struct sk_buff *sk_send_head; // 发送数据包的队列头
__u32 sk_sndmsg_off; //发送数据块在缓冲页的结尾
int sk_write_pending; //等待发送的数量
void *sk_security; //用于安全模式
__u32 sk_mark; //通用的数据包掩码
/* XXX 4 bytes hole on 64 bit */
void (*sk_state_change)(struct sock *sk); //sock 状态改变后调用的函数
void (*sk_data_ready)(struct sock *sk, int bytes); //在数据被处理完成后调用的函数
void (*sk_write_space)(struct sock *sk); //发送空间可以使用后调用的函数
void (*sk_error_report)(struct sock *sk); //处理错误的函数
int (*sk_backlog_rcv)(struct sock *sk, //处理库存数据包函数
struct sk_buff *skb);
void (*sk_destruct)(struct sock *sk); //sock 的销毁函数
};
与应用程序密切相关的共用部分放在了socket结构中,而与协议相关的内容则放在sock结构中,然后使socket与sock挂钩,设计灵活巧妙。
我们看到sock中数据包的结构通过sk_buff来体现,每个协议都是通过sk_buff结构体用于封装、载运数据包,我们可以看下其数据结构。
struct sk_buff {
/* These two members must be first. */
struct sk_buff *next; //队列中的下一个数据包
struct sk_buff *prev; //队列中的前一个数据包
struct sock *sk; //指向所属的 sock 数据包
ktime_t tstamp; //数据包到达的时间
struct net_device *dev; //接收数据包的网络设备
union {
struct dst_entry *dst; //路由项
struct rtable *rtable; //路由表
};
struct sec_path *sp; //用于 xfrm 的安全路径
/*
* This is the control buffer. It is free to use for every
* layer. Please put your private variables there. If you
* want to keep them across layers you have to do a skb_clone()
* first. This is owned by whoever has the skb queued ATM.
*/
char cb[48]; // cb 控制块
unsigned int len, //全部数据块的总长度
data_len; //分段、分散数据块的总长度
__u16 mac_len, //链路层头部的长度
hdr_len; //在克隆数据包时可写的头部长度
union {
__wsum csum; //校验和
struct {
__u16 csum_start; //校验和在数据包头部 skb->head 中的起始位置
__u16 csum_offset;//校验和保存到 csum_start 中的位置
};
};
__u32 priority; //数据包在队列中的优先级
__u8 local_df:1, //是否允许本地数据分段
cloned:1, //是否允许被克隆
ip_summed:2, //IP校验和标志
nohdr:1, //运载时使用,表示不能被修改头部
nfctinfo:3; //数据包连接关系
__u8 pkt_type:3, //数据包的类型
fclone:2, //数据包克隆关系
ipvs_property:1,//数据包所属的 ipvs
peeked:1, //数据包是否属于操作状态
nf_trace:1; //netfilter 对数据包的跟踪标志
__be16 protocol; //底层驱动使用的数据包协议
void (*destructor)(struct sk_buff *skb); //销毁数据包的函数
#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
struct nf_conntrack *nfct;
struct sk_buff *nfct_reasm;
#endif
#ifdef CONFIG_BRIDGE_NETFILTER
struct nf_bridge_info *nf_bridge; //关于网桥的数据
#endif
int iif;
#ifdef CONFIG_NETDEVICES_MULTIQUEUE
__u16 queue_mapping;
#endif
#ifdef CONFIG_NET_SCHED
__u16 tc_index; /* traffic control index */
#ifdef CONFIG_NET_CLS_ACT
__u16 tc_verd; /* traffic control verdict */
#endif
#endif
#ifdef CONFIG_IPV6_NDISC_NODETYPE
__u8 ndisc_nodetype:2;
#endif
/* 14 bit hole */
#ifdef CONFIG_NET_DMA
dma_cookie_t dma_cookie;
#endif
#ifdef CONFIG_NETWORK_SECMARK
__u32 secmark;
#endif
__u32 mark;
sk_buff_data_t transport_header; //指向数据块中传输层头部
sk_buff_data_t network_header; //指向数据块中网络层头部
sk_buff_data_t mac_header; //指向数据块中链路层头部
/* These elements must be at the end, see alloc_skb() for details. */
sk_buff_data_t tail; //指向数据块的结束地址
sk_buff_data_t end; //指向缓冲块的结束地址
unsigned char *head, //指向缓冲块的开始地址
*data; //指向数据块的开始地址
unsigned int truesize; //数据包的实际长度(结构长度与数据块长度之和)
atomic_t users; //数据包的使用计数器
};
共用部分 socket 结构体、通用部分 sock 结构体、专用部分 inet_sock 结构体。
tcp_sock 内容与 tcp 协议紧密相关,我们看其内容
struct tcp_sock {
/* inet_connection_sock has to be the first member of tcp_sock */
struct inet_connection_sock inet_conn; //由注释看到该结构体必须在 tcp_sock 头部 TODO why?
u16 tcp_header_len; /* Bytes of tcp header to send 发送的 tcp 头部字节数 */
u16 xmit_size_goal; /* Goal for segmenting output packets 分段传送的数据包数量 */
/*
* Header prediction flags 头部的预置位
* 0x5?10 << 16 + snd_wnd in net byte order
*/
__be32 pred_flags;
/*
* RFC793 variables by their proper names. This means you can
* read the code and the spec side by side (and laugh ...)
* See RFC793 and RFC1122. The RFC writes these in capitals.
*/
u32 rcv_nxt; /* What we want to receive next 下一个要接收的目标 */
u32 copied_seq; /* Head of yet unread data 代表还没有读取的数据 */
u32 rcv_wup; /* rcv_nxt on last window update sent rcv_nxt 在最后一次窗口更新时内容 */
u32 snd_nxt; /* Next sequence we send 下一个要发送的目标 */
u32 snd_una; /* First byte we want an ack for 第一个要 ack 的字节 */
u32 snd_sml; /* Last byte of the most recently transmitted small packet 最近发送数据包中的尾字节 */
u32 rcv_tstamp; /* timestamp of last received ACK (for keepalives) 最后一次接收到 ack 的时间 */
u32 lsndtime; /* timestamp of last sent data packet (for restart window) 最后一次发送数据包的时间 */
/* Data for direct copy to user 直接复制给用户的数据 */
struct {
struct sk_buff_head prequeue; //预处理队列
struct task_struct *task; //预处理进程
struct iovec *iov; //用户程序(应用程序)接收数据的缓冲区
int memory; //预处理数据包计数器
int len; //预处理长度
#ifdef CONFIG_NET_DMA
/* members for async copy 异步复制的内容 */
struct dma_chan *dma_chan;
int wakeup;
struct dma_pinned_list *pinned_list;
dma_cookie_t dma_cookie;
#endif
} ucopy;
u32 snd_wl1; /* Sequence for window update 窗口更新的顺序 */
u32 snd_wnd; /* The window we expect to receive 期望接收的窗口 */
u32 max_window; /* Maximal window ever seen from peer 从对方获得的最大窗口 */
u32 mss_cache; /* Cached effective mss, not including SACKS 有效的 mss,不包括 SACKS TODO mss、SACKS */
u32 window_clamp; /* Maximal window to advertise 对外公布的最大窗口 */
u32 rcv_ssthresh; /* Current window clamp 当前窗口 */
u32 frto_highmark; /* snd_nxt when RTO occurred 在 rto 时的 snd_nxt */
u8 reordering; /* Packet reordering metric. 预设的数据包数量 */
u8 frto_counter; /* Number of new acks after RTO rto 后的 ack 次数 */
u8 nonagle; /* Disable Nagle algorithm? 是否使用 Nagle 算法 TODO Nagle */
u8 keepalive_probes; /* num of allowed keep alive probes 允许持有的数量 */
/* RTT measurement */
u32 srtt; /* smoothed round trip time << 3 */
u32 mdev; /* medium deviation */
u32 mdev_max; /* maximal mdev for the last rtt period */
u32 rttvar; /* smoothed mdev_max */
u32 rtt_seq; /* sequence number to update rttvar */
u32 packets_out; /* Packets which are "in flight" 处于飞行中的数据包数量 */
u32 retrans_out; /* Retransmitted packets out 转发的数据包数量 */
/*
* Options received (usually on last packet, some only on SYN packets).
*/
struct tcp_options_received rx_opt;
/*
* Slow start and congestion control (see also Nagle, and Karn & Partridge) TODO 慢启动与阻塞控制
*/
u32 snd_ssthresh; /* Slow start size threshold 慢启动的起点值 */
u32 snd_cwnd; /* Sending congestion window 发送的阻塞窗口 */
u32 snd_cwnd_cnt; /* Linear increase counter 线性计数器 */
u32 snd_cwnd_clamp; /* Do not allow snd_cwnd to grow above this 不允许 snd_cwnd 超过的值 */
u32 snd_cwnd_used;
u32 snd_cwnd_stamp;
struct sk_buff_head out_of_order_queue; /* Out of order segments go here 超出分段规则的队列 */
u32 rcv_wnd; /* Current receiver window 当前接收窗口 */
u32 write_seq; /* Tail(+1) of data held in tcp send buffer tcp 发送数据的顺序号 */
u32 pushed_seq; /* Last pushed seq, required to talk to windows 最后送出的顺序号,需要通知窗口 */
/* SACKs data */
struct tcp_sack_block duplicate_sack[1]; /* D-SACK block */
struct tcp_sack_block selective_acks[4]; /* The SACKS themselves*/
struct tcp_sack_block recv_sack_cache[4];
struct sk_buff *highest_sack; /* highest skb with SACK received
* (validity guaranteed only if
* sacked_out > 0)
*/
/* from STCP, retrans queue hinting */
struct sk_buff* lost_skb_hint;
struct sk_buff *scoreboard_skb_hint;
struct sk_buff *retransmit_skb_hint;
struct sk_buff *forward_skb_hint;
int lost_cnt_hint;
int retransmit_cnt_hint;
u32 lost_retrans_low; /* Sent seq after any rxmit (lowest) */
u16 advmss; /* Advertised MSS */
u32 prior_ssthresh; /* ssthresh saved at recovery start */
u32 lost_out; /* Lost packets */
u32 sacked_out; /* SACK'd packets */
u32 fackets_out; /* FACK'd packets */
u32 high_seq; /* snd_nxt at onset of congestion */
u32 retrans_stamp; /* Timestamp of the last retransmit,
* also used in SYN-SENT to remember stamp of
* the first SYN. */
u32 undo_marker; /* tracking retrans started here. */
int undo_retrans; /* number of undoable retransmissions. */
u32 urg_seq; /* Seq of received urgent pointer */
u16 urg_data; /* Saved octet of OOB data and control flags */
u8 urg_mode; /* In urgent mode */
u8 ecn_flags; /* ECN status bits. */
u32 snd_up; /* Urgent pointer */
u32 total_retrans; /* Total retransmits for entire connection */
u32 bytes_acked; /* Appropriate Byte Counting - RFC3465 */
unsigned int keepalive_time; /* time before keep alive takes place */
unsigned int keepalive_intvl; /* time interval between keep alive probes */
int linger2;
unsigned long last_synq_overflow;
u32 tso_deferred;
/* Receiver side RTT estimation */
struct {
u32 rtt;
u32 seq;
u32 time;
} rcv_rtt_est;
/* Receiver queue space 接受队列空间 */
struct {
int space;
u32 seq;
u32 time;
} rcvq_space;
/* TCP-specific MTU probe information. TCP 指定的 MTU 检验内容 */
struct {
u32 probe_seq_start;
u32 probe_seq_end;
} mtu_probe;
#ifdef CONFIG_TCP_MD5SIG
/* TCP AF-Specific parts; only used by MD5 Signature support so far */
struct tcp_sock_af_ops *af_specific;
/* TCP MD5 Signagure Option information */
struct tcp_md5sig_info *md5sig_info;
#endif
};
demo
了解了一些数据结构之后,下面将正式开始介绍 socket 相关的源码。
我们先来看下正常的服务器使用的流程
int main()
{
struct sockaddr_in server_address;
struct sockaddr_in client_address;
server_fd = socket(AF_INET,SOCK_STREAM,0);
server_address.sin_family = AF_INET;
server_address.sin_addr.s_addr = inet_addr("192.168.1.1");
server_address.sin_port = htons(54188);
server_len = sizeof(server_address);
bind(server_fd,(struct sockaddr*)&server_address,server_len);
/*创建一个Socket的监听队列(允许接收10个连接),监听客户端Socket的连接请求*/
listen(server_fd,10);
while(1) {
char recv[20];
printf("server is waiting\n");
/*程序运行到此处时,说明客户端的连接请求已经到来,接受它的连接请求,克隆出一个Socket与客户端建立连接,并将客户端的“电话号码”记录在client_address中,函数返回建立连接的ID号*/
client_len = sizeof(client_address);
client_fd = accept(server_fd,(struct sockaddr*)&client_address,&client_len);
/*使用read和write函数接收客户端字符然后发回客户端*/
read(client_fd,recv,20);
write(client_fd,back,20);
printf("received from client= %s\n",recv);
close(client_fd);
}
close(server_fd);
exit(0);
}
无非先是 socket() 创建服务器socket ,然后bind() 将地址结构与 socket 挂钩起来,于是 listen()监听客户端的连接请求,然后通过accept()然后得到fd,根据vfs即访问文件的方式访问套接字,read/write。
socket 的创建
服务器调用socket()函数,其调用的库函数在glibc源码中找到
#include <errno.h>
#include <sys/socket.h>
/* Create a new socket of type TYPE in domain DOMAIN, using
protocol PROTOCOL. If PROTOCOL is zero, one is chosen automatically.
Returns a file descriptor for the new socket, or -1 for errors. */
int
__socket (domain, type, protocol)
int domain;
int type;
int protocol;
{
__set_errno (ENOSYS);
return -1;
}
weak_alias (__socket, socket)
stub_warning (socket)
#include <stub-tag.h>
这里看到使用 weak_alias() 函数为 socket() 函数声明了一个“函数别名”_socket(