TCP server和client用gdb单步调试观察3次握手和4次挥手

server.c

#include<sys/socket.h>//socket connect bind listen accept recv send
#include<netinet/in.h>//sockaddr_in htons
#include<arpa/inet.h>//inet_addr 
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<signal.h>
int serverfd, clientfd;
struct sockaddr_in address;
typedef struct{
    int data1;
    char data2[32];
    double data3;
    float data4;
}buffer;
buffer buf;
void handler(int sig){
    close(serverfd);
    exit(EXIT_SUCCESS);
}
int main(){
    memset((void*)&buf,0,sizeof(buf));
    signal(SIGINT, handler);
socket:
    while(-1==(serverfd=socket(AF_INET,SOCK_STREAM,0))){
        perror("socket");
        sleep(1);
    }
    int optval=1;
    // setsockopt() set parameter for socket SO_REUSEADDR
    if(setsockopt(serverfd,SOL_SOCKET,SO_REUSEADDR,&optval,sizeof(optval))<0){
        perror("setsockopt");
        sleep(1);
        goto socket;
    }
    address.sin_family=AF_INET;//AF_INET = 2
    address.sin_addr.s_addr=inet_addr("127.0.0.1");//in_addr_t = uint32_t
    address.sin_port=htons(1234); //in_port_t = uint16_t
    //bind() return 0 on success, -1 on error
    if(bind(serverfd, (struct sockaddr*)&address, sizeof(address))<0){
        perror("bind");
        sleep(1);
        goto socket;
    }
    //listen() is a non-blocking function which just set parameter for monitoring. 
    //listen(,int __n), kernal listen max 10=SYN_RCVD+ESTABLISHED(not yet be accepted), 
    //return 0 if success;
    if(listen(serverfd,10)){//return 0 on success, -1 on error
        perror("listen");
        close(serverfd);
        sleep(1);
        goto socket;
    }
accept://accept() is a blocking function which wait for client to connect.
    while(-1==(clientfd=accept(serverfd,NULL,NULL))){
        perror("accept");
        sleep(1);
    }
    while(1){
        if(-1==recv(clientfd,(void*)&buf,sizeof(buf),MSG_WAITALL)){   
            perror("recv");
            sleep(1);
            goto accept;
        }
        printf("\033[2J\033[H\033[38;2;255;125;50mdata1=%d\n%s\ndata3=%f\ndata4=%f\n",
                buf.data1,buf.data2,buf.data3,buf.data4);
        buf.data1++; 
        buf.data3+=1.1; 
        buf.data4+=0.1;
        sprintf(buf.data2,"clientfd=%d\nserverfd=%d",clientfd,serverfd);
        sleep(1);
        if(-1==send(clientfd,&buf,sizeof(buf),MSG_NOSIGNAL)){//If recever terminate, send would not triger SIGPIPE.
            perror("send");
            sleep(1);
            goto accept;//goto accept can reconnect terminate/restart client.
        }
    }
    return 0;
}
/*
    socket(..,int type,..)的常用宏:
        SOCK_STREAM = 1, //TCP
        SOCK_DGRAM = 2,	 //UDP
        SOCK_RAW = 3,	//Raw protocol interface. 
        SOCK_RDM = 4,	//Reliably-delivered messages. 
        .....

    send(,,,int flag)的常用宏:
        MSG_OOB 发送带外数据
        MSG_DONTROUTE 禁止路由查找
        MSG_DONTWAIT 非阻塞操作
        MSG_NOSIGNAL 抑制 SIGPIPE 信号
        MSG_MORE 指示后续还有更多数据
        MSG_EOR 表示消息结束

    recv(,,,int flag)的常用宏:
        MSG_OOB 接收带外数据
        MSG_PEEK 预览数据但不移除
        MSG_WAITALL 阻塞直到接收到所有数据
        MSG_DONTWAIT 非阻塞操作
        MSG_TRUNC 指示数据被截断
        MSG_ERRQUEUE 从错误队列中接收错误信息
        MSG_CTRUNC 表示控制数据被截断
        MSG_EOR 表示消息结束
*/

client.c

