struct socket 结构详解

Socket数据结构网络协议CC++

用户使用socket系统调用编写应用程序时,通过一个数字来表示一个socket,所有的操作都在该数字上进行,这个数字称为套接字描述符。在系统调用 的实现函数里,这个数字就会被映射成一个表示socket的结构体,该结构体保存了该socket的所有属性和数据。在内核的协议中实现中,关于表示 socket的结构体,是一个比较复杂的东西,下面一一介绍。 
    struct socket。 
    这是一个基本的BSD socket,我们调用socket系统调用创建的各种不同类型的socket,开始创建的都是它,到后面,各种不同类型的socket在它的基础上进行 各种扩展。struct socket是在虚拟文件系统上被创建出来的,可以把它看成一个文件,是可以被安全地扩展的。下面是其完整定义:

C代码  

  1. struct socket {
  2. socket_state            state;
  3. unsigned long           flags;
  4. const struct proto_ops *ops;
  5. struct fasync_struct    *fasync_list;
  6. struct file             *file;
  7. struct sock             *sk;
  8. wait_queue_head_t       wait;
  9. short                   type;
  10. };

state用于表示socket所处的状态,是一个枚举变量,其类型定义如下:

C代码  

  1. typedef enum {
  2. SS_FREE = 0,            //该socket还未分配
  3. SS_UNCONNECTED,         //未连向任何socket
  4. SS_CONNECTING,          //正在连接过程中
  5. SS_CONNECTED,           //已连向一个socket
  6. SS_DISCONNECTING        //正在断开连接的过程中
  7. }socket_state;

该成员只对TCP socket有用,因为只有tcp是面向连接的协议,udp跟raw不需要维护socket状态。 
    flags是一组标志位,在内核中并没有发现被使用。 
    ops是协议相关的一组操作集,结构体struct proto_ops的定义如下:

C代码  

  1. struct proto_ops {
  2. int     family;
  3. struct module   *owner;
  4. int (*release)(struct socket *sock);
  5. int (*bind)(struct socket *sock, struct sockaddr *myaddr, int sockaddr_len);
  6. int (*connect)(struct socket *sock, struct sockaddr *vaddr, int sockaddr_len, int flags);
  7. int (*socketpair)(struct socket *sock1, struct socket *sock2);
  8. int (*accept)(struct socket *sock,struct socket *newsock, int flags);
  9. int (*getname)(struct socket *sock, struct sockaddr *addr,int *sockaddr_len, int peer);
  10. unsigned int (*poll)(struct file *file, struct socket *sock,
  11. struct poll_table_struct *wait);
  12. int (*ioctl)(struct socket *sock, unsigned int cmd, unsigned long arg);
  13. int (*listen)(struct socket *sock, int len);
  14. int (*shutdown)(struct socket *sock, int flags);
  15. int (*setsockopt)(struct socket *sock, int level,
  16. int optname, char __user *optval, int optlen);
  17. int (*getsockopt)(struct socket *sock, int level,
  18. int optname, char __user *optval, int __user *optlen);
  19. int (*sendmsg)(struct kiocb *iocb, struct socket *sock,
  20. struct msghdr *m, size_t total_len);
  21. int (*recvmsg)(struct kiocb *iocb, struct socket *sock,
  22. struct msghdr *m, size_t total_len, int flags);
  23. int (*mmap)(struct file *file, struct socket *sock,struct vm_area_struct * vma);
  24. ssize_t (*sendpage)(struct socket *sock, struct page *page,
  25. int offset, size_t size, int flags);
  26. };

协议栈中总共定义了三个strcut proto_ops类型的变量,分别是myinet_stream_ops, myinet_dgram_ops, myinet_sockraw_ops,对应流协议, 数据报和原始套接口协议的操作函数集。 
    type是socket的类型,对应的取值如下:

C代码  

  1. enum sock_type {
  2. SOCK_DGRAM = 1,
  3. SOCK_STREAM = 2,
  4. SOCK_RAW    = 3,
  5. SOCK_RDM    = 4,
  6. SOCK_SEQPACKET = 5,
  7. SOCK_DCCP   = 6,
  8. SOCK_PACKET = 10,
  9. };

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更为精细的状态,其可能的取值如下:

