-
Notifications
You must be signed in to change notification settings - Fork 0
/
传输层实现源码分析.txt
1798 lines (1653 loc) · 53.8 KB
/
传输层实现源码分析.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
传输层笔记
tcp_set_state(struct sock *sk,int state)
{
如果sk->state==TCP_ESTABLISHED
tcp_statistics.TcpCurrEstab--;
如果新的state为TCP_ESTABLISHED以及sk->state状态是TCP_SYN_RECV
唤醒master_sleetc_wakeup
sk->state=state//sk->state设置新的state
判断若开始减了tcp_statistics.TcpCurrEstab++
}
tcp窗口大小选择函数
int tcp_select_window(struct sock*sk)
{
new_window如果sk->window_clamp不为0则取其sk->prot->rspace(sk)两者小值
if(new_windo<min(sk->mss,MAX_WINDOW/2)||new_window<sk->window)
return(sk->window)
return(new_window);
}
完成套接字建立连接工作
struct sk_buff*tcp_find_established(struct sock*s)
{
p=skb_peek(s->recvive_queue);
while循环查找p->sk->state==TCP_ESTABLISHED
return p;
return NULL;
}
找一个skb对应的sk是esatblished的skb
struct sk_buff*tcp_dequeue_established(struct sock*s)
{
skb=tcp_find_established(s);
如果skb!=NULL skb_unlink(skb);
return skb;
}
accept传输层实现
struct sock*tcp_accept(struct sock*sk,int flags )
{
判断sk->state必须为TCP_LISTEN否则报错返回
sk->inuse=1;
while(skb==tcp_dequeue_established(sk))
{
可中断睡眠在sk->sleep;
}
newsk=skb->sk;
kfree_skb(skb,FREE_READ);
sk->ack_backlog--;
return newsk;//返回是新的sock
}
关闭待连接的套接字(收到对端发送的SYN但是没有调用accept接受)
void tcp_close_pending(struct sock*sk)
{
while(skb=skb_dequeue(&sk->recvive_queue)!=NULL)
{
skb->sk->dead=1;
tcp_close(skb->skb,0);
kfree_skb(skb,FREE_READ);
}
return ;
}
设置本地socket进入TIME_WAIT状态,并设置定时器
void tcp_time_wait(struct sock*sk)
{
tcp_set_state(sk,TCP_TIME_WAIT);
设置sk->shutdown为SHUTDOWEN_MASK
如果未设置sk->dead标志位则sk->state_change(sk);
reset_msl_timer(sk,TIME_CLOSE,TCP_TIMEWAIT_LEN);
}
tcp_do_retransimit处理具体的重传工作
reset_msl_timer重新设置超时定时器
tcp_retransmit_time使用指数退避算法计算重传间隔时间
调用reset_xmit_timer重新设置定时器,调用tcp_do_restransimit做具体的重传工作
tcp_retransmit调用tcp_retransmit_time完成重传工作,采用算法重传以及判断多久放弃
tcp_write_timeout处理超时重传对本地的影响限制重传是次数
retransmit_timer超时重传处理总入口函数
具体重传
void tcp_do_retransimit(struct sock*sk,int all)
{
取下sk->send_head的while循环skb调用dev_queue_xmit(skb,dev,sk->priority);
sk->prot->retransmits++;ct++;
if(!all)
break;跳出while循环表示一次重传一个数据包
拥塞判断
if(ct>=sk->cong_window)//(ct表示重传包数while循环里++)
break、、跳出while循环。
//找到下一个skb
skb=skb->link3;//注意link3用于send_head队列数据包的相互连接
send_head指向队列头,send_tail指向队列尾
}
sk->retransmit_timer用于普通tcp包超时重传
重置定时器
void reset_xmit_timer(struct sock*sk,int why,unsigned long when )
{
删除原有定时器sk->retransmit_timer
sk->ip_xmit_timeout=why
使用when更新定时器时间添加新的定时器sk->retransmit_timer
}
void tcp_retransmit_time(struct sock*sk,int all)
{
//调用重传函数
tcp_do_retransimit(sk,all);
//更新计数
sk->retransmits++;
sk->backoff++;
sk->rto=min(sk->rto<<1,120*HZ);
//更新定时器
reset_xmit_timer(sk,TIME_WRITE,sk->rto);
}
void retransmit_timer(unsigned long data)
{
sk=(struct sock*)data;
why=sk->ip_xmit_timeout;
ssk->inuse=1;
switch(why)
{
case TIME_PROBE0:
tcp_send_probe0(sk);
tcp_write_timeout(sk);//判定重传次数决定是否取消定时器
break;
case TIME_WRITE:
skb=sk->send_head;
判断若未超时reset_xmit_timer(sk,TIME_WRITE,skb->when+sk->rto-jiffies)
sk->prot->retransmit(sk,0);
tcp_write_timeout(sk);
brewk;
case TIME_KEEPOPEN:
reset_xmit_timer(sk,TIME_KEEPOPEN,TCP_TIMEOU_LEN);
sk->retransmit++;
tcp_write_timeout(sk);
break;
default:
break;
release_sock(sk);
}
}
void tcp_err(int err,unsigned char*header,unsigned long daddr
unsigned long saddr,struct inet_protocol*protocol)
{
若错误号小于0表示错误严重,将错误号存储于sock->err,调用sk->error_report(sk);
若是源端节制错误 ,则减少拥塞窗口sk->cong_window--
若错误致命,则将错误存储起来不立刻通知用户,用户后续操作会被通知
若错误发生在建联过程中,则终止连接,即使通知用户
sk->state==TCP_SYN_SENT
tcp_set_state(sk,TCP_CLOSE);
sk->error_report(sk);
}
当前用户可读数据量探测
int tcp_readable(struct sock*sk)
{
skb=skb_peek(&sk->recvive_queue);
counted=sk->copied->seq;
while(skb!=sk->recvive_queue)
{
if(before(counted,skb->h.th->seq))(在他之前就是出现了断裂,正常counted>=skb->h.th->seq)
break;
sum=skb->len-(counted-skb->h.th->seq);
if(skb->h.th->syn)
sum++;
if(sum>0)
{
amount+=sum;
if(skb->h.th->syn)
amount--;
counted+=sum;
}
if(skb->h.th->urg)
amount--;
skb=skb->next;
}
return (amount);//返回总共顺序可读数据
}
检测是否存在连接请求
int tcp_listen_select(struct sock *sk,int sel_type,select_table *wait )
{
。。。
判断sel_type==SEL_IN{
调用retval=(tcp_find_established(sk)!=NULL)
if(!retval)//当找不到established 的包则retval为0也就是找不到睡眠
select_wait(&master_select_wakeup,wait);
return retval;
。。。}
return 0;
}
select的tcp实现
int tcp_select(struct sock*sk,int sel_type,select_table*wait)
{
//判断sk->state是否是TCP_LISTEN是则return tcp_listen_select
否则sel_type:(是非监听套接字)
case SEL_IN:
判断是否出错,出错则return 1
判断sk->state==TCP_SYN_SENT||TCP_SYN_RECV
break;
sk->shutdown包含RCV_SHUTDOWN
return 1;//对端套接字关闭了不存在数据过来了
sk->acked_seq==sk->copied_seq
break; //acked_seq表示希望接受的下个序列号如果==copied_seq
表示没有多余的未读数据。
sk->usrg_seq!=sk->copied_seq||sk->acked_seq!=sk->copied_seq+1(有数据未读)
sk->urginline||!sk->urg_data//存在未读紧急数据
return 1;
break;//继续睡眠
case SEL_OUT:
sk->shudtwon包含SEND_SHUTDOWN
return 0;
sk->state==TCP_SYN_SENT||TCP_SYN_RECV
break;//继续睡眠
sk->prot->wspace(sk)<sk->mtu+128+sk->prot->max_header
//空间不足
break;//继续睡眠
return 1;
case SEL_EX:
sk->err||sj->usr_data
return 1;//出错或者存在紧急数据返回
break;
}
select_wait(sk->sleep,wait);
return 0;
}
ioctl实现
int tcp_ioctl(struct sock*sk,int cmd,unsigned long arg)
{
.....
}
tcp首部及其负载校验和
unsigned short tcp_check(struct tcphdr*th,int len,unsigned long saddr,unsigned long daddr)
{
。。。。。
}
负责将数据包从传输层发送到网络层
void tcp_send_skb(struct sock*sk,struct sk_buff*skb)
{
//获取数据包的tcp以及数据长度
size=skb->len-((unsigned char*)th-skb->data);
大小检查size<sizeof(struct tcpohdr)||size>skb->len
出错return ;
size==tcp结构大小
效验th->syn&&th->fin
都没置位则出错返回 return;
//更新skb->h.seq,纯数据长度
skb->h.seq=ntol(th->seq)+size-4*th->doff;
if(after(skb->h.seq,sk->window_seq)||sk->retransmit&&sk->xmit_timeout==TIME_WRITE
|| sk->packets_out>=sk->cong_window)
//这个数据包大小排到窗口之外或者存在超时重传或者未确认数据包超过限定
//将skb挂在sk->write_queue
skb_queue_tail(&sk->write_queue,skb);
if(before(sk->window_seq,sk->write_queue.next->h.seq)&&
sk->send_head==NULL&&sk->ack_backlog==0)
//如果窗口在待发送数据包之前且没有对端未确认的数据包以及对端来本地未应答的数据包
则设置探测定时器
reset_xmit_timer(sk,TIME_PROBE0,sk->rto);
else
//未超过窗口大小
th->ack_seq=ntol(sk->acked_seq);
th_window=ntos(tcp_select_window(sk));
tcp_send_check(th,sk-saddr,sk->daddr,size,sk);
sk->sent_seq=sk->write_seq;//更新已发送序列为写序列
sk->prot->queue_xmit(sk,skb->dev,skb,0);//发送数据包
reset_xmit_timer(sk,TIMER_WRITE,sk->rto);//会在收到ack时删除
}
partial操作函数,partial用于聚合数据的其只缓存一个数据包当数据发送的小于mtu就会聚合等待一会
//取下sk缓存的partial数据包
struct sk_buff*tcp_dequeue_partial(struct sock*sk)
{
skb=sk->partial;
if(skb)
sk->partial=NULL;
del_timer(&sk->partial_timer);
return skb;
}
发送partial数据包
void tcp_send_partial(struct sock*sk)
{
if(sk==NULL)
return ;
while skb=tcp_dequeue_partial(sk)!=NULL//取下数据包
tcp_send_skb(sk,skb);//发送出去
}
创建partial
void tcp_enqueue_partial(struct sk_buff*skb,struct sock*sk )
{
tmp=sk->partial;
若存在sk->partial则删定时器
del_timer(&sk->partial_timer);
sk->partial=skb;
初始化定时器
init_timer(&sk->partial_timer);
设置定时器参数为新的skb参数(超时为1秒)
add_timer(&sk->partial_timer);
如果tmp非空
tcp_send_skb(sk,tmp);
}
发送一个ack数据包
void tcp_send_ack(unsigned long sequence,unsigned long ack,struct sock*sk,
struct tcphdr*th,unsigned long daddr)
{
if(sk->zapped)//收到rst数据包会置zapped位
return ;
buff=sk->prot->wmalloc(sk,MAX_ACK_SIZE,1,GFP_ATOMIC)
如果buff==NULL//没写内存了
sk->ack_backlog++;//未确认数据包个数++;
if(sk->ip_xmit_timeout!=TIME_WRITE&&tcp_connect(sk->state))//存疑
{
reset_xmit_timer(sk,TIME_WRITE,HZ);
}
buff!=NULL
buff->len=tcp头部长度
buff->sk=sk;
buff->localroute=sk->localroute;
tl=(struct tcphdr*)buff->data;
//创建IP头
tmp=sk->prot->build_header(buff,sk->saddr,daddr,&dev,
IPPPROTO_TCP,sk->opt,MAX_ACK_SIZE,SK->IP_TOS,SK->IP_TTL);
buff->len+=tmp;
memcpy(tl,th,sizeof(*tl));
tl->dest=th->source;
tl->source=th->dest;
tl->seq=ntol(sequence);
tl->ack=1;
sk->window=tcp_select_window(sk);
tl->window=ntol(sk->window);
tl->resl=0;
tl->res2=0;
tl->rst=0;
tl->urg=0;
tl->syn=0;
tl->psh=0;
tl->fin=0;
若ack==sk->acked_seq;应答序列号等于本地希望接收的序列号则说明这个ack是最新的ack
sk->ack_backlog=0;//未应答数据包初始化为0
sk->bytes_rcv=0;
sk->ack_timed=0;
if(sk->send_head==NULL&&skb_peek(&sk->write_queue)
&&sk->ip_xmit_timeout==TIME_WRITE)
//未应答数据包为0以及未待发送为0以及设置了超时重传定时器
if(sk->keepopen)
rset_xmit_timer(sk,TIMER_KEEPOPEN,TCP_TIMEOUT_LEN);
else
delete_timer(sk);
tl->ack_seq=ntol(ack);
tl->doff=sizeof(*tl)/4;
tcp_send_check();
sk->prot->queue_xmit(sk,dev,buff,1);
}
tcp首部创建
int tcp_build_header(struct tcphdr* th,struct sock*sk,int push )
{
//拷贝sk->dummy_th到th地址上
。。。
//初始化th变量值
th->seq=htol(sk->write_seq);
th->psh=(push==0)?1:0;
th->doff=sizeof(*th)/4;
th->ack=1;
th->fin=0;
sk->ack_backlog=0;
sk->bytes_rcv=0;
sk->ack_timed=0;
th->ack_seq=htol(sk->acked_seq);
sk->window=tcp_select_window(sk);
th->window=htos(sk->window);
return (sizeof(*th));
}
write实现
int tcp_write(struct sock*sk,unsigned char*from
int len,int nonblock,unsigned flags)
{
while(len>0)
{
//判断是否是shutdown是则返回
sk->shutdown&SEND_SHUTDOWN
//判断不是TCP_ESTABLISHED以及不等于TCP_CLOSE_WAIT进入循环
非TCP_SYN_SENT或者TTCP_SYN_RECV会返回
是的话会根据noblock标志判断是否等待建立连接完成
//开始进行数据发送
第一个从tcp_dequeue_partial(sk)取下个数据包
//计算包头长度
hardlen=((unsigned long)skb->h.th -(unsigned long )skb->data)+sizeof(struct tcphdr);
//取len与数据包最大剩余载荷之间最小值
copy=min(sk->mss-(skb->len- hardlen),len);
从用户态地址拷贝copy长度数据
skb->len+=copy;
from+=copy;
copied+=copy;
len-=copy;
sk->write_seq+=copy;
// skb载荷长度大于sk->mss则调用tcp_send_skb发送
if((skb->len-hardlen)>=sk->mss||(flags&MSG_OOB)||!sk->packets_out)
tcp_send_skb(sk,skb);
else
tcp_enqueue_partial(skb,sk);
continue;
//partial为空
copy=sk->window_seq-sk->write_seq;
if(copy<=0||copy<(sk->max_windo>>1)||copy>sk->mss)
copy=sk->mss;
if(copy>len)
copy=len;
if(copy<sk->mss&&!(flags&MSG_OOB))
skb=prot->wmalloc(sk,sk->mtu+128+prot->max_header,0,GFP_KERNEL);
else
skb=prot->wmalloc(sk,copy+prot->max_header,0,GFP_KERNEL);
//skb分配失败则根据noblock标志是否睡眠等待继续重新分配
。。。
//skb分配成功
skb->len=0;
skb->sk=sk;
skb->free=0;
skb->localroute=sk->localroute|flags&MSG_DONTROUTE;
buff=skb->data;
tmp=prot->build_header(...);
skb->len+=tmp;
skb->dev=dev;
buff+=tmp;
skb->h.th=(struct tcphdr*)buff;
tmp=tcp_build_header((struct tcphdr*)buff,sk,len-copy);
skb->len+=tmp;
//从用户态拷贝copy长度数据
from+=copy;
copied+=copy;
len-=copy;
skb->len+=copy;
skb->free=0;
sk->write_seq+=copy;
//根据copy<sk->mss&&!(flags&MSG_OOB)
//判断是否缓存数据包tcp_enqueue_partial(skb,sk)&&continue;
//否则
tcp_send_skb(sk,skb);
继续len>0的while循环
}
若sk->partial存在且!sk->packets_out 非0或者数据包在窗口内
before(sk->write_seq,sk->window_seq);
tcp_send_partial(sk);
release_sock(sk);
return(copied);
}
int tcp_sendto(struct sock*sk,unsigned char *from
int len,int noblock,unsigned flags,struct sockaddr_in*addr,int addr_len)
{
...效验入参
调用tcp_write(sk,from,len,noblock,flags);
}
发送ack数据包让远端进行数据包发送或者接收
也就是发送个ack数据包
void tcp_read_wakeup(struct sock*sk)
{
判断sk->ack_backlog是不是非空若是则说明没有未应答数据包
获取buff=sk->prot->wmalloc(...)
初始化 buff->len=sizeof(struct tcphdr),buff->sk=sk,buff->localroute=sk->localroute;
构造ip头tmp=sk->prot->build_header(buff,...)
buff->len+=tmp;
tl=(struct tcphdr*)(buff->data+tmp);
memcpy(tl,(void*)&sk->dummy_th,sizeof(*tl));
初始化tl值
tl->seq=htonl(sk->sent_seq);
tl->ack=1;
tl->res1=0;
tl->res2=0;
tl->rst=0;
tl->urg=0;
tl->syn=0;
tl->psh=0;
sk->ack_backlog=0;
sk->bytes_rcv=0;
sk->window=tcp_select_window(sk);
tl->window=ntos(sk->window);
tl->ack_seq=ntohl(sk->acked_seq);
tl->doff=sizeof(*tl)/4;
tcp_send_check(...)
sk->prot->queue_xmit(sk,dev,buff,1);
tcp_statistics.TcpOutSegs++;
}
tcp_write_wakeup与tcp_read_wakeup区别是tl->seq=htonl(sk->send_seq-1);
tcp_read_wakeup发送未应答数据包而tcp_write_wakeup发送已应答的数据包。
tcp_write_wakeup用于probe定时器超时时会被调用。
清除已被读取完的数据包
void cleanup_rbuf(struct sock*sk)
{
left=sk->prot->rspace(sk);
取包判断是否已经读取完成
while(skb=skb_peek(&sk->receive_queue)!=NULL))
{
是否使用中的标志是!skb->used(读取过了会设置此标志)||skb->users (skb使用者为0))
如果已经被读取无人使用则kfree_skb(skb...)
}
if(rspace=sk->prot->rspace(sk)!=left)(看看是否释放了部分数据包空间)
{
sk->ack_backlog++;(用于发送ack数据包,因为tcp_read_wakeup会判断)
if(rspace>(sk->window-sk->bytes_rcv+sk->mtu))判断是否可以容纳一个mtu数据包
{
tcp_read_wakeup(sk);
}
else 不够mtu大
{
was_active=del_timer(&sk->retransmit_timer);
if(!was_acitve||TCP_ACK_TIME<sk->expires.time)定时器未到期
{
reset_xmit_timer(sk,TIME_WRITE,TCP_ACK_TIME);
}
else
add_timer(&sk->retransmit_timer);
}
}
}
读取紧急数据函数
int tcp_read_urg(struct sock*sk,int noblock,unsigned char *to,int len,unsigned flags )
{
sk状态判断
if(sk->urg_data&URG_VALID)
{
c=sk->urg_data;
put_fs_type(c,to);
relsease_sock(sk);
return 1;
}
release_sock(sk);
}
read tcp层实现
int tcp_read(struct sock*sk,unsigned char*to,int len,int noblock,unsigned flags)
{
排除监听套接字sk->state==TCP_LISTEN return -ENOTCONN;
判断是否存在紧急数据flags&MSG_OOB 则调用tcp_read_urg读取;
seq=&sk->copied_seq;
将当前进程加入sk->sleep睡眠队列中
sk->inuse=1;
while(len>0)
{
if(copied&&ssk->urg_data&&sk->urg_seq==&seq)
break;读取过程中遇到紧急数据则停止读取
skb=skb_peek(&sk->receive_queue)
do
{
if(!skb) break;没有数据包了停止读取。
if(before(*seq,skb->h.th->seq))
break;出现序列不一致理论上seq==skb->h.th->seq
offset=*seq-skb->h.th->seq;//部分重叠
if(skb->h.th->syn)
offset--;
if(offset<skb->len)
goto found_ok_skb;//找到数据包
if(skb->h.th->fin)
goto found_fin_ok;//发现fin数据包
skb->used=1;
skb=skb->next;//如果没找到说明目前数据包被读取了继续下一个
}while(skb!=&sk->receive_queue);
if(copied)//走到这里说明之前没有跳转也就是没有数据可读
break;
判断sk是否出错,出错则break;
如果sk->state==TCP_CLOSE说明sk关闭则出错返回
if(noblock) break;
cleanup_rbuf(sk);
relsease_sock(sk);
schedule();//让出cpu睡眠。
处理信号唤醒的情况。
//tcp_data会将数据包挂在receive_queue队列加入,加入一个数据包后调用sock->data_ready指针函数def_callback2唤醒睡眠在sk->sleep上的进程
continent;//这里说明被唤醒
found_ok_skb:
skb->users++;
used=skb->len-offset;判断出有用的数据。
if(len<used) used=len;
判断是否存在 紧急数据处理紧急数据
*seq+=used;
//向用户空间拷贝数据
memcpy_togfs(to,(unsigned char*)skb->h.th+skb->h.th->doff*4+offset;used);
copied+=used;
len-=used;
to+=used;
skb->users--;
如果紧急数据已经被处理则清楚skb->urg_data=0;
if(used+offset<skb->len)//数据包未被完全读取继续下一次循环则会跳出len大循环。
continue;
//下面是数据包被读取完了的处理
if(skb->h.th->fin)
goto found_fin_ok;
//说明不是fin数据包则是普通数据包
//设置skb被读取位
skb->used=1;
continue;//继续下一次循环
found_fin_ok:
++*seq;
skb->used=1;
sk->shutdown|=RCV_SHUTDOWN;
break;//跳出循环
}
cleanup_rbuf(sk);
release_sock(sk);
return copied;
}
关闭套接字时调用tcp_close_state决定是否进行发送fin操作
int tcp_close_state(struct sock*sk,int dead)
{
switch(sk->state)
{
case TCP_SYN_SENT:
break;
case TCP_SYN_RECV:
case TCP_established:
ns=TCP_FIN_WAIT1;
send_fin=1;
break;
case TCP_FIN_WAIT1:
case TCP_FIN_WAIT2:
case TCP_CLOSING:
ns=sk->state;
break;
case TCP_CLOSE:
case TCP_LISTEN:
break;
case TCP_CLOSE_WAIT:
ns=TCP_LAST_ACK;
send_fin=1;
}
tcp_set_state(sk,ns);
if(dead&&ns==TCP_FIN_WAIT2)
{
设置定时器操作
}
return send_fin;
}
发送fin数据包关闭发送通道
void tcp_send_fin(struct sock *sk)
{
//申请个skb空间
buff=prot->wmalloc(...)
sk->inuse=1;
//初始化buff
buff->sk=sk;
buff->len=sizeof(*tl);
buff->localroute=sk->localroute;
tl=(struct tcphdr*)buff->data;
//构造ip头
tmp=prot->build_header(...)
tl=(struct tcphdr*)((char*)tl+tmp);
buff->len+=tmp;
buff->dev=dev;
memcpy(tl,th,sizeof(*tl));
//使用write-seq因为fin需要在已写数据之后发送
tl->seq=ntol(sk->write_seq);
sk->write_seq++;
buff->h.seq=sk->write_seq;
tl->ack=1;
tl->ack_seq=ntol(sk->acked_seq);
tl->window=ntohs(sk->window=tcp_select_window(sk));
tl->fin=1;
tl->rst=0;
tl->doff=sizeof(*tl)/4;
tcp_send_check(...);
//判断write_seq是否存在数据有则排队
if(skb_peek(&sk->writez_queue)!=NULL)
{
buff->free=0;
...
skb_queue_tail(&sk->write_queue);
}
else
{
//更新sent_seq
sk->sent_seq=sk->write_seq;
sk->prot->queue_xmit(sk,dev,buff,0);
reset_xmit_timer(sk,TIME_WRITE,sk->rto);
}
}
shutdown 函数TCP实现
void tcp_shutdown(struct sock*sk,int how)
{
//以下状态不需要操作直接返回
if(sk->state==TCP_FIN_WAIT1||
sk->state==TCP_FIN_WAIT2)||
sk->state==TCP_CLOSING||
sk->state==TCP_LAST_ACK||
sk->state==TCP_TIME_WAIT||
sk->state==TCP_CLOSE||
sk->state==TCP_LISTEN)
return ;
sk->shutdown|=SEND_SHUTDOWN;
//若存在未发送的partial则发送
if(sk->partial)
tcp_send_partial(sk);
if(tcp_close_state(sk,0))
tcp_send_fin(sk);
release_sock(sk);
}
recvfrom的TCP实现
int tcp_recvfrom(struct sock*sock,unsigned char*to
int to_len,int noblock,unsigned flags
struct sockaddr_in*addr,int addr_len)
{
if(addr_len)
*addr_len=sizeof(*addr);
result=tcp_read(sk,to,to_len,noblock,flagsd) ;
if(result<0)
return result;
if(addr)
{
addr->sin_family=AF_INET;
addr->sin_port=sk->dummy_th.dest;
addr->sin_addr.s_addr=sk->daddr;
}
return result;
}
向远端发送rst数据包
tcp_reset(unsigned long saddr,unsigned long daddr ,struct tcphdr*th
struct proto*prot,struct options*opt,struct device*dev, int tos )
{
判断是否已经接受了rst,是否被置位,是则return返回
buff=prot->wmalloc(...)
初始化skb基础数据
buff->len=sizeof(*tl);
buff->sk=NULL;
buff->dev=dev;
buff->localroute=0;
tl=(struct tcphdr*)buff->data;
//构造ip头
tmp=prot->build_header(buff,saddr,daddr,&ndev,IPPROTO_TCP,opt,,sizoef(struct tcphdr),tos,ttl)
tl=(struct tcphdr*)((char*)tl+tmp);
buff->len+=tmp;
//拷贝对方的th
memcpy(tl,th,sizeof(*tl));
//初始化tl数据
tl->dest=th->source;
tl->source=th->dest;
tl->rst=1;
tl->window=0;
//如果收到时ack出发rst
//则将seq=对端设置的确认序列号否则会引起对端丢弃
if(th->ack)
{
tl->ack=0;
tl->seq=th->ack_seq;
tl->ack_seq=0;
}
else//发送个ack+rst//因为tcp连接一旦建立所有交互数据包ack都会置位
{ //不是ack则是syn
tl->ack=1
//判断是否是syn是则tl->ack_seq=htol(th->seq)//+th->len;
tl->ack_seq=htol(th->seq+1);//th->len
tl->seq=0;
}
//初始化剩余数据
tl->syn=0;
tl->urg=0;
tl->fin=0;
tl->psh=0;
tl->doff=sizeof(*tl)/4;
tcp_send_check(...);
prot->queue_xmit(...);
//计数
}
tcp选项处理函数
void tcp_options(...)
{
...
}
unsigned long default_mask(unsigned long dst)
{
....
根据网络号判断地址类型
}
//初始化seq
unsigned long tcp_init_seq(void)
{
struct timeval tv;
do_gettimeofday(&tv);
return tv.tv_usec+tv.tv_sec*1000000;
}
处理tcp连接请求//daddr\saddr从远端角度看,daddr是本地地址
被tcp_rcv调用处理syn请求连接数据包
void tcp_conn_request(struct sock*sk,struct sk_buff*skb,
unsigned long daddr,unsigned long saddr,struct options*opt,struct device*dev,unsigned long seq)
{
判断sk->dead置位则tcp_rst(...)
没有置位则sk->data_ready(sk,0)唤醒睡眠在此套接字的进程
//继续下一步
判断目前sk->ack_backlog是不是大于sk->max_ack_backlog
//是则释放数据包返回
//kamlloc获取新的mewsk
memcpy(newsk,sk,sizeof(newsk));
//初始化newsk
skb_queue_head_init(&newsk->write_queue);
skb_queue_head_init(&newsk->receiver_queue);
newsk->send_head=NULL;
newsk->send_tail=NULL;
skb_queue_head_init(&newsk->back_log);
newsk->rtt=0;
newsk->rto=TCP_TIMEOUT_INIT;
newsk->mdev=0;
newsk->max_window=0;
newsk->cong_window=1;
newsk->cong_count=0;
newsk->ssthresh=0;
newsk->backoff=0;
newsk->blog=0;
newsk->initr=0;
newsk->proc=0;
newsk->done=0;
newsk->partial=NULL;
newsk->pair=0;
newsk->wmem_alloc=0;
newsk->rmem_alloc=0;
newsk->localroute=sk->localroute;
newsk->max_unacked=MAX_WINDOW-TCP_WINDOW_DIFF;
newsk->err=0;
newsk->shutdown=0;
newsk->ack_backlog=0;
//初始化希望接受到的下一个字节序列号
newsk->acked_seq=skb->h.th->seq+1;
newsk->copied_seq=skb->h.th->seq=1;
newsk->fin_seq=skb->h.th->seq;
//初始化sk状态为TCP_SYN_RECV;
newsk->state=TCP_SYN_RECV;
//初始化
newsk->timeout=0;
newsk->ip_xmit_timeout=0;
//初始化seq
newsk->write_seq=seq;
newsk->rcv_ack_seq=newsk->write_seq;
nerwsk->urg_data=0;
newsk->retransmits=0;
newsk->linger=0;
newsk->destroy=0;
init_timer(&newsk->timer);
newsk->timer.data=(unsigned long )newsk;
newsk->timer.function=&net_timer;
init_timer(&newsk->retransmit_timer);//重发定时器
newsk->retransmit_timer.data=(unsigned long )data;
newsk->retransmit_timer.function=&retransmit_timer;
newsk->dummy_th.source=skb->h.th->dest;
newsk->dummy.th.dest=skb->h.th->source;
//daddr是本地地址saddr是发送的端地址
newsk->daddr=saddr;
newsk->saddr=daddr;
初始化newsk->dummy.th里的值
....
newsk->acked_seq=skb->h.th->seq+1;
newsk->copied_seq=skb->h.th->seq+1;
newsk->socket=NULL;
newsk->ip_ttl=sk->ip_ttl;
newsk->ip_tos=skb->ip_hdr->tos;
rt=ip_rt_route(saddr,NULL,NULL);
。。。
tcp_options(newsk,skb->h.th);
分配个skb
buff=newsk->prot->wmalloc(...)
buff->len=sizeof(struct tcphdr)+4;
buff->sk=newsk;
buff->localroute=newsk->localroute;
tl=(struct tcphdr*)buff->data;
构造ip头
tmp=sk->prot->build_header(...)
buff->len+=tmp;
tl=(struct tcphdr*)((char*)tl+data);
buff->h.seq=newsk->write_seq;
初始化buff里的tl值
。。。
tl->ack=1;
...
tl->syn=1;
...
tcp_send_check(...);
newsk->prot->queue_xmit(...)
reset_xmit_timer(newsk,TIME_WRITE,TCP_TIMEOUT_INIT);
skb->sk=newsk;
sk->rmem_alloc-=skb->mem_len;
newsk->rmem_alloc+=skb->mem_len;
skb_queue_tail(&sk->receive_queue,skb);
sk->ack_backlog++;
release_sock)(newsk);
计数
//分配个newsk发送个ack+syn
}
close的tcp层时实现
void tcp_close(struct sock*sk,int timeout)
{
//如果是监听套接字
{
//设置套接字状态为close
tcp_set_state(sk,TCP_CLOSE);
//tcp_close_pending会释放
//receive_queue的数据包
tcp_close_pending(sk);
release_sock(sk);
return ;
}
sk->keepopen=1;
//设置sk->shutdown
sk->shutdown=SHUTDOWN_MASK;
if(!sk->dead)
//def_callback1唤醒sk->sleep
sk->satte_change(sk);
判断timeout==0则表示立即关闭进行如下步骤
{//释放skn->receive_queue数据包
while(skb=skb_dequeue(&sk->receive_queue!=NULL))
kfree_skb(skb,FREE_READ);
//释放partial
if(sk->partial)
tcp_send_partial(sk);
}end of timeout
if(timeout)
//tcp_set_state设置sk->state状态
tcp_set_state(sk,TCP_CLOSE);(?不知道啥时候关闭)
else
//tcp_close_state 根据sk->state决定是否发送fin数据包
if(tcp_close_state(sk,1)==1)
tcp_send_fin(sk);
release_sock(sk);
}
处理write_queue队列数据包
void tcp_write_xmit(struct sock*sk)
{
if(sk->zapped)//zapped是在收到rst的时候置位的
return ;//若已收到rst则返回
while(skb=skb_peek(&sk->write_queue))!=NULL)&&
before(skb->h.seq,sk->window_seq+1)&&