UDP堵塞和非堵塞gdb逐步调试观察socket的创建

1. server端采取缺省堵塞式,接收缓存区为空时,函数recvfrom堵塞

server.c代码:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<signal.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<fcntl.h>

int serverfd;
struct sockaddr_in serveraddr, clientaddr;

typedef struct{
    int a;
    float b;
    double c;
    char d[32];
}buffer;
buffer buf;
void handler(int sig){
    close(serverfd);
    exit(EXIT_SUCCESS);
    return;
}
int main(){
    signal(SIGINT, handler);
socket:
    if(-1==(serverfd=socket(AF_INET,SOCK_DGRAM,0))){//IPv4,UDP,0
        perror("socket");
        exit(EXIT_FAILURE);
    }
    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;
    }
    memset(&buf,0,sizeof(buf));
    memset(&serveraddr,0,sizeof(serveraddr));
    memset(&clientaddr,0,sizeof(clientaddr));
    serveraddr.sin_family=AF_INET;
    serveraddr.sin_addr.s_addr=inet_addr("127.0.0.1");
    serveraddr.sin_port=htons(1234);
    if(-1==bind(serverfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr))){
        perror("bind");
        close(serverfd);
        sleep(1);
        goto socket;
    }

    socklen_t clientaddrlen=sizeof(clientaddr);
    while(1){
        recvfrom(serverfd,(void*)&buf,sizeof(buf),0,(struct sockaddr*)&clientaddr,&clientaddrlen);//记录发送方地址,无需实现直到发送方地址
        printf("\033[2J\033[H\033[38;2;255;125;50m%d\n%f\n%f\n%s\n",buf.a,buf.b,buf.c,buf.d);
        buf.a ++; buf.b += 1.1; buf.c += 0.1; sprintf(buf.d,"Server %d times",buf.a);
        sleep(3);
        sendto(serverfd,(void*)&buf,sizeof(buf),0,(struct sockaddr*)&clientaddr,sizeof(clientaddr));//根据记录发送方地址回执
        sleep(3);
    }
}
//server在收发之间没有sleep,不要设置成非堵塞,否则recvfrom会迅速收空接收缓冲区然后报错。

2. client端可采取堵塞和非堵塞2中方案:

堵塞方案中,通过函数setsockopt定义超时,防止无限堵塞

clientblocking.c

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<signal.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<sys/time.h>

int clientfd;
struct sockaddr_in serveraddr, tempaddr;
typedef struct{
    int a;
    float b;
    double c;
    char d[32];
}buffer;
buffer buf;
struct timeval timeout;
void handler(int sig){
    close(clientfd);
    exit(EXIT_SUCCESS);
}
int main(){
    signal(SIGINT,handler);
socket:
   if(-1==(clientfd=socket(AF_INET,SOCK_DGRAM,0))){
       perror("socket");
       exit(EXIT_FAILURE);
   }
   timeout.tv_sec=1;
   timeout.tv_usec=0;
   if(-1==setsockopt(clientfd,SOL_SOCKET,SO_RCVTIMEO,(void*)&timeout,sizeof(timeout))){//给client的revofrom设定超时时间1s
    perror("setsockopt_timeout");
    sleep(1);
    goto socket;
   }
   memset(&buf,0,sizeof(buf));
   memset(&serveraddr,0,sizeof(serveraddr));
   memset(&tempaddr,0,sizeof(tempaddr));
   serveraddr.sin_family=AF_INET;
   serveraddr.sin_addr.s_addr=inet_addr("127.0.0.1");
   serveraddr.sin_port=htons(1234);
   socklen_t tempaddrlen=sizeof(tempaddr);
   while(1){
        buf.a ++; buf.b += 1.1; buf.c += 0.1; sprintf(buf.d,"Client %d times",buf.a);
        sendto(clientfd,&buf,sizeof(buf),0,(struct sockaddr*)&serveraddr,sizeof(serveraddr));//客户端预先知道的服务器地址,用于发送数据
        sleep(1);
        //recvfrom 函数默认是阻塞的
        //如果server终止运行,client接收数据超时报错,反过来server无需设置超时,因为server是先收后发
        if(-1==recvfrom(clientfd,&buf,sizeof(buf),0,(struct sockaddr*)&tempaddr,&tempaddrlen)){//记录发送方的地址,不一定是预先知道的服务器地址
            perror("recvfrom");
        }
        else
            printf("\033[2J\033[H\033[38;2;50;125;255m%d\n%f\n%f\n%s\n",buf.a,buf.b,buf.c,buf.d);
   }
}

fcntl函数或者recvfrom(,,,MSG_DONTWAIT,,)定义非堵塞方案,非堵塞方案中,如果接收缓冲区为空(终止server运行即可让client的接收缓冲区迅速为空),会报错提醒。

clientnonblocking.c

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<signal.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<sys/time.h>
#include<fcntl.h>

