Linux高级编程——网络通信UDP

基于 UDP 协议的通信

User Datagram Protocol,用户数据报协议。

UDP 协议是无连接,传输不可靠(尽力而为),但传输效率很高。

UDP 协议传输不可靠:

  • 内部没有流量和拥塞控制机制,一旦接收方缓冲区满,它不会通知和阻塞发送方,就会发生丢包。
  • 由于接受不会对接收的数据包进行排序处理,只是简单的按照数据包的到达顺序接收处理,所以可能会发生乱序问题。

UDP 协议不存在粘包问题,因为它是严格以数据包为单位进行手法处理的。

UDP 协议支持空数据包,可以用作通知或心跳包(或保活)。

UDP 协议支持组播和广播(Broadcast)。广播地址分为网段广播地址和受限广播地址(255.255.255.255)。广播地址只能作为目标地址使用,而不能作为源地址。

UDP 协议通常采用对等的通信方式,一般不区分服务器端和客户端。

UDP 协议通信过程与手机短信很类似。

基于 UDP 协议通信的实现步骤:
  1. 创建套接字,相当于每一部手机,调用 socket 函数实现,不用花一分钱!
  2. 给套接字绑定地址,相当于给手机上号。调用 bind 函数实现,不用花一分钱!
  3. 收发数据,相当于收发短信。调用 sendto/ recvfromsend/recvwrite/read 函数实现,不用花一分钱!
  4. 关闭套接字,相当于毁掉手机。调用close 函数实现,不用花一分钱

注意:作为先发消息的一方,上面的第2步可以不用做,因为在第3步调用发送相关的函数时会进行隐式绑定。

TCP 端口号和UDP端口号不存在冲突问题!

  • wireshark
  • sniffer
#include 
#include 
#include 
#include 
#include 
#include 
#include 


struct Student
{
    int sno;
    char name[21];
    float sight;
};


int main()
{
    // 创建一个新的数据报式套接字,用于 UDP 通信
    //第二个数据为数据报套接字类型
    int sock = socket(AF_INET, SOCK_DGRAM, 0);

    // 设置套接字属性,以开启对外广播
    int val = 1;//为0关闭,为非0开启广播
    setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &val, sizeof(val));

    if(-1 == sock)
    {
        perror("socket");
        return 1;
    }

    struct sockaddr_in myaddr;
    myaddr.sin_family = AF_INET;
    myaddr.sin_addr.s_addr = INADDR_ANY;
    myaddr.sin_port = htons(8888);

    if(-1 == bind(sock, (struct sockaddr*)&myaddr, sizeof(myaddr)))
    {
        perror("bind");
        return 1;
    }

    char msg[1025];
    int ret;

    // 发送消息
    // struct sockaddr_in youraddr;
    // youraddr.sin_family = AF_INET;
    // youraddr.sin_addr.s_addr = inet_addr("192.168.0.56");//指定IP
    // youraddr.sin_port = htons(8888);

    // 和 TCP 协议不一样,这里的 connect 函数并不是连接,内部没有发生任何通信行为。
    // 它只是将套接字和一个目标地址做一个关联而已。关联后调用 send 和 recv 就只能跟这个目标地址进行收发数据。
    // 当然,如果需要更改这个关联,随时可以再次调用 connect 函数。
    // connect(sock, (struct sockaddr*)&youraddr, sizeof(youraddr));   
    // send(sock, msg, strlen(msg), 0);

    // 更常用,可以方便的给任意目标地址发送消息
    // sendto(sock, msg, strlen(msg), 0, (struct sockaddr*)&youraddr, sizeof(youraddr));

    // 可以使用 connect 函数对后续 recv 和 recvfrom 函数接收到的消息进行过滤
    // struct sockaddr_in youraddr;
    // youraddr.sin_family = AF_INET;
    // youraddr.sin_addr.s_addr = inet_addr("192.168.0.213");
    // youraddr.sin_port = htons(8888);
    // connect(sock, (struct sockaddr*)&youraddr, sizeof(youraddr));

    struct Student s = {1001, "张三"};  // 顺序初始化
    struct Student s2 = {.name = "李四", .sno = 1002};  // 指定初始化

    struct sockaddr_in youraddr;
    youraddr.sin_family = AF_INET;
    //youraddr.sin_addr.s_addr = inet_addr("192.168.0.255");  // 当前局域网的广播地址
    youraddr.sin_addr.s_addr = inet_addr("255.255.255.255");  // 受限广播地址
    youraddr.sin_port = htons(8888);
    
    //while(1)
        sendto(sock, &s, sizeof(s), 0, (struct sockaddr*)&youraddr, sizeof(youraddr));

    struct sockaddr_in addr;
    socklen_t len = sizeof(addr);

    char name[25];
    // ret = recv(sock, msg, sizeof(msg) - 1, 0);
    ret = recvfrom(sock, name, 25, 0, (struct sockaddr*)&addr, &len);//接收时获取对方的信息
    
    if(ret >= 0)
    {
        msg[ret] = '\0';
        //网络字节区inet将整形转换成字符型
        printf("\n%s:%hu说:%s\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port), name + 4);
    }

	//先发送方
    // while(1)
    // {
    //     struct sockaddr_in addr;
    //     socklen_t len = sizeof(addr);
    //     // ret = recv(sock, msg, sizeof(msg) - 1, 0);
    //     ret = recvfrom(sock, msg, sizeof(msg) - 1, 0, (struct sockaddr*)&addr, &len);
        
    //     if(ret >= 0)
    //     {
    //         msg[ret] = '\0';
    //         printf("\n%s:%hu说:%s\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port), msg);

    //         strcpy(msg, "你是傻子!\n");
    //         // sendto(sock, msg, strlen(msg), 0, (struct sockaddr*)&addr, sizeof(addr));
    //         // send 之前要 connect
    //         //printf("%ld\n", send(sock, msg, strlen(msg), 0));
    //     }
    // }

    close(sock);

    return 0;
}

你可能感兴趣的:(Linux高级编程,udp,linux,网络)