今天继续学习socket编程,从今天起开始学习UDP,具体内容如下:

①、无连接

UDP协议它内部并没有维护端到端的一些连接状态,这跟TCP是不同的,TCP是基于连接的,而在连接的时候是需要进行三次握手,而UDP是不需要的。

②、基于消息的数据传输服务

对于TCP而言,它是基于流的数据传输服务,而在编程时,会遇到一个粘包问题,是需要我们进行处理的,而对于UDP来说不存在粘包问题,因为它是基于消息的数据传输服务,我们可以认为,这些数据包之间是有边界的,而TCP数据包之间是无边界的。

③、不可靠

这里面的不可靠主要表现在数据报可能会丢失,还可能会重复,还可能会乱序,以及缺乏流量控制,

④、一般情况下UDP更加高效。

这里提到了“一般情况”~

首先先看一下它的流程示意图:

从图中可以看出,相对TCP而言,要简单不少~

下面就用编码的方式来认识一下UDP,在正式编码前,先看一下整个程序都需要用到哪些函数:

下面正式一步步来实现它:

服务端echosrv.c:

首先第一个步骤是创建套接字:

第二个步骤:初使化地址,并绑定套接口:

相比TCP,UDP当绑定之后,并不需要监听,而可以直接接收客户端发来的消息了,所以接下来这一步是回射服务器:

接下来,来利用recvfrom、sendto两个函数来实现回射服务器的内容,首先来看一下recvfrom的函数原形:

如果成功接收消息后,接着得将消息用sendto发回给客户端,来看下它的函数原型:

具体代码如下:

好了,接着编写客户端:echocli.c:

所以,接下来开始编写回射客户端的代码:

接下来接收从服务端回显过来的数据:

从以上代码的编写过程中,可以很直观的感受到UDP代码要比TCP代码简洁得多,下面编译运行一下:

可见一切运行正常,另外,请问下,客户端并没有与服务器端建立连接,也就是调用connect,那客户端是什么时候与服务端绑定的呢?

是在第一次sendto的时候就会绑定一个地址,也就是这句话:

对于sock而言,它有两个地址:

本地地址(也就是上图中说到的本地地址):可以通过getsockname来获取。

远程地址:可以通过getpeername来获取。

当第一次绑定成功之后, 之后就不会再次绑定了,关于UDP简单的实现就到这,贴上完整代码:

