您的位置:首页 > 理论基础 > 计算机网络

TCP/IP协议之struct sock结构体

2016-11-09 17:41 344 查看
注:TCP/IP系列协议分析都是基于linux 1.2.3版本。

————————————转载内容分割线———————————

sock结构体是我们在网络编程中遇到的第一个庞大的结构体:

struct sock

{

struct options opt;/*IP选项缓存于此处/

volatile unsigned long wmem_alloc;/当前写缓冲区大小,该值不可大于系统规定的最大值/

volatile unsigned long rmem_alloc;/当前读缓冲区大小,该值不可大于系统规定最大值/

unsigned long write_seq; /* write_seq 表示应用程序下一次写数据时所对应的第一个字节的序列号*/

unsigned long sent_seq; /* sent_seq 表示本地将要发送的下一个数据包中第一个字节对应的序列*/

unsigned long acked_seq;/* acked_seq 表示本地希望从远端接收的下一个数据的序列号*/

unsigned long copied_seq;/* 应用程序有待读取(但尚未读取)数据的第一个序列号。*/

unsigned long rcv_ack_seq;/* 表示目前本地接收到的对本地发送数据的应答序列号。*/

unsigned long window_seq;

/* 窗口大小,是一个绝对值,表示本地将要发送数据包中所包含最后一个数据的序列号,不可大于 window_seq.*/

unsigned long fin_seq;

/* 该字段在对方发送FIN数据包时使用,在接收到远端发送的FIN数据包后,fin_seq 被初始化为对方的

FIN 数据包最后一个字节的序列号加 1,表示本地对此 FIN 数据包进行应答的序列号*/

unsigned long urg_seq;

unsigned long urg_data;

/* 以上两个字段用于紧急数据处理,urg_seq 表示紧急数据最大序列号。urg_data 是一个标志位,

当设置为 1 时,表示接收到紧急数据。*/

volatile char inuse,/inuse=1 表示其它进程正在使用该 sock 结构,本进程需等待/

dead,/* dead=1 表示该 sock 结构已处于释放状态*/

urginline,/* urginline=1 表示紧急数据将被当作普通数据处理。*/

intr,

blog,/* blog=1 表示对应套接字处于节制状态,此时接收的数据包均被丢弃*/

done,

reuse,

keepopen,/* keepopen=1 表示使用保活定时器 */

linger,/* linger=1 表示在关闭套接字时需要等待一段时间以确认其已关闭。*/

delay_acks,/* delay_acks=1表示延迟应答,可一次对多个数据包进行应答 */

destroy,/* destroy=1 表示该 sock 结构等待销毁*/

ack_timed,

no_check,

zapped, /* In ax25 & ipx means not linked */

broadcast,

nonagle;/* noagle=1 表示不使用 NAGLE 算法*/

unsigned long lingertime;/表示等待关闭操作的时间,只有当 linger 标志位为 1 时,该字段才有意义。/

int proc;/* 该 sock 结构(即该套接字)所属的进程的进程号。*/

struct sock *next;

struct sock *prev;

struct sock *pair;

/* 以上三个字段用于 sock 的连接*/

struct sk_buff * volatile send_head;

struct sk_buff * volatile send_tail;

/* send_head, send_tail 用于 TCP协议重发队列。*/

struct sk_buff_head back_log; /* back_log为接收的数据包缓存队列。

用于计算目前累计的应发送而未发送的应答数据包的个数*/

struct sk_buff partial;/创建最大长度的待发送数据包。*/

struct timer_list partial_timer;/按时发送 partial 指针指向的数据包,以免缓存(等待)时间过长。/

long retransmits;/* 重发次数*/

/*

write_queue 指向待发送数据包,其与 send_head,send_tail 队列的不同之处在于send_head,send_tail 队列中数据包均已经发送出去,但尚未接收到应答。而 write_queue 中数据包尚未发送。 receive-queue为读队列,其不同于 back_log 队列之处在于 back_log 队列缓存从网络层传 上来的数据包,在用户进行读取操作时,不可操作 back_log 队列,而是从 receive_queue 队列中去数据包读取其中的数据,即数据包首先缓存在 back_log 队列中,然后从 back_log 队列中移动到 receive_queue队列中方可被应用程序读取。而并非所有back_log 队列中缓 存的数据包都可以成功的被移动到 receive_queue队列中,如果此刻读缓存区太小,则当 前从back_log 队列中被取下的被处理的数据包将被直接丢弃,而不会被缓存到receive_queue 队列中。如果从应答的角度看,在back_log队列中的数据包由于有可能被 丢弃,故尚未应答,而将一个数据包从 back_log 移动到 receive_queue时,表示该数据包 已被正式接收,即会发送对该数据包的应答给远端表示本地已经成功接收该数据包。 */

struct sk_buff_head write_queue,

receive_queue;

struct proto prot;/指向传输层处理函数集*/

struct wait_queue *sleep;/进程等待sock的地位*/

unsigned long daddr;/套接字的远端地址/

unsigned long saddr;/套接字的本地地址/

unsigned short max_unacked;/* 最大未处理请求连接数(应答数) */

unsigned short window;/* 远端窗口大小 */

unsigned short bytes_rcv;/* 已接收字节总数*/

/* mss is min(mtu, max_window) */

unsigned short mtu; /最大传输单元/

volatile unsigned short mss; /最大报文长度:MSS=MTU-IP 首部长度-TCP首部长度 /

volatile unsigned short user_mss; /用户指定的 MSS值/

volatile unsigned short max_window;

unsigned long window_clamp;/最大窗口大小和窗口大小钳制值 /

unsigned short num;/* 本地端口号*/

/*

以下三个字段用于拥塞算法

*/

volatile unsigned short cong_window;

volatile unsigned short cong_count;

volatile unsigned short ssthresh;

volatile unsigned short packets_out;/* 本地已发送出去但尚未得到应答的数据包数目*/

volatile unsigned short shutdown;/* 本地关闭标志位,用于半关闭操作*/

volatile unsigned long rtt;/* 往返时间估计值*/

volatile unsigned long mdev;/* mean deviation, 即RTTD, 绝对偏差*/

volatile unsigned long rto;/* RTO是用 RTT 和 mdev 用算法计算出的延迟时间值*/

volatile unsigned short backoff;/* 退避算法度量值 */

volatile short err;/* 错误标志值*/

unsigned char protocol;/* 传输层协议值*/

volatile unsigned char state;/* 套接字状态值,如 TCP_ESTABLISHED */

volatile unsigned char ack_backlog;/* 缓存的未应答数据包个数*/

unsigned char max_ack_backlog;/* 最大缓存的未应答数据包个数*/

unsigned char priority;/* 该套接字优先级,在硬件缓存发送数据包时使用 */

unsigned char debug;

unsigned short rcvbuf;/* 最大接收缓冲区大小*/

unsigned short sndbuf;/* 最大发送缓冲区大小*/

unsigned short type;/* 类型值如 SOCK_STREAM */

#ifdef CONFIG_IPX

ipx_address ipx_dest_addr;

ipx_interface *ipx_intrfc;

unsigned short ipx_port;

unsigned short ipx_type;

#endif

#ifdef CONFIG_AX25

ax25_address ax25_source_addr,ax25_dest_addr;

struct sk_buff *volatile ax25_retxq[8];

char ax25_state,ax25_vs,ax25_vr,ax25_lastrxnr,ax25_lasttxnr;

char ax25_condition;

char ax25_retxcnt;

char ax25_xx;

char ax25_retxqi;

char ax25_rrtimer;

char ax25_timer;

unsigned char ax25_n2;

unsigned short ax25_t1,ax25_t2,ax25_t3;

ax25_digi *ax25_digipeat;

#endif

#ifdef CONFIG_ATALK

struct atalk_sock at;

#endif

/* IP ‘private area’ or will be eventually */

int ip_ttl; /* IP首部 TTL 字段值,实际上表示路由器跳数*/

int ip_tos; /* IP首部 TOS字段值,服务类型值*/

struct tcphdr dummy_th;/* 缓存的 TCP首部,在 TCP协议中创建一个发送数据包时可以利用此字段

快速创建 TCP 首部。*/

struct timer_list keepalive_timer; /*保活定时器,用于探测对方窗口大小,防止对方通报窗口大小的

数据包丢弃,从而造成 本地发送通道被阻塞。*/

struct timer_list retransmit_timer; /重发定时器,用于数据包超时重发/

struct timer_list ack_timer; /*延迟应答定时器,延迟应答可以减少应答数据包的个数,但不可

无限延迟以免造成远端 重发,所以设置定时器定期发送应答数据包。 */

int ip_xmit_timeout; /该字段为标志位组合字段,用于表示下文中 timer定时器超时的原因/

#ifdef CONFIG_IP_MULTICAST

int ip_mc_ttl;

int ip_mc_loop;

char ip_mc_name[MAX_ADDR_LEN];

struct ip_mc_socklist *ip_mc_list;

#endif

/以上4 个字段用于 IP多播/

int timeout;

struct timer_list timer;

/* 以上两个字段用于通用定时,timeout 表示定时时间值,ip_xmit_timeout表示此次定时的 原因,timer为定时器。 */

struct timeval stamp;/* 时间戳*/

struct socket socket;/对应的socket结构体*/

void (*state_change)(struct sock *sk);

void (*data_ready)(struct sock *sk,int bytes);

void (*write_space)(struct sock *sk);

void (*error_report)(struct sock *sk);

/* 以上四个函数指针字段指向回调函数。这些字段的设置为自定义回调函数提供的很大的

灵活性,内核在发生某些时间时,会调用这些函数,如此可以实现自定义响应。目前这

种自定义响应还是完全有内核控制。 */

};