int clientfd;
struct sockaddr_in serveraddr, tempaddr;
typedef struct{
    int a;
    float b;
    double c;
    char d[32];
}buffer;
buffer buf;
struct timeval timeout;
void handler(int sig){
    close(clientfd);
    exit(EXIT_SUCCESS);
}
int main(){
    signal(SIGINT,handler);
//socket:
   if(-1==(clientfd=socket(AF_INET,SOCK_DGRAM,0))){//IPv4,UDP,0
       perror("socket");
       exit(EXIT_FAILURE);
   }
  
   memset(&buf,0,sizeof(buf));
   memset(&serveraddr,0,sizeof(serveraddr));
   memset(&tempaddr,0,sizeof(tempaddr));
   serveraddr.sin_family=AF_INET;
   serveraddr.sin_addr.s_addr=inet_addr("127.0.0.1");
   serveraddr.sin_port=htons(1234);

   socklen_t tempaddrlen=sizeof(tempaddr);
   while(1){
        buf.a ++; buf.b += 1.1; buf.c += 0.1; sprintf(buf.d,"Client %d times",buf.a);
        //It is not necessary sendto(,,,MSG_DONTWAIT,,) because UDP sendto is non-blocking
        if(-1==sendto(clientfd,&buf,sizeof(buf),0,(struct sockaddr*)&serveraddr,sizeof(serveraddr))){//客户端预先知道的服务器地址,用于发送数据
            perror("sendto");
        }
        sleep(1);
        if(-1==recvfrom(clientfd,&buf,sizeof(buf),MSG_DONTWAIT,(struct sockaddr*)&tempaddr,&tempaddrlen)){//记录发送方的地址,但是不一定是预先知道的服务器地址
            perror("recvfrom");//recvfrom: Resource temporarily unavailable
        }
        else
            printf("\033[2J\033[H\033[38;2;50;125;255m%d\n%f\n%f\n%s\n",buf.a,buf.b,buf.c,buf.d);
        sleep(1);
   }
}

/* // the code below equals to recvfrom(,,,MSG_DONTWAIT,,)
   int flag;//设置为非阻塞套接字,即sendto和recvfrom非堵塞,直到sendto缓冲区满或者recvfrom缓冲区空报错
   if(-1==(flag=fcntl(clientfd,F_GETFL,0))){
       perror("fcntl");
       sleep(1);
       goto socket;
   }
   if(-1==fcntl(clientfd,F_SETFL,flag|O_NONBLOCK)){
       perror("fcntl");
       sleep(1);
       goto socket;
   }
*/

gcc -Wall -O3 xxx.c -o xxx.elf编译链接。。

./xxx.elf运行。

如果只运行strace ./server.elf可监控到server.elf进程堵塞在recvfrom