C代码  

  1. enum {
  2. TCP_ESTABLISHED = 1,
  3. TCP_SYN_SENT,
  4. TCP_SYN_RECV,
  5. TCP_FIN_WAIT1,
  6. TCP_FIN_WAIT2,
  7. TCP_TIME_WAIT,
  8. TCP_CLOSE,
  9. TCP_CLOSE_WAIT,
  10. TCP_LAST_ACK,
  11. TCP_LISTEN,
  12. TCP_CLOSING,
  13. TCP_MAX_STATES
  14. ;

这些取值从名字上看,似乎只使用于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的字节数总和不能超过缓冲区大小的设定。

接着上一篇,继续介绍struct sock。 
    sk_rmem_alloc, sk_wmem_alloc和sk_omem_alloc分别表示接收缓冲队列,发送缓冲队列及其它缓冲队列中已经分配的字节数,用于跟踪缓冲区的使用情况。 
    struct sock有一个struct sock_common成员,因为struct inet_timewait_sock也要用到它,所以把它单独归到一个结构体中,其定义如下:

C代码  

  1. struct sock_common {
  2. unsigned short      skc_family;
  3. volatile unsigned char skc_state;
  4. unsigned char       skc_reuse;
  5. int         skc_bound_dev_if;
  6. struct hlist_node   skc_node;
  7. struct hlist_node   skc_bind_node;
  8. atomic_t        skc_refcnt;
  9. unsigned int        skc_hash;
  10. struct proto        *skc_prot;
  11. };

struct inet_sock。 
    这是INET域专用的一个socket表示,它是在struct sock的基础上进行的扩展,在基本socket的属性已具备的基础上,struct inet_sock提供了INET域专有的一些属性,比如TTL,组播列表,IP地址,端口等,下面是其完整定义:

C代码  

  1. struct inet_sock {
  2. struct sock     sk;
  3. #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
  4. struct ipv6_pinfo   *pinet6;
  5. #endif
  6. __u32           daddr;          //IPv4的目的地址。
  7. __u32           rcv_saddr;      //IPv4的本地接收地址。
  8. __u16           dport;          //目的端口。
  9. __u16           num;            //本地端口(主机字节序)。
  10. __u32           saddr;          //发送地址。
  11. __s16           uc_ttl;         //单播的ttl。
  12. __u16           cmsg_flags;
  13. struct ip_options   *opt;
  14. __u16           sport;          //源端口。
  15. __u16           id;             //单调递增的一个值,用于赋给iphdr的id域。
  16. __u8            tos;            //服务类型。
  17. __u8            mc_ttl;         //组播的ttl
  18. __u8            pmtudisc;
  19. __u8            recverr:1,
  20. is_icsk:1,
  21. freebind:1,
  22. hdrincl:1,      //是否自己构建ip首部(用于raw协议)
  23. mc_loop:1;      //组播是否发向回路。
  24. int             mc_index;       //组播使用的本地设备接口的索引。
  25. __u32           mc_addr;        //组播源地址。
  26. struct ip_mc_socklist   *mc_list;   //组播组列表。
  27. struct {
  28. unsigned int        flags;
  29. unsigned int        fragsize;
  30. struct ip_options   *opt;
  31. struct rtable       *rt;
  32. int                 length;
  33. u32                 addr;
  34. struct flowi        fl;
  35. } cork;
  36. };

struct raw_sock 
    这是RAW协议专用的一个socket的表示,它是在struct inet_sock基础上的扩展,因为RAW协议要处理ICMP协议的过滤设置,其定义如下:

C代码  

  1. struct raw_sock {
  2. struct inet_sock   inet;
  3. struct icmp_filter filter;
  4. };

struct udp_sock 
    这是UDP协议专用的一个socket表示,它是在struct inet_sock基础上的扩展,其定义如下:

C代码  

  1. struct udp_sock {
  2. struct inet_sock inet;
  3. int             pending;
  4. unsigned int    corkflag;
  5. __u16           encap_type;
  6. __u16           len;
  7. };

struct inet_connection_sock 
    看完上面两个,我们觉得第三个应该就是struct tcp_sock了,但事实上,struct tcp_sock并不直接从struct inet_sock上扩展,而是从struct inet_connection_sock基础上进行扩展,struct inet_connection_sock是所有面向连接的socket的表示,关于该socket,及下面所有tcp相关的socket,我们在分析 tcp实现时再详细介绍,这里只列出它们的关系。

strcut tcp_sock 
    这是TCP协议专用的一个socket表示,它是在struct inet_connection_sock基础进行扩展,主要是增加了滑动窗口协议,避免拥塞算法等一些TCP专有属性。

struct inet_timewait_sock

struct tcp_timewait_sock 
    在struct inet_timewait_sock的基础上进行扩展。

struct inet_request_sock

struct tcp_request_sock 
    在struct inet_request_sock的基础上进行扩展。

时间: 2024-10-25 19:42:26

struct socket 结构详解的相关文章

struct socket结构体详解

在内核中为什么要有struct socket结构体呢?    struct socket结构体的作用是什么?    下面这个图,我觉得可以回答以上两个问题.      由这个图可知,内核中的进程可以通过使用struct socket结构体来访问linux内核中的网络系统中的传输层.网络层.数据链路层.也可以说struct socket是内核中的进程与内核中的网路系统的桥梁.   struct socket {      socket_state  state; // socket state  

PHP扩展代码结构详解

PHP扩展代码结构详解: 这个是继:使用ext_skel和phpize构建php5扩展  内容 (拆分出来) Zend_API:深入_PHP_内核:http://cn2.php.net/manual/zh/internals2.ze1.php 我们使用ext_skel创建扩展 hello_module,该模块包含一个方法:hello_world. 使用ext_skel 生成的代码都是PHP_开头的宏, 而不是ZEND_开头. 实际上这两者是一样的. 在源代码src/main/PHP.h 中发现:

socket API详解

send函数 int send( SOCKET s,   const char FAR *buf,   int len,   int flags ); 不论是客户还是服务器应用程序都用send函数来向TCP连接的另一端发送数据. 客户程序一般用send函数向服务器发送请求,而服务器则通常用send函数来向客户程序发送应答. 该函数的第一个参数指定发送端套接字描述符: 第二个参数指明一个存放应用程序要发送数据的缓冲区: 第三个参数指明实际要发送的数据的字节数: 第四个参数一般置0. 这里只描述同步

Socket模型详解(转)

Socket模型详解(转) Socket模型详解 两种I/O模式 一.选择模型 二.异步选择 三.事件选择 四.重叠I/O模型 五.完成端口模型 五种I/O模型的比较 两种I/O模式 1. 两种I/O模式 阻塞模式:执行I/O操作完成前会一直进行等待,不会将控制权交给程序.套接字默认为阻塞模式.可以通过多线程技术进行处理. 非阻塞模式:执行I/O操作时,Winsock函数会返回并交出控制权.这种模式使用起来比较复杂,因为函数在没有运行完成就进行返回,会不断地返回 WSAEWOULDBLOCK错误

Linux的SOCKET编程详解(转)

Linux的SOCKET编程详解 1. 网络中进程之间如何通信 进 程通信的概念最初来源于单机系统.由于每个进程都在自己的地址范围内运行,为保证两个相互通信的进 程之间既互不干扰又协调一致工作,操作系统为进程通信提供了相应设施,如 UNIX BSD有:管道(pipe).命名管道(named pipe)软中断信号(signal) UNIX system V有:消息(message).共享存储区(shared memory)和信号量(semaphore)等. 他们都仅限于用在本机进程之间通信.网间进

socket原理详解

1.什么是socket 我们知道进程通信的方法有管道.命名管道.信号.消息队列.共享内存.信号量,这些方法都要求通信的两个进程位于同一个主机.但是如果通信双方不在同一个主机又该如何进行通信呢?在计算机网络中我们就学过了tcp/ip协议族,其实使用tcp/ip协议族就能达到我们想要的效果,如下图(图片来源于<tcp/ip协议详解卷一>第一章1.3) .  图一 各协议所处层次 当然,这样做固然是可以的,但是,当我们使用不同的协议进行通信时就得使用不同的接口,还得处理不同协议的各种细节,这就增加了

c/c++ socket函数详解

c/c++ socket函数详解 注意: 使用socketAPI前,要先将相关链接库(Ws2_32.lib)加入链接,并使用WSAStartUp函数初始化.每个socket函数都可能失败(返回-1),需要判断结果 socket分成两种: 一种专门用来监听新链接(或新活动),这种socket叫做master socket,一般只存在于服务器 一种专门用来收发数据,这种socket叫做connected socket,客户端和服务器都存在 int socket(int af,int type,int

logback教程(2) Logback结构详解

长话短说:LogBack的结构 为了适用不同的环境,logback的基础结构符合常规. logback分为三个模块: logback-core,logback-classic以及logback-access. 1 >核心模块(core)为其他两个模块提供基础. 2 >classic模块继承自core.classic模块很明相当于log4j的增强版. 3 >Logback-classic原生的继承自SLF4J API因此你可以很容易的在LogBack和其他像日志系统比如log4j或java

Socket 死连接详解

Socket 死连接详解 当使用 Socket 进行通信时,由于各种不同的因素,都有可能导致死连接停留在服务器端,假如服务端需要处理的连接较多,就有可能造成服务器资源严重浪费,对此,本文将阐述其原理以及解决方法. 在写 Socket 进行通讯时,我们必须预料到各种可能发生的情况并对其进行处理,通常情况下,有以下两种情况可能造成死连接: 通讯程序编写不完善 网络/硬件故障 a) 通讯程序编写不完善 这里要指出的一点就是,绝大多数程序都是由于程序编写不完善所造成的死连接,即对 Socket 未能进行