深入理解TCP协议及其源代码——send和recv背后数据的收发过程
Posted litosty
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深入理解TCP协议及其源代码——send和recv背后数据的收发过程相关的知识,希望对你有一定的参考价值。
TCP数据发送和接收的原理
TCP连接的建立过程
TCP Socket的连接的过程是服务端先通过socket()
函数创建一个socket对象,生成一个socket文件描述符,然后通过bind()
函数将生成的socket绑定到要监听的地址和端口上面。绑定好了之后,使用listen()
函数来监听相应的端口。而客户端是在通过socket()
函数创建一个socket对象之后,通过connect()
函数向被服务端监听的socket发起一个连接请求,即发起一次TCP连接的三次握手。接下来就可以就可以通过TCP连接收发数据了。
Socket中send()
和recv()
为了实现数据的收发,每个TCP socket在内核中都维护了一个发送缓冲区和一个接收缓冲区,send()
函数把应用缓冲区中的数据拷贝到TCP发送缓冲区中,接下来的发送过程由TCP负责;recv()
函数是将TCP接收缓冲区中的数据拷贝到应用缓冲区中。在socket中,send()
和recv()
只管拷贝,真正的发送和接收是由TCP连接负责的。接下来我们看看TCP连接是如何实现数据收发的。
TCP连接中的数据收发
追踪socket中的__sys_sendto
和__sys_recvfrom
函数
实验环境的准备参考构建调试Linux内核网络代码的环境MenuOS系统
$ cd ~/linuxnet/lab3
$ make rootfs
#保持当前终端和Qemu运行,重新打开一个终端,运行gdb
$ gdb
(gdb)file ~/linux-5.0.1/vmlinux
(gdb)target remote:1234
(gdb)b __sys_sendto
(gdb)b __sys_recvfrom
(gdb)c
MenuOS>>replyhi#在MenuOS中执行
MenuOS>>hello#在MenuOS中执行
(gdb)info b# 设置好的断点信息如下
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep y 0xffffffff817ba370 in __sys_sendto at net/socket.c:1758
2 breakpoint keep y 0xffffffff817ba540 in __sys_recvfrom at net/socket.c:1819
我们先给socket中的__sys_sendto
和__sys_recvfrom
打上断点,执行一次replyhi/hello命令观察一下断点的情况:
此处客户端给服务端发送了字符串"hello",而服务端回复了"hi",__sys_sendto()
和__sys_recvfrom()
函数的参数值如下:
Breakpoint 2, __sys_recvfrom (fd=5, ubuf=0xffb668ec, size=1024, flags=2147483648, addr=0x0 <irq_stack_union>,
addr_len=0x0 <irq_stack_union>) at net/socket.c:1819
1819 {
(gdb) x/5c ubuf////第一次截到__sys_recvfrom函数时,从缓冲区中看到已经收到了字符串"hello"
0xffb668ec: 104 'h' 101 'e' 108 'l' 108 'l' 111 'o'
(gdb) c
Continuing.
Breakpoint 1, __sys_sendto (fd=4, buff=0xffb66d0c, len=5, flags=0, addr=0x0 <irq_stack_union>, addr_len=0)
at net/socket.c:1758
1758 {
(gdb) x/5c buff//这时,截到了发送hello的__sys_sendto函数
0xffb66d0c: 104 'h' 101 'e' 108 'l' 108 'l' 111 'o'
(gdb) c
Continuing.
Breakpoint 2, __sys_recvfrom (fd=4, ubuf=0xffb6690c, size=1024, flags=2147483648, addr=0x0 <irq_stack_union>,
addr_len=0x0 <irq_stack_union>) at net/socket.c:1819
1819 {
(gdb) x/5c ubuf//此时该函数尚未接收到字符串
0xffb6690c: 0 '