echosrv.c:

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h> #define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while() void echo_srv(int sock)
{
char recvbuf[] = {};
struct sockaddr_in peeraddr;
socklen_t peerlen;
int n;
while ()
{
peerlen = sizeof(peeraddr);
memset(recvbuf, , sizeof(recvbuf));
n = recvfrom(sock, recvbuf, sizeof(recvbuf), , (struct sockaddr*)&peeraddr, &peerlen);
if (n == -)
{
if (errno == EINTR)
continue; ERR_EXIT("recvfrom");
}
else if (n > )
{
fputs(recvbuf, stdout);
sendto(sock, recvbuf, n, , (struct sockaddr*)&peeraddr, peerlen);
}
} close(sock);
} int main(void)
{
int sock;
if ((sock = socket(PF_INET, SOCK_DGRAM, )) < )
ERR_EXIT("socket"); struct sockaddr_in servaddr;
memset(&servaddr, , sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons();
servaddr.sin_addr.s_addr = htonl(INADDR_ANY); if (bind(sock, (struct sockaddr*)&servaddr, sizeof(servaddr)) < )
ERR_EXIT("bind"); echo_srv(sock);//回射服务器 return ;
}

echocli.c:

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h> #define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while() void echo_cli(int sock)
{
struct sockaddr_in servaddr;
memset(&servaddr, , sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons();
servaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); int ret;
char sendbuf[] = {};
char recvbuf[] = {};
while (fgets(sendbuf, sizeof(sendbuf), stdin) != NULL)
{
sendto(sock, sendbuf, strlen(sendbuf), , (struct sockaddr*)&servaddr, sizeof(servaddr));
ret = recvfrom(sock, recvbuf, sizeof(recvbuf), , NULL, NULL);
if (ret == -)
{
if (errno == EINTR)
continue;
ERR_EXIT("recvfrom");
} fputs(recvbuf, stdout);
memset(sendbuf, , sizeof(sendbuf));
memset(recvbuf, , sizeof(recvbuf));
} close(sock);
} int main(void)
{
int sock;
if ((sock = socket(PF_INET, SOCK_DGRAM, )) < )
ERR_EXIT("socket"); echo_cli(sock); return ;
}

下面来看一下它的一些注意点。

①、UDP报文可能会丢失、重复

针对数据可能会丢失,发送端就要启动一个定时器,在超时时间到的时候要重传,要有一个超时的处理机制,对于接收方也是一样的,如果对等方发送的数据丢失了,接收方也会一直阻塞,也应该要有这种超时的机制;针对发送的数据可能会重复,所以应用层应该要维护数据报之间的序号,也就是第二条提到的。

②、UDP报文可能会乱序

需要维护数据报之间的序号。

③、UDP缺乏流量控制

UDP有对应自己的一个缓冲区,当缓冲区满的时候,如果再往里面发送数据,并不是将数据给丢失掉,而是将数据覆盖掉原来的缓冲区,并没有像TCP那样的滑动窗口协议,达到流量控制的目的,其实可以模拟TCP滑动窗口协议来实现流量控制的目的。

④、UDP协议数据报文截断

如果接收到数据报大于我们接收的缓冲区,那么数据报文就会被截断,那些数据就已经被丢弃了,这边可以用一个例子来演示下,为了简单起见,客户端与服务端写在同一个文件中:

编译运行:

由于UDP是基于报式套接口,而不是基于流的,也就是说UDP只会接收对应大小的数据,其余的数据会从缓冲区中清除,由此可见,它不会产生粘包问题。

⑤、recvfrom返回0,不代表连接关闭,因为udp是无连接的。

当我们发送数据时,不发送任何一个字节的数据,返回值就是0。

⑥、ICMP异步错误

下面用一个实验场景来说明下,就是我们不启动服务端,而只是启动客户端,然后发送数据,这时会有什么反应呢?

从结果来看,客户端阻塞了,并没有捕捉到对等方没有启动的信息,那这现象跟“ICMP异步错误”有什么关系呢?

结合代码来分析:

所以这时就称之为异步的ICMP错误,按正常的情况下是需要在recvfrom才会被通知到,而在服务端没有开启时,不应该sendto成功,但是由于sendto只是完成了一个数据的拷贝,所以错误延迟到recvfrom的时候才能够被通知,而这时recvfrom其实也不能够被通知的,因为TCP规定这种ICMP错误是不能够返回给未连接的套接字的,所以说也得不到通知,recvfrom会一直阻塞,如果说能够收到错误通知,那肯定会退出了,因为代码已经做了错误判断,如下:

那如何解决此问题呢?采用下面这个方法:UDP connect。

⑦、UDP connect

其实UDP也是能调用connect的,在客户端加入如下代码,看是否解决了上面的问题,能够收到对等方未启动的错误呢?

下面来看下结果:

可见,在服务端没有开启的情况下,客户端这次收到了ICMP异步错误通知,通知是在recvfrom中返回的,因为此时的sock是已连接的套接字了,这就是UDP connect的一个作用,那UDP connect是否真的意味着建立了跟TCP一样的连接呢?肯定不是这样的,UDP在调connect的时候,并不会调TCP的三次握手操作,并没有跟对方传递数据,它仅仅只是维护了一个信息,这个sock跟对方维护了一种状态,通过这个套接字能够发送数据给对等方,而且只能够发送给对等方,实际上也就是该sock中的远程地址得到了绑定,那么这种sock就不能够发送给其它地址了,另外一点,一旦连接成功之后,客户端的sendto代码可以进行下面的改装:

编译运行:

可见效果一样,正常收发,另外,当sock是已连接套接口时,sendto也可以改用send函数来进行发送,改装如下:

这时就不演示了,效果是一样的,同样可以正常收发,可以UDP的connect的TCP的connect意义是不一样的。

⑧、UDP外出接口的确定

好了,关于UDP的初入认识就先学到这,之后会再次慢慢研究,愉快的周末又要结束了,好好睡一觉迎接全新的一周,goodbye~~~

linux网络编程之socket编程(十三)的更多相关文章

  1. linux网络编程之socket编程(四)

    经过两周的等待,终于可以回归我正常的学习之旅了,表哥来北京了在我这暂住,晚上回家了基本在和他聊天,周末带他在北京城到处乱转,几乎剥夺了我自由学习的时间了,不过,亲人之情还是很难得的,工作学习并不是生活 ...

  2. linux网络编程之socket编程(一)

    今天开始,继续来学习linux编程,这次主要是研究下linux下的网络编程,而网络编程中最基本的需从socket编程开始,下面正式开始学习: 什么是socket: 在学习套接口之前,先要回顾一下Tcp ...

  3. linux网络编程之socket编程(六)

    经过一个国庆长假,又有一段时间没有写博文了,今天继续对linux网络编程进行学习,如今的北京又全面进入雾霾天气了,让我突然想到了一句名句:“真爱生活,珍惜生命”,好了,言归正传. 回顾一下我们之间实现 ...

  4. linux网络编程之socket编程(八)

    学习socket编程继续,今天要学习的内容如下: 先来简单介绍一下这五种模型分别是哪些,偏理论,有个大致的印象就成,做个对比,因为最终只会研究一个I/O模型,也是经常会用到的, 阻塞I/O: 先用一个 ...

  5. linux网络编程之socket编程(十六)

    继续学习socket编程,今天的内容会有些难以理解,一步步来分解,也就不难了,正入正题: 实际上sockpair有点像之前linux系统编程中学习的pipe匿名管道,匿名管道它是半双工的,只能用于亲缘 ...

  6. linux网络编程之socket编程(十五)

    今天继续学习socket编程,这次主要是学习UNIX域协议相关的知识,下面开始: [有个大概的认识,它是来干嘛的] ①.UNIX域套接字与TCP套接字相比较,在同一台主机的传输速度前者是后者的两倍. ...

  7. linux网络编程之socket编程(三)

    今天继续对socket编程进行学习,在学习之前,需要回顾一下上一篇中编写的回射客户/服务器程序(http://www.cnblogs.com/webor2006/p/3923254.html),因为今 ...

  8. linux网络编程之socket编程(二)

    今天继续对socket编程进行研究,这里会真正开如用socket写一个小例子,进入正题: TCP客户/服务器模型:   关于这个模型的流程这里就不多说了,比较容易理解,下面则利用这种模型来编写一个实际 ...

  9. linux网络编程之socket编程(十二)

    今天继续学习socket编程,期待的APEC会议终于在京召开了,听说昨晚鸟巢那灯火通明,遍地礼花,有点08年奥运会的架势,有种冲动想去瞅见一下习大大的真容,"伟大的祖国,我爱你~~~&quo ...

随机推荐

  1. 【计算机视觉】基于OpenCV的人脸识别

    一点背景知识 OpenCV 是一个开源的计算机视觉和机器学习库.它包含成千上万优化过的算法,为各种计算机视觉应用提供了一个通用工具包.根据这个项目的关于页面,OpenCV 已被广泛运用在各种项目上,从 ...

  2. PHP中NULL和‘'的区别

    PHP中NULL和‘'区别 null的类型是null,""的类型是string 所以是不同东西 <pre>$a=22;unset($a);var_dump($a);&l ...

  3. C++ 特性之 lambda

    "我扑到书籍上,就像饥饿的人扑在面包上"-- 高尔基 简而言之,Lambda 表达式就是用于创建匿名函数的. 或许,Lambda 表达式算得上是 C++ 11 新增特性中最激动人心 ...

  4. dotnet core JWT Demo

    JWT介绍 JSON Web Token(JWT)是目前最流行的跨域身份验证解决方案.JWT的官网地址:https://jwt.io/. 通俗地来讲,JWT是能代表用户身份的令牌,可以使用JWT令牌在 ...

  5. Flink状态管理和容错机制介绍

    本文主要内容如下: 有状态的流数据处理: Flink中的状态接口: 状态管理和容错机制实现: 阿里相关工作介绍: 一.有状态的流数据处理# 1.1.什么是有状态的计算# 计算任务的结果不仅仅依赖于输入 ...

  6. 02 HTML

    1. HTML概念: HTML是最基础的网页开发语言 * Hyper Text Markup Language 超文本标记语言 * 超文本: * 超文本是用超链接的方法,将各种不同空间的文字信息组织在 ...

  7. [转帖]TPC-C解析系列04_TPC-C基准测试之数据库事务引擎的挑战

    TPC-C解析系列04_TPC-C基准测试之数据库事务引擎的挑战   http://www.itpub.net/2019/10/08/3331/ OceanBase这次TPC-C测试与榜单上Oracl ...

  8. phpstrom xdebug phpstudy调试,跳不到设置断点的原因,以及配置方法

    设置的是127 的地址,而用localhost 方式请求,所以无法跳到断点 环境 phpstudy 2018 php 7.2.10 xdebug版本 (因为要与php版本要对应,注意别选错了,我的用的 ...

  9. C++ 中三种继承方式的理解

    一.公有继承(public inheritance) 1.概念:当类的继承方式为公有继承时,基类的公有成员和保护成员的访问属性在派生类中不变,而基类的私有成员不可以直接访问. 实验一下:   我们用代 ...

  10. vue的安装配置与项目创建

    1,安装vue必须先安装node.js.  --------去官网安装node.js 因为npm依赖node.js环境,使用npm的时候需要安装node.js.安装node.js的时候npm会默认安装 ...