首页 > 代码库 > Socket编程实践(14) --UDP编程基础(2)

Socket编程实践(14) --UDP编程基础(2)

使用UDP注意事项

1.UDP报文可能会丢失、重复、乱序

 

2.UDP缺乏流量控制:当缓冲区写满以后,由于UDP没有流量控制机制,因此会覆盖缓冲区。

 

3.UDP协议数据报文截断:如果接收到的UDP数据报大于缓冲区,报文可能被截断,后面的部分会丢失。

 

4.使用UDP: recvfrom返回0,不代表连接关闭,因为UDP是无连接的。

    而且sendto可以发送数据0包(只含有UDP首部[20字节]);

 

5.ICMP异步错误

   观察现象:关闭UDP服务端,启动客户端,从键盘接受数据后,再发送数据。UDP客户端阻塞在recvfrom位置;

说明:

    1)UDP发送报文的时,只把数据copy到发送缓冲区。在服务器没有起来的情况下,可以发送成功

    2)所谓ICMP异步错误是指:发送的报文的时候,没有错误,接受报文recvfrom的时候,回收到ICMP应答.

    3)异步的错误,是无法返回未连接的套接字

 

6.UDP调用connect

    1)UDP调用connet,并没有三次握手,只是维护了一个(和对等方的)状态信息

    2)一但调用connect,就可以使用send函数

//注意点3实验:
#include "commen.h"

int main()
{
    //创建UDP socket
    int sockfd = socket(AF_INET,SOCK_DGRAM,0);
    if (sockfd == -1)
    {
        err_exit("socket error");
    }

    //填写server端信息
    struct sockaddr_in serverAddr;
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_port = htons(9001);
    serverAddr.sin_addr.s_addr =INADDR_ANY;
    //绑定server端端口号
    if (bind(sockfd,(struct sockaddr *)&serverAddr,sizeof(serverAddr)) == -1)
    {
        err_exit("bind error");
    }

    //向自己发送数据
    if (sendto(sockfd,"1234",4,0,(struct sockaddr *)&serverAddr,sizeof(serverAddr)) == -1)
    {
        err_exit("sendto error");
    }


  //接收一个字节,但想通过四次接收
  //在接收数据时,如果缓冲区较小,则将截断报文,后面的报文将丢失.
    char buf[1];
    for (int i = 0; i < 4; ++i)
    {
        int readCount = recvfrom(sockfd,buf,sizeof(buf),0,NULL,NULL);
        if (readCount == -1)
        {
            err_exit("recvfrom error");
        }
        cout << "Have recv " << readCount << " bytes." << endl;
        write(STDOUT_FILENO,buf,readCount);
    }

    return 0;
}


//注意点5实验:
#include "commen.h"

int main()
{
    //创建UDP socket
    int sockfd = socket(AF_INET,SOCK_DGRAM,0);
    if (sockfd == -1)
    {
        err_exit("socket error");
    }

    //填写server端信息
    struct sockaddr_in serverAddr;
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_port = htons(9001);
    serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");

    char buf[BUFSIZ];
    //从键盘接收数据,注意:是在服务器没有起来的情况之下发送数据
    while (fgets(buf,sizeof(buf),stdin) != NULL)
    {
        //向server端传送数据
        if (sendto(sockfd,buf,strlen(buf),0,(struct sockaddr *)&serverAddr,sizeof(serverAddr)) == -1)
        {
            err_exit("sendto error");
        }
        else
        {
            cout << "send success...!" << endl;
        }

        memset(buf,0,sizeof(buf));
        int readCount = 0;
        //从server端接收数据,并且忽略server端信息recvfrom(xx,xx,xx,xx,NULL,NULL)
        if ((readCount = recvfrom(sockfd,buf,sizeof(buf),0,NULL,NULL)) == -1)
        {
            err_exit("recvfrom error");
        }
        else
        {
            cout << "recv success...!" << endl;
        }
        buf[readCount] = 0;
        //将数据写到屏幕
        fputs(buf,stdout);
    }

    return 0;
}


//注意点6实验(注意查看第18~26行)
#include "commen.h"

int main()
{
    //创建UDP socket
    int sockfd = socket(AF_INET,SOCK_DGRAM,0);
    if (sockfd == -1)
    {
        err_exit("socket error");
    }

    //填写server端信息
    struct sockaddr_in serverAddr;
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_port = htons(9001);
    serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");

    //添加connect调用
    if (connect(sockfd,(struct sockaddr *)&serverAddr,sizeof(serverAddr)) == -1)
    {
        err_exit("connect error");
    }
    else
    {
        cout << "connect success!" << endl;
    }

    char buf[BUFSIZ];
    //从键盘接收数据,注意:是在服务器没有起来的情况之下发送数据
    while (fgets(buf,sizeof(buf),stdin) != NULL)
    {
        //向server端传送数据
        if (sendto(sockfd,buf,strlen(buf),0,(struct sockaddr *)&serverAddr,sizeof(serverAddr)) == -1)
        {
            err_exit("sendto error");
        }
        else
        {
            cout << "send success...!" << endl;
        }

        memset(buf,0,sizeof(buf));
        int readCount = 0;
        //从server端接收数据,并且忽略server端信息recvfrom(xx,xx,xx,xx,NULL,NULL)
        if ((readCount = recvfrom(sockfd,buf,sizeof(buf),0,NULL,NULL)) == -1)
        {
            err_exit("recvfrom error");
        }
        else
        {
            cout << "recv success...!" << endl;
        }
        buf[readCount] = 0;
        //将数据写到屏幕
        fputs(buf,stdout);
    }

    return 0;
}

实验结果:还是需要在server端不启动的情况下(此时返回一个ICMP的应答报文)

 

 

实验:直接调用send函数(不再是sendto了-33行)

#include "commen.h"

int main()
{
    //创建UDP socket
    int sockfd = socket(AF_INET,SOCK_DGRAM,0);
    if (sockfd == -1)
    {
        err_exit("socket error");
    }

    //填写server端信息
    struct sockaddr_in serverAddr;
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_port = htons(9001);
    serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");

    //添加connect调用
    if (connect(sockfd,(struct sockaddr *)&serverAddr,sizeof(serverAddr)) == -1)
    {
        err_exit("connect error");
    }
    else
    {
        cout << "connect success!" << endl;
    }

    char buf[BUFSIZ];
    //从键盘接收数据,注意:是在服务器没有起来的情况之下发送数据
    while (fgets(buf,sizeof(buf),stdin) != NULL)
    {
        //向server端传送数据
        if (send(sockfd,buf,strlen(buf),0) == -1)
        {
            err_exit("sendto error");
        }

        memset(buf,0,sizeof(buf));
        int readCount = 0;
        //从server端接收数据,并且忽略server端信息recvfrom(xx,xx,xx,xx,NULL,NULL)
        if ((readCount = recvfrom(sockfd,buf,sizeof(buf),0,NULL,NULL)) == -1)
        {
            err_exit("recvfrom error");
        }

        buf[readCount] = 0;
        //将数据写到屏幕
        fputs(buf,stdout);
    }

    return 0;
}

 

结论:

1.UDP也可以调用connet:(与不调用connect的区别)UDP客户端调用了connect以后,不会阻塞在recvfrom函数这里。

2.一但调用connect,就可以使用send函数

3.UDP协议数据截断:如果接收到的数据报大于缓冲区,则报文被截断,后面的部分会丢失。

Socket编程实践(14) --UDP编程基础(2)