#include<sys/socket.h>//socket connect bind listen accept recv send
#include<netinet/in.h>//sockaddr_in htons
#include<arpa/inet.h>//inet_addr 
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<signal.h>
#include<string.h>
int serverfd;
struct sockaddr_in address;
typedef struct{
    int data1;
    char data2[32];
    double data3;
    float data4;
}buffer;
buffer buf;
void handler(int sig){
    close(serverfd);
    exit(EXIT_SUCCESS);
}
/*
    SIGPIPE interrrupt would happen when server terminated and client run,
    but send(flag=MSG_NOSIGNAL) can supress SIGPIPI.
    If no define this handler2(did nothing), the kernel default action is terminate the process.
*/
//void handler2(int sig){ return; }
int main(){
    memset((void*)&buf,0,sizeof(buf));
    signal(SIGINT, handler);
    //signal(SIGPIPE,handler2);
socket:
    while(-1==(serverfd=socket(AF_INET,SOCK_STREAM,0))){
        perror("socket");
        sleep(1);
    }
    address.sin_family=AF_INET;//AF_INET = 2
    address.sin_addr.s_addr=inet_addr("127.0.0.1");//in_addr_t = uint32_t
    address.sin_port=htons(1234); //in_port_t = uint16_t
    //connect() return 0 on success, -1 on error
    while(connect(serverfd,(struct sockaddr *)&address, sizeof(address))<0){
        perror("connect");
        sleep(1);
    }
    while(1){
        buf.data1++; 
        buf.data3+=1.1; 
        buf.data4+=0.1;
        sprintf(buf.data2,"serverfd=%d",serverfd);
        //If recever terminate, send(,,,MSG_NOSIGNAL) would not triger signal(SIGPIPE,)
        if(-1==send(serverfd,(void*)&buf,sizeof(buf),MSG_NOSIGNAL)){
            perror("send");
            close(serverfd);
            sleep(1);
            goto socket;//but goto connect can not successfuly reconnect client onto terminate/restart server, but goto socket can.
        }
        if(-1==recv(serverfd,(void*)&buf,sizeof(buf),MSG_WAITALL)){
            perror("recv");
            close(serverfd);
            sleep(1);
            goto socket;//goto connect can not successfuly reconnect client onto terminate/restart server, but goto socket can.
        }
        printf("\033[2J\033[H\033[38;2;50;125;255mdata1=%d\n%s\ndata3=%f\ndata4=%f\n",
                buf.data1,buf.data2,buf.data3,buf.data4);
    }
    return 0;
}
/*
    sa_family_t 表示地址族(address family)的小整数类型,定义在<sys/socket.h>,常见的地址族包括:
        AF_INET 用于IPv4协议;
        AF_INET6 用于IPv6协议;
        AF_UNIX 用于本地通信Unix域套接;

    in_addr_t 表示IPv4地址的32 位无符号整数uint32_t,定义在<netinet/in.h>,
        用来存储以网络字节序(大端)表示的IPv4地址
    
    in_port_t 表示端口号的整数类型,定义在<netinet/in.h>
*/

编译:

gcc -Wall -O3 server.c -o server.elf

gcc -Wall -O3 client.c -o client.elf

gdb调试:

server.elf的gdb调试

xyz@xyz:/media/xyz/disk_d/LinuxKnowledge/LinuxC/chapter2025/socket/TCP$ gdb ./server.elf 
GNU gdb (Ubuntu 15.0.50.20240403-0ubuntu1) 15.0.50.20240403-git
Copyright (C) 2024 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./server.elf...

This GDB supports auto-downloading debuginfo from the following URLs:
  <https://debuginfod.ubuntu.com>
Enable debuginfod for this session? (y or [n]) y
Debuginfod has been enabled.
To make this setting permanent, add 'set debuginfod enabled on' to .gdbinit.
Downloading separate debug info for /media/xyz/disk_d/LinuxKnowledge/LinuxC/chapter2025/socket/TCP/server.elf
(No debugging symbols found in ./server.elf)                                                                                                                                                                                                
(gdb) b socket
Breakpoint 1 at 0x1110
(gdb) b setsockopt
Breakpoint 2 at 0x1040
(gdb) b bind
Breakpoint 3 at 0x10b0
(gdb) b listen
Breakpoint 4 at 0x10a0
(gdb) b accept
Breakpoint 5 at 0x10d0
(gdb) b recv
Breakpoint 6 at 0x1030
(gdb) b send
Breakpoint 7 at 0x1050
(gdb) r
Starting program: /media/xyz/disk_d/LinuxKnowledge/LinuxC/chapter2025/socket/TCP/server.elf 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Download failed: 无效的参数.  Continuing without source file ./socket/../sysdeps/unix/syscall-template.S.
Download failed: 无效的参数.  Continuing without source file ./socket/../sysdeps/unix/sysv/linux/setsockopt.c.
Download failed: 无效的参数.  Continuing without source file ./socket/../sysdeps/unix/syscall-template.S.
Download failed: 无效的参数.  Continuing without source file ./socket/../sysdeps/unix/syscall-template.S.
Download failed: 无效的参数.  Continuing without source file ./socket/../sysdeps/unix/sysv/linux/accept.c.
Download failed: 无效的参数.  Continuing without source file ./socket/../sysdeps/unix/sysv/linux/recv.c.
Download failed: 无效的参数.  Continuing without source file ./socket/../sysdeps/unix/sysv/linux/send.c.
Download failed: 无效的参数.  Continuing without source file ./socket/../sysdeps/unix/syscall-template.S.

Breakpoint 1, __GI_socket () at ../sysdeps/unix/syscall-template.S:120
warning: 120    ../sysdeps/unix/syscall-template.S: 没有那个文件或目录
(gdb) c
Continuing.
Download failed: 无效的参数.  Continuing without source file ./socket/../sysdeps/unix/sysv/linux/setsockopt.c.

Breakpoint 2, __GI___setsockopt (fd=3, level=1, optname=2, optval=0x7fffffffd884, len=4) at ../sysdeps/unix/sysv/linux/setsockopt.c:94
warning: 94     ../sysdeps/unix/sysv/linux/setsockopt.c: 没有那个文件或目录
(gdb) c
Continuing.
Download failed: 无效的参数.  Continuing without source file ./socket/../sysdeps/unix/syscall-template.S.

Breakpoint 3, __GI_bind () at ../sysdeps/unix/syscall-template.S:120
warning: 120    ../sysdeps/unix/syscall-template.S: 没有那个文件或目录
(gdb) c
Continuing.
Download failed: 无效的参数.  Continuing without source file ./socket/../sysdeps/unix/syscall-template.S.

Breakpoint 4, __GI_listen () at ../sysdeps/unix/syscall-template.S:120
warning: 120    ../sysdeps/unix/syscall-template.S: 没有那个文件或目录
(gdb) c
Continuing.
Download failed: 无效的参数.  Continuing without source file ./socket/../sysdeps/unix/sysv/linux/accept.c.

Breakpoint 5, __libc_accept (fd=3, addr=..., len=0x0) at ../sysdeps/unix/sysv/linux/accept.c:24
warning: 24     ../sysdeps/unix/sysv/linux/accept.c: 没有那个文件或目录
(gdb) c
Continuing.
Download failed: 无效的参数.  Continuing without source file ./socket/../sysdeps/unix/sysv/linux/recv.c.

Breakpoint 6, __libc_recv (fd=4, buf=0x555555558098 <buf>, len=56, flags=256) at ../sysdeps/unix/sysv/linux/recv.c:24
warning: 24     ../sysdeps/unix/sysv/linux/recv.c: 没有那个文件或目录
(gdb) 
......
data1=5
serverfd=3
data3=5.500000
data4=0.500000
Download failed: 无效的参数.  Continuing without source file ./socket/../sysdeps/unix/sysv/linux/send.c.

Breakpoint 7, __libc_send (fd=4, buf=0x555555558098 <buf>, len=56, flags=16384) at ../sysdeps/unix/sysv/linux/send.c:24
warning: 24     ../sysdeps/unix/sysv/linux/send.c: 没有那个文件或目录
(gdb) c
Continuing.
Download failed: 无效的参数.  Continuing without source file ./socket/../sysdeps/unix/sysv/linux/recv.c.

Breakpoint 6, __libc_recv (fd=4, buf=0x555555558098 <buf>, len=56, flags=256) at ../sysdeps/unix/sysv/linux/recv.c:24
warning: 24     ../sysdeps/unix/sysv/linux/recv.c: 没有那个文件或目录
(gdb) 

clent.elf的dbg调试:

xyz@xyz:/media/xyz/disk_d/LinuxKnowledge/LinuxC/chapter2025/socket/TCP$ gdb ./client.elf 
GNU gdb (Ubuntu 15.0.50.20240403-0ubuntu1) 15.0.50.20240403-git
Copyright (C) 2024 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./client.elf...

This GDB supports auto-downloading debuginfo from the following URLs:
  <https://debuginfod.ubuntu.com>
Enable debuginfod for this session? (y or [n]) y
Debuginfod has been enabled.
To make this setting permanent, add 'set debuginfod enabled on' to .gdbinit.
Downloading separate debug info for /media/xyz/disk_d/LinuxKnowledge/LinuxC/chapter2025/socket/TCP/client.elf
(No debugging symbols found in ./client.elf)                                                                                                                                                                                                
(gdb) b socket
Breakpoint 1 at 0x10e0
(gdb) b connect
Breakpoint 2 at 0x10c0
(gdb) b send
Breakpoint 3 at 0x1040
(gdb) b recv
Breakpoint 4 at 0x1030
(gdb) r
Starting program: /media/xyz/disk_d/LinuxKnowledge/LinuxC/chapter2025/socket/TCP/client.elf 
Downloading separate debug info for system-supplied DSO at 0x7ffff7fc3000
[Thread debugging using libthread_db enabled]                                                                                                                                                                                               
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Download failed: 无效的参数.  Continuing without source file ./socket/../sysdeps/unix/syscall-template.S.
Download failed: 无效的参数.  Continuing without source file ./socket/../sysdeps/unix/sysv/linux/connect.c.
Download failed: 无效的参数.  Continuing without source file ./socket/../sysdeps/unix/sysv/linux/send.c.
Download failed: 无效的参数.  Continuing without source file ./socket/../sysdeps/unix/sysv/linux/recv.c.
Download failed: 无效的参数.  Continuing without source file ./socket/../sysdeps/unix/syscall-template.S.

