用戶使用socket系統調用編寫應用程序時,經過一個數字來表示一個socket,全部的操做都在該數字上進行,這個數字稱爲套接字描述符。在系統調用 的實現函數裏,這個數字就會被映射成一個表示socket的結構體,該結構體保存了該socket的全部屬性和數據。在內核的協議中實現中,關於表示 socket的結構體,是一個比較複雜的東西,下面一一介紹。
struct socket。
這是一個基本的BSD socket,咱們調用socket系統調用建立的各類不一樣類型的socket,開始建立的都是它,到後面,各類不一樣類型的socket在它的基礎上進行 各類擴展。struct socket是在虛擬文件系統上被建立出來的,能夠把它當作一個文件,是能夠被安全地擴展的。下面是其完整定義:
struct socket {
socket_state state;
unsigned long flags;
const struct proto_ops *ops;
struct fasync_struct *fasync_list;
struct file *file;
struct sock *sk;
wait_queue_head_t wait;
short type;
};
state用於表示socket所處的狀態,是一個枚舉變量,其類型定義以下:
typedef enum {
SS_FREE = 0, //該socket還未分配
SS_UNCONNECTED, //未連向任何socket
SS_CONNECTING, //正在鏈接過程當中
SS_CONNECTED, //已連向一個socket
SS_DISCONNECTING //正在斷開鏈接的過程當中
}socket_state;
該成員只對TCP socket有用,由於只有tcp是面向鏈接的協議,udp跟raw不須要維護socket狀態。
flags是一組標誌位,在內核中並無發現被使用。
ops是協議相關的一組操做集,結構體struct proto_ops的定義以下:
struct proto_ops {
int family;
struct module *owner;
int (*release)(struct socket *sock);
int (*bind)(struct socket *sock, struct sockaddr *myaddr, int sockaddr_len);
int (*connect)(struct socket *sock, struct sockaddr *vaddr, int sockaddr_len, int flags);
int (*socketpair)(struct socket *sock1, struct socket *sock2);
int (*accept)(struct socket *sock,struct socket *newsock, int flags);
int (*getname)(struct socket *sock, struct sockaddr *addr,int *sockaddr_len, int peer);
unsigned int (*poll)(struct file *file, struct socket *sock,
struct poll_table_struct *wait);
int (*ioctl)(struct socket *sock, unsigned int cmd, unsigned long arg);
int (*listen)(struct socket *sock, int len);
int (*shutdown)(struct socket *sock, int flags);
int (*setsockopt)(struct socket *sock, int level,
int optname, char __user *optval, int optlen);
int (*getsockopt)(struct socket *sock, int level,
int optname, char __user *optval, int __user *optlen);
int (*sendmsg)(struct kiocb *iocb, struct socket *sock,
struct msghdr *m, size_t total_len);
int (*recvmsg)(struct kiocb *iocb, struct socket *sock,
struct msghdr *m, size_t total_len, int flags);
int (*mmap)(struct file *file, struct socket *sock,struct vm_area_struct * vma);
ssize_t (*sendpage)(struct socket *sock, struct page *page,
int offset, size_t size, int flags);
};
協議棧中總共定義了三個strcut proto_ops類型的變量,分別是myinet_stream_ops, myinet_dgram_ops, myinet_sockraw_ops,對應流協議, 數據報和原始套接口協議的操做函數集。
type是socket的類型,對應的取值以下:
enum sock_type {
SOCK_DGRAM = 1,
SOCK_STREAM = 2,
SOCK_RAW = 3,
SOCK_RDM = 4,
SOCK_SEQPACKET = 5,
SOCK_DCCP = 6,
SOCK_PACKET = 10,
};
sk是網絡層對於socket的表示,結構體struct sock比較龐大,這裏不詳細列出,只介紹一些重要的成員,
sk_prot和sk_prot_creator,這兩個成員指向特定的協議處理函數集,其類型是結構體struct proto,該結構體也是跟struct proto_ops類似的一組協議操做函數集。這二者之間的概念彷佛有些混淆,能夠這麼理解,struct proto_ops的成員操做struct socket層次上的數據,處理完了,再由它們調用成員sk->sk_prot的函數,操做struct sock層次上的數據。即它們之間存在着層次上的差別。struct proto類型的變量在協議棧中總共也有三個,分別是mytcp_prot,myudp_prot,myraw_prot,對應TCP, UDP和RAW協議。
sk_state表示socket當前的鏈接狀態,是一個比struct socket的state更爲精細的狀態,其可能的取值以下: enum {
TCP_ESTABLISHED = 1,
TCP_SYN_SENT,
TCP_SYN_RECV,
TCP_FIN_WAIT1,
TCP_FIN_WAIT2,
TCP_TIME_WAIT,
TCP_CLOSE,
TCP_CLOSE_WAIT,
TCP_LAST_ACK,
TCP_LISTEN,
TCP_CLOSING,
TCP_MAX_STATES
};
這些取值從名字上看,彷佛只使用於TCP協議,但事實上,UDP和RAW也借用了其中一些值,在一個socket建立之初,其取值都是 TCP_CLOSE,一個UDP socket connect完成後,將這個值改成TCP_ESTABLISHED,最後,關閉sockt前置回TCP_CLOSE,RAW也同樣。
sk_rcvbuf和sk_sndbuf分別表示接收和發送緩衝區的大小。sk_receive_queue和sk_write_queue分別爲接收緩 衝隊列和發送緩衝隊列,隊列裏排列的是套接字緩衝區struct sk_buff,隊列中的struct sk_buff的字節數總和不能超過緩衝區大小的設定。安全