root@xyz:/media/xyz/disk_d/LinuxKnowledge/LinuxC/chapter2025/socket/UDP_block# strace ./server.elf 
execve("./server.elf", ["./server.elf"], 0x7ffd84e3ffe0 /* 28 vars */) = 0
brk(NULL)                               = 0x58020d300000
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x777203b06000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (没有那个文件或目录)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=121883, ...}) = 0
mmap(NULL, 121883, PROT_READ, MAP_PRIVATE, 3, 0) = 0x777203ae8000
close(3)                                = 0
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\220\243\2\0\0\0\0\0"..., 832) = 832
pread64(3, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0"..., 784, 64) = 784
fstat(3, {st_mode=S_IFREG|0755, st_size=2125328, ...}) = 0
pread64(3, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0"..., 784, 64) = 784
mmap(NULL, 2170256, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x777203800000
mmap(0x777203828000, 1605632, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x28000) = 0x777203828000
mmap(0x7772039b0000, 323584, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1b0000) = 0x7772039b0000
mmap(0x7772039ff000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1fe000) = 0x7772039ff000
mmap(0x777203a05000, 52624, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x777203a05000
close(3)                                = 0
mmap(NULL, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x777203ae5000
arch_prctl(ARCH_SET_FS, 0x777203ae5740) = 0
set_tid_address(0x777203ae5a10)         = 20581
set_robust_list(0x777203ae5a20, 24)     = 0
rseq(0x777203ae6060, 0x20, 0, 0x53053053) = 0
mprotect(0x7772039ff000, 16384, PROT_READ) = 0
mprotect(0x5801db2d4000, 4096, PROT_READ) = 0
mprotect(0x777203b44000, 8192, PROT_READ) = 0
prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0
munmap(0x777203ae8000, 121883)          = 0
rt_sigaction(SIGINT, {sa_handler=0x5801db2d21f0, sa_mask=[INT], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x777203845330}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
socket(AF_INET, SOCK_DGRAM, IPPROTO_IP) = 3
bind(3, {sa_family=AF_INET, sin_port=htons(1234), sin_addr=inet_addr("127.0.0.1")}, 16) = 0
recvfrom(3, 

此时启动clinetBlocking.elf 堵塞撤销,因为clientBlocking.elf发送UDP报文。

tcpdump -i lo port  1234 抓取UDP报文

ngrep -d lo port 1234抓取通讯数据

单独运行strace ./clientNonBlocking.elf可观察recvfrom不堵塞,如果接收区为空,recvfrom报错

root@xyz:/media/xyz/disk_d/LinuxKnowledge/LinuxC/chapter2025/socket/UDP_nonblock# strace ./clientNonBlocking.elf 
execve("./clientNonBlocking.elf", ["./clientNonBlocking.elf"], 0x7ffce889dd40 /* 28 vars */) = 0
brk(NULL)                               = 0x59e588bd5000
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7e48e00ac000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (没有那个文件或目录)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=121883, ...}) = 0
mmap(NULL, 121883, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7e48e008e000
close(3)                                = 0
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\220\243\2\0\0\0\0\0"..., 832) = 832
pread64(3, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0"..., 784, 64) = 784
fstat(3, {st_mode=S_IFREG|0755, st_size=2125328, ...}) = 0
pread64(3, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0"..., 784, 64) = 784
mmap(NULL, 2170256, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7e48dfe00000
mmap(0x7e48dfe28000, 1605632, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x28000) = 0x7e48dfe28000
mmap(0x7e48dffb0000, 323584, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1b0000) = 0x7e48dffb0000
mmap(0x7e48dffff000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1fe000) = 0x7e48dffff000
mmap(0x7e48e0005000, 52624, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7e48e0005000
close(3)                                = 0
mmap(NULL, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7e48e008b000
arch_prctl(ARCH_SET_FS, 0x7e48e008b740) = 0
set_tid_address(0x7e48e008ba10)         = 20452
set_robust_list(0x7e48e008ba20, 24)     = 0
rseq(0x7e48e008c060, 0x20, 0, 0x53053053) = 0
mprotect(0x7e48dffff000, 16384, PROT_READ) = 0
mprotect(0x59e57b2f3000, 4096, PROT_READ) = 0
mprotect(0x7e48e00ea000, 8192, PROT_READ) = 0
prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0
munmap(0x7e48e008e000, 121883)          = 0
rt_sigaction(SIGINT, {sa_handler=0x59e57b2f14a0, sa_mask=[INT], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7e48dfe45330}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
socket(AF_INET, SOCK_DGRAM, IPPROTO_IP) = 3
sendto(3, "\1\0\0\0\315\314\214?\232\231\231\231\231\231\271?Client 1 times\0\0"..., 48, 0, {sa_family=AF_INET, sin_port=htons(1234), sin_addr=inet_addr("127.0.0.1")}, 16) = 48
clock_nanosleep(CLOCK_REALTIME, 0, {tv_sec=1, tv_nsec=0}, 0x7fff702bc010) = 0
recvfrom(3, 0x59e57b2f4060, 48, MSG_DONTWAIT, 0x59e57b2f4090, [16]) = -1 EAGAIN (资源暂时不可用)
dup(2)                                  = 4
fcntl(4, F_GETFL)                       = 0x2 (flags O_RDWR)
getrandom("\xa3\x31\x17\x26\x50\xae\x9d\xfd", 8, GRND_NONBLOCK) = 8
brk(NULL)                               = 0x59e588bd5000
brk(0x59e588bf6000)                     = 0x59e588bf6000
fstat(4, {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0x3), ...}) = 0
write(4, "recvfrom: Resource temporarily u"..., 43recvfrom: Resource temporarily unavailable
) = 43
close(4)                                = 0
clock_nanosleep(CLOCK_REALTIME, 0, {tv_sec=1, tv_nsec=0}, 0x7fff702bc010) = 0
sendto(3, "\2\0\0\0\315\314\f@\232\231\231\231\231\231\311?Client 2 times\0\0"..., 48, 0, {sa_family=AF_INET, sin_port=htons(1234), sin_addr=inet_addr("127.0.0.1")}, 16) = 48
clock_nanosleep(CLOCK_REALTIME, 0, {tv_sec=1, tv_nsec=0}, 0x7fff702bc010) = 0
recvfrom(3, 0x59e57b2f4060, 48, MSG_DONTWAIT, 0x59e57b2f4090, [16]) = -1 EAGAIN (资源暂时不可用)
dup(2)                                  = 4
fcntl(4, F_GETFL)                       = 0x2 (flags O_RDWR)
fstat(4, {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0x3), ...}) = 0
write(4, "recvfrom: Resource temporarily u"..., 43recvfrom: Resource temporarily unavailable
) = 43
close(4)                                = 0
clock_nanosleep(CLOCK_REALTIME, 0, {tv_sec=1, tv_nsec=0},

UDP的sendto是non-blocking函数,无论设置sendto(,,,MSG_DONTWAIT,,)与否。

gdb ./server.elf逐步调试观察到在调用bind函数后,socket被创建。

1)socket()执行完后,bind()执行之前,UDP socket localhost:1234未被kernel创建

2)bind()执行完后,recvfrom()执行之前,UDP socket localhost:1234被kernel创建

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值