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层的详细内容: