标签 udp 下的文章

“”

sendto(),把UDP数据报发给指定地址和端口。
函数原型
int sendto (int s, const void buf, int len, unsigned int flags, const struct sockaddr to, int tolen);
参数说明
s: socket描述符。
buf: UDP数据报缓存地址。
len: UDP数据报长度。
flags: 该参数一般为0。
to: sendto()函数参数,struct sockaddr_in类型,指明UDP数据发往哪里报。
tolen: 对方地址长度,一般为:sizeof(struct sockaddr_in)。
函数返回值
对于sendto()函数,成功则返回实际传送出去的字符数,失败返回-1,错误原因存于errno 中。

发送udp数据示例

int udp_send(string ip, int port, string data)
{
    int sockfd;
    if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
    {
        printf("socket error\n");
        return -1;
    }
    struct sockaddr_in addrto;
    bzero(&addrto, sizeof(struct sockaddr_in));
    addrto.sin_family = AF_INET;
    addrto.sin_addr.s_addr = htonl(INADDR_ANY);
    addrto.sin_port = htons(9999);
    //设置广播
    int yes = 1;
    setsockopt(m_nsockfd, SOL_SOCKET, SO_BROADCAST, &yes, sizeof(yes));    
    if(bind(m_nsockfd, (struct sockaddr *) & (addrto), sizeof(struct sockaddr_in)) == -1)
    {
        printf("udp_listen bind error...\n");
        return -1;
    }  
    int addrlen = sizeof(sockaddr_in);
    struct sockaddr_in addr;
    bzero(&addr, sizeof(struct sockaddr_in));    
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = inet_addr(ip.c_str());
    return sendto(m_nsockfd, data.data(), data.length(), 0, (struct sockaddr *)&addr, addrlen);
}

php 实现udp发送数据,主要使用 socket_sendto就可以了。

  $socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
  //PHP UDP socket_ recv() timeout
  socket_set_option($socket,SOL_SOCKET,SO_RCVTIMEO,array("sec"=>2,"usec"=>0));

  //Set socket options.
  socket_set_nonblock($socket);
  socket_set_option($socket, SOL_SOCKET, SO_BROADCAST, 1);
  socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1);
  if (defined('SO_REUSEPORT'))
      socket_set_option($socket, SOL_SOCKET, SO_REUSEPORT, 1);

  //Bind to any address & rand port 
  socket_bind($socket, '0.0.0.0', 9999)
  
  $senddata = "hello";
  socket_sendto($socket,$sendcmd, strlen($senddata), 0, "127.0.0.1", 54321);

php 设置socket 非阻塞

socket_set_nonblock($socket);

php 设置udp 广播

socket_set_option($socket, SOL_SOCKET, SO_BROADCAST, 1);

php 设置 udp 地址端口重用

socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1);

php 设置udp_recv接收超时

  socket_set_option($socket,SOL_SOCKET,SO_RCVTIMEO,array("sec"=>2,"usec"=>0));

php 字符串转大写

uppercase

php 判断http post

 function is_post()
{
    return isset($_SERVER['REQUEST_METHOD']) && strtoupper($_SERVER['REQUEST_METHOD'])=='POST';    
}

php 取随机数

