基本UDP套接口编程

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/YL970302/article/details/83062361

UDP客户—服务器程序函数调用,客户不与服务器建立连接,它只管用函数sendto给服务器发送数据报,该函数要求目的地址(服务器)作为其参数。同样的,服务器不从客户接收连接,它只管调用函数recvfrom,等待来自某客户的数据到达,与数据报一起,recvfrom返回客户的协议地址,所以服务器可以发送响应给正确的客户。

在连接中用到的函数:

头文件:<sys/socket.h>

recvfrom:该函数用数据报发送者的协议地址装填由from所指的套接口地址结构,存储在此套接口地址结构中的字节数也以addrlen所指的整数返回给调用者。

sendto:该函数的参数to是一个含有数据将发往的协议地址(例如:IP地址和端口号)的套接口地址结构,它的大小由addrlen来指定。

两个函数都以读写数据的长度作为函数返回值。

注:sendto的最后一个参数值是整数,而recvfrom的最后一个参数是一指向整数值的指针。                                                          

代码实现:

//服务器端代码
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<assert.h>
#include<sys/socket.h>
#include<arpa/inet.h>

int main()
{                              //UDP专用
    int sockfd = socket(AF_INET,SOCK_DGRAM,0);
    assert(sockfd != -1);

    struct sockaddr_in saddr,caddr;
    memset(&saddr,0,sizeof(saddr));
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(6000);
    saddr.sin_addr.s_addr = inet_addr("127.0.0.1");

    int res = bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
    assert(res != -1);

    while(1)
    {
        int len = sizeof(caddr);
        char buff[128] = {0};     //对方端口的地址
        recvfrom(sockfd,buff,127,0,(struct sockaddr*)&caddr,&len);
        printf("ip=%s,port=%d,buff=%s\n",inet_ntoa(caddr.sin_addr),ntohs(caddr.sin_port),buff);        
        sendto(sockfd,"ok",2,0,(struct sockaddr*)&caddr,sizeof(caddr));
    }    
}
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<assert.h>
#include<sys/socket.h>
#include<arpa/inet.h>

int main()
{
    int sockfd = socket(AF_INET,SOCK_DGRAM,0);
    assert(sockfd != -1);

    struct sockaddr_in saddr,caddr;
    memset(&saddr,0,sizeof(saddr));
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(6000);
    saddr.sin_addr.s_addr = inet_addr("127.0.0.1");


    while(1)
    {
        char buff[128] = {0};
        printf("input:\n");
        fgets(buff,128,stdin);
        if(strncmp(buff,"end",3) == 0)
        {
            break;
        }
        //给服务器端发送数据
        sendto(sockfd,buff,strlen(buff),0,(struct sockaddr*)&saddr,sizeof(saddr));
        memset(buff,0,128);
        int len = sizeof(caddr);
        //服务器给给客户端回复确认信息
        recvfrom(sockfd,buff,127,0,(struct sockaddr*)&saddr,&len);        
        printf("buff = %s\n",buff);
    }
    close(sockfd);
}

上文提到在UDP套接口编程的时候是不用建立连接的,那么我们就来看一下这个不用连接是怎么来体现的呢

1.我们先打开服务器端和两个客户端,然后进行通信

2.关闭服务器端和其中的一个客户端

3.利用刚刚打开的那个客户端继续给服务器发送数据

4.重启服务器端,再利用客户端给服务器端发送数据

下图就是对上述四点描述的结果展示

                       

在此我们还可以用strace -p + pid 命令可以跟踪系统调用,可以查看程序是阻塞在了哪一步

  我们知道TCP和UDP传输数据的时候是有区别的,TCP采用流式服务(这里不再细说,在TCP协议特点里有讲)而UDP采用的是数据报服务,那么我们来看看UDP在传输数据的时候是怎么体现数据报服务的。

                                  

  根据代码实现的结果截图我们可以看出,(此时服务器端一次只接收一个数据)客户端发送一连串数据给服务器端,第一次服务器端只接收了h,第二次只接收7,那么就有人问了那第一次发送的h以后的数据呢,还有第二次发送的7以后的数据呢?其实这就是UDP传输数据的特点所在,刚说了UDP是数据报服务,那么当规定了它只能读取一定数目的数据之后,无论缓冲区中还有多少数据他都是不能再读的,直接就被丢弃了,下面我们来看看UDP发送和接收缓冲区图

                                                       

从上面这张图我们可以看出,在UDP发送数据的时候是一次性发送一个数据报,接收的时候也是,并不像TCP那样一下可以把好几个合在一起发送,接收的时候都放在缓冲区里一下可以全部接收,所以在UDP传输数据的过程中,发送次数和接收次数是一对一的。

数据报套接字的优点:服务器的崩溃不会给客户造成不便,也不会要求客户重启,因为基于数据报的服务器通常不保留连接信息,所以它们可以在不打扰其客户的前提下通知并重启。

UDP小结:

(1)UDP以恒定得速度发送数据,如果网不好数据丢了就丢了,比如我们在和亲戚朋友视频聊天得时间就用得是UDP,如果某一时刻得网不好那你俩就都卡住了,或者是一方卡住,但是当网络好了得时候你会发现你俩在实时交流,并没有产生延迟,如果是TCP得话,他的数据如果没有传过去就会一直从传,知道收到确认后才会继续发送后面得数据,这样得话你就会发现即使网好了,你俩也不能在同一时刻交流,他之前说的话可能现在才传过来,他此刻说的你却还不能接收到。

(2)如果非要用UDP实现一个TCP得可靠传输得功能那么我们就参考TCP在应用层实现一个可靠得机制

猜你喜欢

转载自blog.csdn.net/YL970302/article/details/83062361