在inet_create 函数中,这个结构体的成员基本上都被初始化了

static int inet_create(struct socket *sock, int protocol)

{

……

switch(sock->type)

{

……

case SOCK_STREAM:

case SOCK_SEQPACKET:

/*

在 socket 系统调用时,我们一般将 protocol 参数设置为 0。如果设置为非 0,

则对于不同的类型,必须赋予正确值,否则可能在此处处理时出现问题。

*/

if (protocol && protocol != IPPROTO_TCP)

{

kfree_s((void *)sk, sizeof(*sk));

return(-EPROTONOSUPPORT);

}

protocol = IPPROTO_TCP;

/*

TCP_NO_CHECK定义为 1,表示对于 TCP协议默认使用校验

*/

sk->no_check = TCP_NO_CHECK;

/*

注意此处prot 变量被初始化为 tcp_prot,稍后 sock 结构的prot 字段将被初始

化为prot 变量值。

*/

prot = &tcp_prot;

break;

}

……

sk->socket = sock;/建立与其对应的 socket结构之间的关系,socket结构先于 sock 结构建立。 /

#ifdef CONFIG_TCP_NAGLE_OFF

sk->nonagle = 1;

#else

sk->nonagle = 0;

#endif

sk->type = sock->type;/初始化 sock 结构 type 字段:套接字类型/

sk->stamp.tv_sec=0;

sk->protocol = protocol;/传输层协议/

sk->wmem_alloc = 0;

sk->rmem_alloc = 0;

sk->sndbuf = SK_WMEM_MAX;/最大发送缓冲区大小/

sk->rcvbuf = SK_RMEM_MAX;/最大接收缓冲区大小/

sk->pair = NULL;

sk->opt = NULL;

sk->write_seq = 0;

sk->acked_seq = 0;

sk->copied_seq = 0;

sk->fin_seq = 0;

sk->urg_seq = 0;

sk->urg_data = 0;

sk->proc = 0;

sk->rtt = 0; /TCP_WRITE_TIME << 3;/

sk->rto = TCP_TIMEOUT_INIT; /TCP_WRITE_TIME/

sk->mdev = 0;

sk->backoff = 0;

sk->packets_out = 0;

/*

cong_window 设置为 1,即 TCP首先进入慢启动阶段。这是 TCP协议处理拥塞的 一种策略

*/

sk->cong_window = 1; /* start with only sending one packet at a time. */

sk->cong_count = 0;

sk->ssthresh = 0;

sk->max_window = 0;

sk->urginline = 0;

sk->intr = 0;

sk->linger = 0;

sk->destroy = 0;

sk->priority = 1;

sk->shutdown = 0;

sk->keepopen = 0;

sk->zapped = 0;

sk->done = 0;

sk->ack_backlog = 0;

sk->window = 0;

sk->bytes_rcv = 0;

sk->state = TCP_CLOSE;/由于尚未进行连接,状态设置为 CLOSE。/

sk->dead = 0;

sk->ack_timed = 0;

sk->partial = NULL;

sk->user_mss = 0;

sk->debug = 0;

/*

设置最大可暂缓应答的字节数

*/

/* this is how many unacked bytes we will accept for this socket. */

sk->max_unacked = 2048; /* needs to be at most 2 full packets. */

/* how many packets we should send before forcing an ack.

if this is set to zero it is the same as sk->delay_acks = 0 */

sk->max_ack_backlog = 0;

sk->inuse = 0;

sk->delay_acks = 0;

skb_queue_head_init(&sk->write_queue);

skb_queue_head_init(&sk->receive_queue);

sk->mtu = 576;/MTU设置为保守的576字节, 该大小在绝大多数连接中不会造成分片/

sk->prot = prot;

sk->sleep = sock->wait;

sk->daddr = 0;

sk->saddr = 0 /* ip_my_addr() */;

sk->err = 0;

sk->next = NULL;

sk->pair = NULL;

sk->send_tail = NULL;

sk->send_head = NULL;

sk->timeout = 0;

sk->broadcast = 0;

sk->localroute = 0;

init_timer(&sk->timer);

init_timer(&sk->retransmit_timer);

sk->timer.data = (unsigned long)sk;

sk->timer.function = &net_timer;

skb_queue_head_init(&sk->back_log);

sk->blog = 0;

sock->data =(void *) sk;

/sock 结构之 dummy_th 字段是 tcphdr结构,该结构与 TCP首部各字段对应/

sk->dummy_th.doff = sizeof(sk->dummy_th)/4;

sk->dummy_th.res1=0;

sk->dummy_th.res2=0;

sk->dummy_th.urg_ptr = 0;

sk->dummy_th.fin = 0;

sk->dummy_th.syn = 0;

sk->dummy_th.rst = 0;

sk->dummy_th.psh = 0;

sk->dummy_th.ack = 0;

sk->dummy_th.urg = 0;

sk->dummy_th.dest = 0;

sk->ip_tos=0;

sk->ip_ttl=64;

#ifdef CONFIG_IP_MULTICAST

sk->ip_mc_loop=1;

sk->ip_mc_ttl=1;

*sk->ip_mc_name=0;

sk->ip_mc_list=NULL;

#endif

/*

对 sock 结构中几个回调函数字段的初始化

*/

sk->state_change = def_callback1;

sk->data_ready = def_callback2;

sk->write_space = def_callback3;

sk->error_report = def_callback1;

/*

如果该套接字已经分配本地端口号,则对 sock 结构中 dummy_th 结构字段进行赋值

*/

if (sk->num)

{

/*

* It assumes that any protocol which allows

* the user to assign a number at socket

* creation time automatically

* shares.

*/

put_sock(sk->num, sk);

sk->dummy_th.source = ntohs(sk->num);

}

……

return(0);

}

此后,数据的传送,都由sock结构体作为数据的携带者,传送给下层的传输层,网络层,链路层,链路层。

文章转载至:

http://www.cnblogs.com/image-eye/archive/2012/01/05/2313383.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  tcp-ip sock struct-soc