Chinaunix首页 | 论坛 | 博客
  • 博客访问: 149336
  • 博文数量: 69
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 602
  • 用 户 组: 普通用户
  • 注册时间: 2014-12-25 20:56
文章分类

全部博文(69)

文章存档

2015年(68)

2014年(1)

我的朋友

分类: Android平台

2015-01-19 23:50:54

在看listen的代码之前.我们也先来看相关的数据结构: 
inet_connection_sock它包含了一个icsk_accept_queue的域,这个域是一个request_sock_queue类型,.我们就先来看这个结构: 

request_sock_queue也就表示一个request_sock队列.这里我们知道,tcp中分为半连接队列(处于SYN_RECVD状态)和已完成连接队列(处于established状态).这两个一个是刚接到syn,等待三次握手完成,一个是已经完成三次握手,等待accept来读取. 

这里每个syn分节到来都会新建一个request_sock结构,并将它加入到listen_sock的request_sock hash表中.然后3次握手完毕后,将它放入到request_sock_queue的rskq_accept_head和rskq_accept_tail队列中.这样当accept的时候就直接从这个队列中读取了.

  1. struct request_sock_queue {   
  2. ///一个指向头,一个指向结尾.  
  3.      struct request_sock *rskq_accept_head;   
  4.      struct request_sock *rskq_accept_tail;   
  5.      rwlock_t         syn_wait_lock;   
  6.      u8           rskq_defer_accept;   
  7.     /* 3 bytes hole, try to pack */  
  8. ///相应的listen_socket结构.  
  9.      struct listen_sock   *listen_opt;   
  10. };  
  11. struct listen_sock {   

  12.      u8           max_qlen_log;   
  13. /*qlen最大长度取对数log,即log2 (max_qlen),这个值在进入SYN/ACK定时器时有用*/
  14.     /* 3 bytes hole, try to use */    
  15.     int          qlen;   
  16. ??///当前的半连接队列的长度.
  17.     int          qlen_young;   
  18. /*?也是指当前的半开连接队列长度,不过这个值会当重传syn/ack的时候(这里要注意是这个syn/ack第一次重传的时候才会减一)自动减
  19. 一,也就是重传了SYN/ACK的request_sock不在是新的request_sock,在SYN/ACK定时器时有用*/
  20.     int          clock_hand;   
  21. /*每次SYN-ACK定时器超时时,我们需要遍历SYN队列哈希表,但表太大了,所以每次都只遍历部分哈希表,而每次遍历完,将哈希索引值放在clock_hand这里,下次遍历时直接从clock_hand开始,而不用从头开始*/
  22.      u32          hash_rnd;   
  23. ///这个值表示了当前的syn_backlog(半开连接队列)的最大值  
  24.      u32          nr_table_entries;   
  25. ///半连接队列.  
  26.      struct request_sock *syn_table[0];   
  27. };  

request_sock的结构就不在这里贴出来了,我们只要知道每一个SYN请求都会新建一个request_sock结构,并将它加入到listen_sock的syn_table哈希表中,然后接收端会发送一个SYN/ACK段给SYN请求端,当SYN请求端将3次握手的最后一个ACK发送给接收端后,并且接收端判断ACK正确,则将request_sock从syn_table哈希表中删除,将request_sock加入到request_sock_queue的rskq_accept_head和rskq_accept_tail队列中,最后的accept系统调用不过是判断accept队列是否存在完成3次请求的request_sock,从这个队列中将request_sock结构释放,然后在BSD层新建一个socket结构,并将它和接收端新建的子sock结构关联起来。

我们可以想到,listen系统调用必然要分配一个listen_sock结构,其实也正如此,inet_listen系统调用最终会调用?inet_csk_listen_start函数,

它的主要工作是新分配一个listen socket,将它加入到inet_connection_sock的icsk_accept_queue域的listen_opt中.然后对当前使用端口进行判断.最终返回,

  1. int inet_csk_listen_start(struct sock *sk, const int nr_table_entries)   
  2. {   
  3.      struct inet_sock *inet = inet_sk(sk);   
  4.      struct inet_connection_sock *icsk = inet_csk(sk);   
  5. ///新分配一个listen_sock,并让request_sock_queue->listen_opt指向它
  6.     int rc = reqsk_queue_alloc(&icsk->icsk_accept_queue, nr_table_entries);   
  7.   
  8.     if (rc != 0)   
  9.         return rc;   
  10. ///先将这两个ack_backlog赋值为0.  
  11.      sk->sk_max_ack_backlog = 0;   
  12.      sk->sk_ack_backlog = 0;   
  13.      inet_csk_delack_init(sk);   
  14.   
  15.     /* There is race window here: we announce ourselves listening,
  16.       * but this transition is still not validated by get_port().
  17.       * It is OK, because this socket enters to hash table only
  18.       * after validation is complete.
  19.       */  
  20. ///设置状态.  
  21.      sk->sk_state = TCP_LISTEN;   
  22. ///get_port上面已经分析过了.这里之所以还要再次判断一下端口,是为了防止多线程,也就是另一个线程在我们调用listen之前改变了这个端口的信息.  
  23.     if (!sk->sk_prot->get_port(sk, inet->num)) {   
  24. //端口可用的情况,将端口值付给sport,并加入到inet_hashinfo(上面已经分析过)的listening_hash hash链表中.  
  25.          inet->sport = htons(inet->num);   
  26.   
  27.          sk_dst_reset(sk);   
  28. ///这里调用__inet_hash实现的.  
  29.          sk->sk_prot->hash(sk);   
  30.   
  31.         return 0;   
  32.      }   
  33. ///不可用,则返回错误.  
  34.      sk->sk_state = TCP_CLOSE;   
  35.      __reqsk_queue_destroy(&icsk->icsk_accept_queue);   
  36.     return -EADDRINUSE;   
  37. }  

inet_accept系统调用最终会调用inet_csk_accept,?inet_csk_accept调用reqsk_queue_get_child从accept队列中取一个request_sock,得到request_sock对应的sock,然后将request_scok删除,这里的目的主要是为了得到request_soc的对应的sock,还在BSD层生成相应的sock结构,?reqsk_queue_get_child代码如下(2.6.32内核):

static inline struct sock *reqsk_queue_get_child(struct request_sock_queue *queue,
       struct sock *parent)
{
struct request_sock *req = reqsk_queue_remove(queue);
struct sock *child = req->sk;

WARN_ON(child == NULL);

sk_acceptq_removed(parent);
__reqsk_free(req);
return child;
}

阅读(852) | 评论(0) | 转发(0) |
0

上一篇:VFS和BSD

下一篇:vector和list区别

给主人留下些什么吧!~~