IPV6 UDP socket 几点注意

在IPv6环境中,使用UDP套接字时,`setsockopt`和`connect()`有特定行为。`connect()`指定默认发送地址,并且若`addr`为空,将绑定到`::1`。不使用`connect()`而仅使用`bind()`时,情况不同。实测显示了这些调用如何影响UDP套接字的状态。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

setsockopt

ipv6 也是可以的  实测可以
SO_BINDTODEVICE
              Bind  this socket to a particular device like “eth0”, as specified in the passed interface name.  If the name is an
              empty string or the option length is zero, the socket device binding is removed.  The passed option is a  variable-
              length  null-terminated interface name string with the maximum size of IFNAMSIZ.  If a socket is bound to an inter‐
              face, only packets received from that particular interface are processed by the socket.  Note that this works  only
              for  some  socket  types, particularly AF_INET sockets.  It is not supported for packet sockets (use normal bind(2)
              there).

connect()

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

这个东西是 UDP的话,addr 表示默认的发送地址, 但只接收这个地址的报文

如果addr 为空也就是 ::
那么 将会绑定到 ::1 这个地址
udp6 0 0 2000:bb::56:1121 ::1:1111

DESCRIPTION
       The  connect() system call connects the socket referred to by the file descriptor sockfd to the address specified by addr.
       The addrlen argument specifies the size of addr.  The format of the address in addr is determined by the address space  of
       the socket sockfd; see socket(2) for further details.

       If  the  socket sockfd is of type SOCK_DGRAM then addr is the address to which datagrams are sent by default, and the only
       address from which datagrams are received.  If the socket is of type SOCK_STREAM or SOCK_SEQPACKET, this call attempts  to
       make a connection to the socket that is bound to the address specified by addr.

       Generally, connection-based protocol sockets may successfully connect() only once; connectionless protocol sockets may use
       connect() multiple times to change their association.  Connectionless sockets may dissolve the association  by  connecting
       to an address with the sa_family member of sockaddr set to AF_UNSPEC (supported on Linux since kernel 2.2).

实测

运行以下程序有这个状态 netstat

udp6 0 0 2000:bb::56:1121 2000:bb::58:1111 ESTABLISHED
这是使用了 connect() 系统调用的结构
如果不使用 connect() 只使用了bind()
udp 0 0 3000::1:1121 :::*

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <arpa/inet.h>
#include <error.h>
#include <errno.h>
#include <unistd.h>

#define ERROR_DO printf("function: %s; line NO.: %d \
    error: %s \n", __FUNCTION__, __LINE__, strerror(errno))


#define PEERADDRSTR "2000:bb::58"
#define LOCLADDRSTR "2000:bb::56"