Breakpoint 1, __GI_socket () at ../sysdeps/unix/syscall-template.S:120
warning: 120    ../sysdeps/unix/syscall-template.S: 没有那个文件或目录
(gdb) c
Continuing.
Download failed: 无效的参数.  Continuing without source file ./socket/../sysdeps/unix/sysv/linux/connect.c.

Breakpoint 2, __libc_connect (fd=3, addr=..., len=16) at ../sysdeps/unix/sysv/linux/connect.c:24
warning: 24     ../sysdeps/unix/sysv/linux/connect.c: 没有那个文件或目录
(gdb) c
Continuing.
connect: Connection refused

Breakpoint 2, __libc_connect (fd=3, addr=..., len=16) at ../sysdeps/unix/sysv/linux/connect.c:24
24      in ../sysdeps/unix/sysv/linux/connect.c
(gdb) c
Continuing.
connect: Connection refused

Breakpoint 2, __libc_connect (fd=3, addr=..., len=16) at ../sysdeps/unix/sysv/linux/connect.c:24
24      in ../sysdeps/unix/sysv/linux/connect.c
(gdb) c
Continuing.
Download failed: 无效的参数.  Continuing without source file ./socket/../sysdeps/unix/sysv/linux/send.c.

Breakpoint 3, __libc_send (fd=3, buf=0x555555558080 <buf>, len=56, flags=16384) at ../sysdeps/unix/sysv/linux/send.c:24
warning: 24     ../sysdeps/unix/sysv/linux/send.c: 没有那个文件或目录
(gdb) 
.......
data1=4
clientfd=4
serverfd=3
data3=4.400000
data4=0.400000
Download failed: 无效的参数.  Continuing without source file ./socket/../sysdeps/unix/sysv/linux/send.c.

Breakpoint 3, __libc_send (fd=3, buf=0x555555558080 <buf>, len=56, flags=16384) at ../sysdeps/unix/sysv/linux/send.c:24
warning: 24     ../sysdeps/unix/sysv/linux/send.c: 没有那个文件或目录
(gdb) c
Continuing.
Download failed: 无效的参数.  Continuing without source file ./socket/../sysdeps/unix/sysv/linux/recv.c.

Breakpoint 4, __libc_recv (fd=3, buf=0x555555558080 <buf>, len=56, flags=256) at ../sysdeps/unix/sysv/linux/recv.c:24
warning: 24     ../sysdeps/unix/sysv/linux/recv.c: 没有那个文件或目录
(gdb) 

lsof -i :1234监控socket的建立和连接过程:

xyz@xyz:/media/xyz/disk_d/LinuxKnowledge/LinuxC/chapter2025/socket/TCP$ lsof -i :1234
xyz@xyz:/media/xyz/disk_d/LinuxKnowledge/LinuxC/chapter2025/socket/TCP$ lsof -i :1234
COMMAND     PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
server.el 23881  xyz    3u  IPv4 311875      0t0  TCP localhost:1234 (LISTEN)
xyz@xyz:/media/xyz/disk_d/LinuxKnowledge/LinuxC/chapter2025/socket/TCP$ lsof -i :1234
COMMAND     PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
server.el 23881  xyz    3u  IPv4 311875      0t0  TCP localhost:1234 (LISTEN)
client.el 24224  xyz    3u  IPv4 312022      0t0  TCP localhost:53996->localhost:1234 (ESTABLISHED)
xyz@xyz:/media/xyz/disk_d/LinuxKnowledge/LinuxC/chapter2025/socket/TCP$ 

tcpdump -i lo port 1234监控三次握手和逐条收发报文:

server.elf和client.elf逐次单步调试socket, bind, listen, accept connect send recv函数期间,重复lsof可监控到server和client之间establish连接。tcpdump可抓取到三次握手的过程,期间如果server.elf运行到断点bind, client运行到connect会提示refused connection.此时如果继续运行server.elf到accept断点,三次握手报文可被抓取。继续单步运行send和recv,可抓取到每次的TCP报文。

上述单步调试过程中,如果命令tcpdump -i lo port 1234 -w capture.pcap将TCP报文保存成pcap文件,用wireshark打开capture.pcap文件可看到:

第2行报文(红色)是server还没有listen()时client试图connect()得到的回执 RST,ACK;

第3,4,5行报文(灰色)是client再次提出握手,双方成功完成3次握手;

第18,19,20,21行时4次挥手;(此时dgb q退出调试并终止进程server.elf和client.elf)

第2行报文TCP层的详细内容:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值