UDP套接字的使用介绍

UDP套接字相比于TCP套接字具有不同的特征。
    1、UDP套接字在使用之前不需要进行连接,即对应于客户端不需要进行connect,创建socket之后直接发送信息;服务器在绑定IP和端口之后在循环中不需要accept进行阻塞,直接利用接收函数阻塞接收信息,并且在完成通讯之后不需要进行close;
    2、UDP是尽力而为的通信。
    3、在发送的时候UDP需要指定一个比较合适的缓冲大小,不然超过缓冲的将会被丢弃。
    4、UDP发送数据的时候采用的sento()和recvfrom()函数,而不是send()和recv()。

UDP发送和接收数据的函数
/**
*    socket:套接字fd
*    msg:指向要发送的字节序列
*    msgLength:要发送的字节序列的长度
*    flag:
*        flags取值有:
*        0: 与write()无异
*        MSG_DONTROUTE:告诉内核,目标主机在本地网络,不用查路由表
*        MSG_OOB:指明发送的是带外信息
*    destAddr:指向要发送的目的地址的sockaddr的指针
*    addrLen:地址信息的长度
**/
ssize_t  sendto(int socket, const void *msg, size_t msgLength, int flag,
                        const struct sockaddr *destAddr, socklen_t addrLen);
/**
*    前四个参数和sendto()一样
*    msg:要保存的字节序的位置
*    flags取值有:
*        0:常规操作,与read()相同
*        MSG_OOB:指明发送的是带外信息
*        MSG_PEEK:可以查看可读的信息,在接收数据后不会将这些数据丢失
*    srcAddr:通过这个指针可以得到send的客户端的地址
*    addrLen:地址结构的长度
**/
ssize_t  recvfrom((int socket, const void *msg, size_t msgLength, int flag,
                        const struct sockaddr *srcAddr, socklen_t addrLen);
以上两个函数的返回值分别为发送/接收的数据的字节数;
并且以上两个函数需要注意的部分是指定了msg的长度之后,就只会发送或者接收那么多,剩下的就会被丢弃。

下面的例子中,客户端将发送一段字节,发送给服务器后,服务器将这些字节原封不动的返回给客户端,客户端回显。
客户端代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netdb.h>

int main(int argc, char *argv[])
{
    //test argument num
    if(argc < 3 || argc > 4)
    {
        printf("<server address/name> <echo word> [derver port/service]\n");
        exit(1);
    }

    char *server = argv[1];
    char *echoString = argv[2];

    size_t echoStringLen = strlen(echoString);

    //Third argument
    char *servPort = (argc == 4) ? argv[4] : "echo";

    //通过设置addrinfo结构体告诉系统我们需要什么样的地址
    struct addrinfo addrKind;
    memset(&addrKind, 0, sizeof(addrKind));        //结构体置0
    addrKind.ai_family = AF_UNSPEC;        //任何地址族都可以
    addrKind.ai_socktype = SOCK_DGRAM;    //只接受UDP报文
    addrKind.ai_protocol = IPPROTO_UDP;        //协议采用UDP
    //其他没有设置的字段都为0

    //通过getaddrinfo函数得到地址
    struct addrinfo *servAddr;    //解析的服务器的地址链表
    int rtnVal = getaddrinfo(serve, servPort, &addrKind, &servAddr);
    if(rtnVal != 0)
    {
        printf("%s\n", gai_strerror(rtnVal));    //利用gai_strerror()函数获取错误信息
        exit(1);
    }

    //我们根据UDP的信息,创建UDP套接字
    int sock = socket(servAddr->ai_family, servAddr->ai_socktype, servAddr->ai_protocol);    //创建UDP套接字
    if(sock < 0)
    {
        printf("socket() error\n");
        exit(1);
    }

    //创建UDP套接字之后直接发送数据,不需要进行connect连接
    //由于之前已经知道了服务器的地址,直接向这个地址发送数据
    ssize_t numBytes = sendto(sock, echoString, echoStringLen, 0,servAddr->ai_addr, servAddr->ai_addrlen);
    if(numBytes < 0 || numBytes != echoStringLen)
    {
        printf("sendto() error\n");
        exit(1);
    }

    //发送完了,现在我们需要接受一个服务器发来的回应
    struct sockaddr fromAddr;
    socklen_t fromAddrLen = sizeof(sockaddr);
    char buffer[MAXSIZE + 1];
    numBytes = recvfrom(sock, buffer, MAXSIZE, 0, &fromAddr, &fromAddrLen);
    if(numBytes < 0 || numBytes != echoStringLen)
    {
        printf("recvfrom() error\n");
        exit(1);
    }

    //接下来需要比较主动发送的服务器地址和recvfrom的地址是不是一样
    //暂时用一个没有实现的函数进行表示
    if(!SockAddrsEqual(servAddr->ai_addr, fromAddr))
    {
        printf("received package from unknow source\n");
        exit(1);
    }

    freeaddrinfo(servAddr);    //销毁地址链表
    buffer[echoStringLen] = '\0';
    printf("received: %s\n", buffer);

    close(sock);
    exit(0);
}

服务器代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netdb.h>

int main(int argc, char *argv[])
{
    if(argc != 2)
    {
        printf("argument need <server port>\n");
        exit(1);
    }

    char *service = argv[1];
    //构建addrinfo用来解析服务器的地址
    struct addrinfo addrKind;
    memset(&addrKind, 0, sizeof(addrKind));        //结构体置0
    addrKind.ai_family = AF_UNSPEC;        //任何地址族都可以
    addrKind.ar_flags = AI_PASSIVE;        //可以接受任何地址
    addrKind.ai_socktype = SOCK_DGRAM;    //只接受UDP报文
    addrKind.ai_protocol = IPPROTO_UDP;        //协议采用UDP
    //其他没有设置的字段都为0

    struct addrinfo *servAddr;    //解析的服务器的地址链表
    int rtnVal = getaddrinfo(NULL, service, &addrKind, &servAddr);    //获取服务器的地址,因为后面需要绑定服务器的地址和端口
    if(rtnVal != 0)
    {
        printf("%s\n", gai_strerror(rtnVal));    //利用gai_strerror()函数获取错误信息
        exit(1);
    }

    int sock = socket(servAddr->ai_family, servAddr->ai_socktype, servAddr->ai_protocol);    //创建UDP套接字
    if(sock < 0)
    {
        printf("socket() error\n");
        exit(1);
    }

    //绑定本地的地址和端口
    if(bind(sock, servAddr->ai_addr, servAddr->ai_addrlen) < 0)
    {
        printf("bind() error\n");
        exit(1);
    }
    freeaddrinfo(servAddr);    //销毁地址链表

    for(; ;)    //服务器一直循环接收
    {
        struct sockaddr clntAddr;
        socklen_t clntAddrLen = sizeof(sockaddr);

        //阻塞,接收客户端的信息
        char buffer[MAXSIZE + 1];
        ssize_t recenumBytes = recvfrom(sock, buffer, MAXSIZE, 0, &clntAddr, &clntAddrLen);
        if(recenumBytes < 0)
        {
            printf("recvfrom() error\n");
            exit(1);
        }

        //接收到了,将该信息发送给客户端
        ssize_t sendnumBytes = sendto(sock, buffer, numBytes, 0, clntAddr, clntAddrLen);
        if(sendnumBytes < 0 || sendnumBytes != recenumBytes)
        {
            printf("sendto() error\n");
            exit(1);
        }
    }
}


  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值