#define CONNECT_TEST
int start(int *serv_sock, int enable_connect)
{
    struct sockaddr_in6 local;                                                               
    struct sockaddr_in6 peer;                                                               
    int ret = 0;                                                                          
    int local_len = 0;                                                                      
    int peer_len = 0;                                                                      
    struct in6_addr localaddr;
    struct in6_addr peeraddr;

    local_len = sizeof(struct sockaddr_in6);                                                 
    peer_len = local_len;                                                                    

    memset(&local, 0, local_len);                                                             
    memset(&peer, 0, peer_len);                                                             

    local.sin6_family = AF_INET6;                                                             
    peer.sin6_family = AF_INET6;                                                             

    inet_pton(AF_INET6, PEERADDRSTR, &peeraddr);
    inet_pton(AF_INET6, LOCLADDRSTR, &localaddr);
    local.sin6_addr = localaddr ;                                      

    /* if not assign peeraddr that *connect() system call will set peer to ::1

        udp6       0      0 2000:bb::56:1121        ::1:1111
        really did
        use start(, enable) connet call enable
        use `netstat -natu to see the netstat`
    */
#ifndef CONNECT_TEST // not compile this line
    peer.sin6_addr = peeraddr ;                                     
#endif

    local.sin6_port = htons(1121);                     
    peer.sin6_port = htons(1111);                     

    *serv_sock = socket(AF_INET6, SOCK_DGRAM, 0);                                                
    if(*serv_sock < 0){                                                                         
         ERROR_DO;
         printf("[HA UDP] sock init failed.error: %s\n", strerror(errno));       
            return -1;                                                                          
    }                                                                                           

    /* set timeout */                                                                           
    struct timeval tv_out;                                                                      
    tv_out.tv_sec = 5;                                                                         
    tv_out.tv_usec = 0;                                                                         
    if(setsockopt(*serv_sock, SOL_SOCKET, SO_RCVTIMEO, &tv_out, sizeof(tv_out)) < 0){           
            close(*serv_sock);                                                                  
            ERROR_DO;
            return -1;                                                                          
    }                                                                                           
    if(setsockopt(*serv_sock, SOL_SOCKET, SO_BINDTODEVICE, "enp4s0", strlen("enp4s0")+1) < 0){
         ERROR_DO;
         printf ("******please run with sudo or as root********\n");
        close(*serv_sock);                                                          
        exit(0);                                                                  
    }                                                                                   

    int on = 1;                                                                                 
    if(setsockopt(*serv_sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0){                  
            close(*serv_sock);                                                                  
         ERROR_DO;
            return -1;                                                                          
    }                                                                                           

    int recv_buf = 10000000;                                                                    
    if(setsockopt(*serv_sock, SOL_SOCKET, SO_RCVBUF, (char *) &recv_buf, sizeof(recv_buf))< 0){ 
            close(*serv_sock);                                                                  
         ERROR_DO;
            return -1;                                                                          
    }                                                                                           

    ret = bind(*serv_sock, (struct sockaddr *)&local, local_len);                                   
    if(ret < 0){
            close(*serv_sock);                                                                  
         ERROR_DO;
            return -1;                                                                      
    }                                                                                       
    if (enable_connect) {
        printf("enable udp connect\n");
        ret = connect(*serv_sock, (struct sockaddr *)&peer, local_len);                           
        if(ret < 0){                                                                            
                return -1;                                                                      
        }                                                                                       
    }
    return 0;                                                                               

}

int send6(void)
{
     int client_fd;
     struct sockaddr_in6 ser_addr;
     struct sockaddr_in6 cli_addr;

     struct in6_addr seraddr;
     struct in6_addr cliaddr;
    #define BUFF_LEN 20 
    char buf[20] = {"abc"}; 
     client_fd = socket(AF_INET6, SOCK_DGRAM, 0);
     if(client_fd < 0)
     {
         ERROR_DO;
         return -1;
     }

     memset(&ser_addr, 0, sizeof(ser_addr));
     memset(&cli_addr, 0, sizeof(ser_addr));

     inet_pton(AF_INET6, LOCLADDRSTR, &seraddr);
     inet_pton(AF_INET6, PEERADDRSTR, &cliaddr);
     ser_addr.sin6_family = AF_INET6;
     ser_addr.sin6_addr = seraddr;
     ser_addr.sin6_port = htons(1121);

     cli_addr.sin6_family = AF_INET6;
     cli_addr.sin6_addr = cliaddr;
     cli_addr.sin6_port = htons(1111);

     if (bind(client_fd, (struct sockaddr *)&cli_addr, sizeof(cli_addr)))
         ERROR_DO;

    sendto(client_fd, buf, BUFF_LEN, 0, (struct sockaddr*) &ser_addr, sizeof(ser_addr));
    close(client_fd);
    return 0;
}
int main(int argc, char **argv)                                                   
{                                                                                             
    enum {
        DISABLE_CONNECT,
        ENABLE_CONNECT
    };
    int sock;
    int len;
    char buff[256] = {0};

#ifdef CONNECT_TEST 
    start(&sock, ENABLE_CONNECT);
#else
    start(&sock, DISABLE_CONNECT);
#endif
    while (1) {
        // no work very well
        send6();
        len = read(sock, buff, 256);
        if (len < 0)ERROR_DO;
        printf("len: %d %s\n", len, buff);
        sleep(3);
    }
    return 1;
}

Contact: bigjordon%163.com % to @

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值