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协议数据截断:如果接收到的数据报大于缓冲区,则报文被截断,后面的部分会丢失。

时间: 2024-10-10 13:10:41

Socket编程实践(14) --UDP编程基础(2)的相关文章

Socket编程实践(12) --UDP编程基础

UDP特点 无连接,面向数据报(基于消息,不会粘包)的数据传输服务; 不可靠(可能会丢包, 乱序, 重复), 但因此一般情况下UDP更加高效; UDP客户/服务器模型 UDP-API使用 #include <sys/types.h> #include <sys/socket.h> ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *a

Socket编程实践(13) --UDP编程基础(1)

UDP特点 无连接,面向数据报(基于消息,不会粘包)的数据传输服务; 不可靠(可能会丢包),但一般情况下UDP更加高效;     UDP客户/服务基本模型 UDP基础API 1.Recvfrom SYNOPSIS #include <sys/types.h> #include <sys/socket.h> ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr,

Socket编程实践(14) --UNIX域协议

UNIX域协议 UNIX域套接字与TCP相比, 在同一台主机上, UNIX域套接字更有效率, 几乎是TCP的两倍(由于UNIX域套接字不需要经过网络协议栈,不需要打包/拆包,计算校验和,维护序号和应答等,只是将应用层数据从一个进程拷贝到另一个进程, 而且UNIX域协议机制本质上就是可靠的通讯, 而网络协议是为不可靠的通讯设计的). UNIX域套接字可以在同一台主机上各进程之间传递文件描述符; UNIX域套接字与传统套接字的区别是用路径名来表示协议族的描述; UNIX域套接字也提供面向流和面向数据

基于socket的TCP和UDP编程

一.概述 TCP(传输控制协议)和UDP(用户数据报协议是网络体系结构TCP/IP模型中传输层一层中的两个不同的通信协议. TCP:传输控制协议,一种面向连接的协议,给用户进程提供可靠的全双工的字节流,TCP套接口是字节流套接口(STream socket)的一种. UDP:用户数据报协议.UDP是一种无连接协议.UDP套接口是数据报套接口(datagram Socket)的一种. 二.TCP和UDP介绍 1)基本TCP客户—服务器服务器 服务器是指在网络环境下运行相应的应用软件,为网上用户提供

Linux下的socket编程实践(十) 基本UDP编程细节

在我的这两篇博客中,简单介绍并实现了基于UDP(TCP)的windows(UNIX下流程基本一致)下的服务端和客户端的程序,本文继续探讨关于UDP编程的一些细节. http://blog.csdn.net/nk_test/article/details/47733307 http://blog.csdn.net/nk_test/article/details/47756381 下图是一个简单的UDP客户/服务器模型: 我在这里也实现了一个简单的UDP回射服务器/客户端: /**实践: 实现一个基

linux系统socket通信编程实践

简单介绍并实现了基于UDP(TCP)的windows(UNIX下流程基本一致)下的服务端和客户端的程序,本文继续探讨关于UDP编程的一些细节. 下图是一个简单的UDP客户/服务器模型: .imageplus-append-lu-img-txt { overflow: hidden; margin: 10px 0 } .imageplus-append-nova-txt { border: 1px solid #f2f2f2; font-family: Microsoft YaHei; line-

Socket编程实践(6) --TCP粘包原因与解决

流协议与粘包 粘包的表现 Host A 发送数据给 Host B; 而Host B 接收数据的方式不确定 粘包产生的原因 说明 TCP 字节流,无边界 对等方,一次读操作,不能保证完全把消息读完 UDP 数据报,有边界 对方接受数据包的个数是不确定的 产生粘包问题的原因分析 1.SQ_SNDBUF 套接字本身有缓冲区 (发送缓冲区.接受缓冲区) 2.tcp传送的端 mss大小限制 3.链路层也有MTU大小限制,如果数据包大于>MTU要在IP层进行分片,导致消息分割. 4.tcp的流量控制和拥塞控

Java笔记二十三.网络编程基础与UDP编程

网络编程基础与UDP编程 转载请表明出处:http://blog.csdn.net/u012637501(嵌入式_小J的天空) 一.网络编程基础 1.TCP/IP协议:TCP/IP协议是一个非常实用的网络应用程序通信协议,包括TCP(传输控制协议)和IP地址(计算机唯一标识号). 2.IP地址:IP在互联网中能唯一标识一台计算机,是每一台计算机的唯一标识(身份证),通过这个标识号来指定接收数据的计算机和识别发送数据的计算机,该标识号即为IP地址. (1)Ipv4:指在计算机中IP地址用4个字节(

第14章 UDP编程(1)_UDP客户端服务器模型

1. UDP编程模型 (1)UDP客户端服务器模型 ①客户端可以不调用bind()而直接与服务器通讯. ②UDP是无连接的,因此服务端不需要调用accept和listen,客户端也无需调用connect函数. (2)数据传输 ①发送数据 头文件 #include <sys/socket.h> 函数 ssize_t send(int sockfd, const void* buf, size_t nbytes, int flag); ssize_t send(int sockfd, const