echo(rand();
echo(rand();
echo(rand(10,100))

php socket 官方链接地址
https://www.php.net/manual/en/book.sockets.php

php explode 示例

$arr = explode(":", $buf);

php trim 示例 

$str = "Hello Const!";
echo $str . "<br>";
echo trim($str,"Const!");

经常使用UDP发送数据,但一般都是小包,没有太关注过发送大包的问题。突然要发个大包,c++ udp send big packet。
就tcpdump wireshark抓包看了一下。
结果在tcpdump中,出现了这个东西。
UDP, bad length 5455 > 1472
看来UDP包长度超过MTU了。应该是分片了,但分片就分片,只要网络可靠,应该都没什么问题。

另外就是怕UDP的接收或者发送缓冲区不够了。

udp setsockopt sendbuf example

还有多网卡的问题。直接指定网卡,这个要用到struct ifreq.这是linux下的用法。
windows好像没有这个SO_BINDTODEVICE选项,不过可以使用静态路由的方式。可以参考这个链接:
多网卡指定网卡进行UDP通信(添加静态路由解决双网卡问题 )全记录
最后代码如下:

注意 struct ifreq 头文件如下: struct ifreq header file

#include <sys/ioctl.h>
#include <net/if.h>

int cudp::udp_send(string ip, int port, string data)
{
    printf("udp_send ip=%s,port=%d\n",ip.c_str(),port);
    int m_nsockfd;
    if ((m_nsockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
    {
        printf("udp_send error.\n");
        return -1;
    }
    struct ifreq ifr;
    memset(&ifr, 0x00, sizeof(ifr));
    strncpy(ifr.ifr_name, "eth0", 4);
    setsockopt(m_nsockfd, SOL_SOCKET, SO_BINDTODEVICE, (char *)&ifr, sizeof(ifr));

    int addrlen = sizeof(sockaddr_in);
    struct sockaddr_in addr;
    bzero(&addr, sizeof(struct sockaddr_in));    
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = inet_addr(ip.c_str());
    int sendbuff;
    socklen_t optlen;
    int res = 0;

    // Get buffer size
    optlen = sizeof(sendbuff);
    res = getsockopt(m_nsockfd, SOL_SOCKET, SO_SNDBUF, &sendbuff, &optlen);

    if(res == -1)
        printf("Error getsockopt one");
    else
        printf("send buffer size = %d\n", sendbuff);

    sendbuff = 50*1024;

    printf("sets the send buffer to %d\n", sendbuff);
    res = setsockopt(m_nsockfd, SOL_SOCKET, SO_SNDBUF, &sendbuff, sizeof(sendbuff));

    if(res == -1)
        printf("Error setsockopt");
        
    int ret = sendto(m_nsockfd, data.data(), data.length(), 0, (struct sockaddr *)&addr, addrlen);
    printf("sendto ret = %d\n", ret);
    return ret;
}

linux下可以直接拿来使用,用第一个示例就好了。

注意 struct ifreq 头文件如下: struct ifreq header file

#include <sys/ioctl.h>
#include <net/if.h>

这是代码的方法,肯定也有指定路由的办法。有兴趣的话可以了解一下。大致思路就是使用

route add ...
  1. example(TCP, UDP, RAW):

    int sock;
    struct ifreq ifr;
    sock = socket(AF_INET, SOCK_DGRAM, 0);
    memset(&ifr, 0x00, sizeof(ifr));
    strncpy(ifr.ifr_name, "eth0", IFNAMSIZE);
    setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, (char *)&ifr, sizeof(ifr));
    
  2. example(PACKET):

    int sock;
    struct sockaddr_ll sl;
    struct ifreq ifr;
    sock = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_IPV6));
    memset(&sl, 0x00, sizeof(sl));
    memset(&ifr, 0x00, sizeof(ifr));
    sl.sll_family = AF_PACKET;
    sl.sll_protocol = htons(ETH_P_IPV6);
    strncpy(ifr.ifr_name, "eth0", sizeof(ifr.ifr_name));
    ioctl(fd, SIOCGIFINDEX, &ifr);
    sl.sll_ifindex = ifr.ifr_ifindex;
    bind(fd, (struct sockaddr *)&sl, sizeof(sl));
    
  3. example(PACKET):

    int sock;
    struct sockaddr addr;
    sock = socket(PF_PACKET, SOCK_PACKET, ETH_P_IP);
    memset(&addr, 0x00, sizeof(addr));
    addr.sa_family = PF_PACKET;
    strncpy(addr.sa_data, "eth0", sizeof(addr.sa_data));
    bind(sock, &addr, sizeof(addr));
    

SO_BINDTODEVICE套接口选项说明:
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 interface, only packets received from that particular interface are processed by  the socket.  Note  that  this  only works for some socket types, particularly AF_INET sockets.  It is not supported for packet sockets (use normal bind(8) there).
Referenced from:https://blog.csdn.net/networkangle/article/details/52549758?tdsourcetag=s_pcqq_aiomsg

c++ udp send big packet

UDP datagram 大小限制 65,535 bytes

测试socket udp 单次传输数据上限(sendto()函数data不能超过65507字节。udp头占8字节,ip头占20字节,加起来正好65535字节)

void udp_test()
{
    char *p = new char[65507];
    string strsend;
    strsend.assign(p, 65507);
    int ret = udp_send("127.0.0.1", 8888, strsend);
    printf("udp_send ret=%d\n", ret);
    delete[] p;
}

udp_send ip=127.0.0.1,port=8888
send buffer size = 163840
sendto ret = 65507
udp_send ret=65507

void udp_test()
{
    char *p = new char[65508];
    string strsend;
    strsend.assign(p, 65508);
    int ret = udp_send("127.0.0.1", 8888, strsend);
    printf("udp_send ret=%d\n", ret);
    delete[] p;
}

udp_send ip=127.0.0.1,port=8888
send buffer size = 163840
sendto ret = -1
udp_send ret=-1

UDP仅提供数据报作为IP数据包的数据部分,IP数据包具有16位长度的字段,因此将数据限制为2 ^ 16字节(包括标题),或UDP数据部分的65507字节(假设没有ipv4选项) ,除了用UDP处理更大的数据包外,除了将它们分成几个数据包并自行处理重组等之外,没有其他方法。

UDP规范在UDP报头中为您提供16位的数据包大小,这意味着您一次发送的字节数不能超过65k。您必须将数据拆分为多个数据包。使用TCP代替UDP将使事情变得更加简单,因为可以确保完整性和接收顺序。