导航:首页 > 编程系统 > recvfromlinux

recvfromlinux

发布时间:2023-04-02 06:02:34

❶ 使用recvfrom接收UDP包在Windows和linux平台的不同表现

操作系统的UDP接收流程如下:收到一个UDP包后,验证没有错误后,放入一个包队列中,队列中的每一个元素就是一个完整的UDP包。当应用程序通过recvfrom()读取时,OS把相应的一个完整UDP包取出,然后拷贝到用户提供的内存中,物理用户提供的内存大小是多少,OS都会完整取出一个UDP包。如果用户提供的内存小于这个UDP包的大小,那么在填充慢内存后,UDP包剩余的部分就会被丢弃,以后再也无法取回。

这与TCP接收完全不同,TCP没有完整包的概念,也没有边界,OS只会取出用户要求的大小,剩余的仍然保留在OS中,下次还可以继续取出。

socket编程虽然是事实上的标准,而且不同平台提供的接口函数也非常类似,但毕竟它不存在严格的标准。所以各个平台的实现也不完全兼容。下面就从recvfrom()这个函数看看Window平台和Linux平台的不同。

Windows平台的表现
int
WSAAPI
recvfrom(
_In_ SOCKET s,
_Out_writes_bytes_to_(len, return) __out_data_source(NETWORK) char FAR * buf,
_In_ int len,
_In_ int flags,
_Out_writes_bytes_to_opt_(*fromlen, *fromlen) struct sockaddr FAR * from,
_Inout_opt_ int FAR * fromlen
);

再看MSDN说明:
If the datagram or message is larger than the buffer specified, the buffer is filled with the first part of the datagram, and recvfrom generates the error WSAEMSGSIZE. For unreliable protocols (for example, UDP) the excess data is lost.
可以看出,buf大小小于UDP包大小的时候,recvfrom()会返回-1,并设置错误WSAEMSGSIZE。
实际编程测试验证确实是这样的表现。

Linux平台的表现
__extern_always_inline ssize_t
recvfrom (int __fd, void *__restrict __buf, size_t __n, int __flags,
__SOCKADDR_ARG __addr, socklen_t *__restrict __addr_len)

可以看出与Windows平台的函数原型相同。但是在其man手册里,没有看到UDP包大于接收缓冲区情况的特殊说明。
代码测试表明,buf小于UDP包大小的时候,recvfrom()仍然返回复制到缓冲区的字节数,调用者无法得知UDP包被截断的情况。

❷ linux Ubuntu 中,写了一个简单的UDP协议,只用了sendto 与recvfrom ,出错了

网络的编辑选项里面有可以写代码的,最起码代码格式整齐,容易阅读。版

客户端的权serv_addr.sin_addr.s_addr=INADDR_ANY这里使用服务器的固定地址试试。广播模式这个地址好像是用255.255.255.0的(局域网下)。

❸ linux手册翻译——recv(2)


recv, recvfrom, recvmsg - receive a message from a socket


recv()、recvfrom() 和 recvmsg() 调用用于从套接字接收消息。 它们可用于在UDP和TCP的套接字上接收数据。 本页首先介绍了所有三个系统调用的共同特点,然后介绍了调用之间的区别。

recv() 和 read(2) 之间的唯一区别是 flags 的存在。 使用腔弊枣零标志参数,recv() 通常等效于 read(2) (但请参阅 NOTES),且
recv(sockfd, buf, len, flags);
等价于
recvfrom(sockfd, buf, len, flags, NULL, NULL);

所有三个调用都在成功完成时返回消息的长度。 如果消息太长而无法放入提供的缓冲区,则 可能 会丢弃多余的字节,具体 取决于接收消息的套接字类型 ,显然TCP是不可能丢弃的。

如果套接字上卜滚没有可用消息,则接收调用将等待消息到达,除非套接字是非阻塞伍拆的(请参阅 fcntl(2)),在这种情况下,将返回值 -1 并将 errno 设置为 EAGAIN 或 EWOULDBLOCK。 recv_()调用通常会返回任何可用的数据,只要拿到数据就会立马返回,最多返回指定缓冲区大小的数据,但是并不会等待到让缓冲区满 ,除非设置了 MSG_WAITALL 标志,见下。

应用程序可以使用 select(2)、poll(2) 或 epoll(7) 来确定更多数据何时到达。

The flags argument is formed by ORing one or more of the following values:

ee_errno contains the errno number of the queued error. ee_origin is the origin code of where the error originated. The other fields are protocol-specific. The macro SOCK_EE_OFFENDER returns a pointer to the address of the network object where the error originated from given a pointer to the ancillary message. If this address is not known, the sa_family member of the sockaddr contains AF_UNSPEC and the other fields of the sockaddr are undefined. The payload of the packet that caused the error is passed as normal data.
For local errors, no address is passed (this can be checked with the cmsg_len member of the cmsghdr ). For error receives, the MSG_ERRQUEUE flag is set in the msghdr . After an error has been passed, the pending socket error is regenerated based on the next queued error and will be passed on the next socket operation.

recvfrom() 将接收到的消息放入缓冲区 buf 。 调用者必须在 len 中指定缓冲区的大小。

如果调用者希望拿到消息的原地址, 并且底层协议可以提供消息的源地址时,应将 src_addr 设置为指向用于接收消息原地址的缓冲区。 在这种情况下, addrlen 是一个 value-result 参数。 在调用之前,它应该被初始化为与 src_addr 关联的缓冲区的大小。 返回时,addrlen 被更新以包含源地址的实际大小。 如果提供的缓冲区太小,则截断返回的地址; 在这种情况下, addrlen 将返回一个大于提供给调用的值。

如果调用者对源地址不感兴趣,则应将 src_addr 和 addrlen 指定为 NULL。

ssize_t recv(int sockfd, void* buf, size_t len, int flags);
recv() 调用通常仅用于已连接的套接字(请参阅 connect(2))。 相当于调用:
recvfrom(fd, buf, len, flags, NULL, 0);

ssize_t recvmsg(int sockfd, struct msghdr* msg, int flags);
recvmsg() 调用使用 msghdr 结构来 最小化直接提供的参数数量 。 这个结构在 <sys/socket.h> 中定义如下:

msg_name 字段指向调用者分配的缓冲区,如果套接字未连接( 特指UDP的服务端 ),则该缓冲区用于返回源地址。 调用者应在此调用之前将 msg_namelen 设置为此缓冲区的大小; 从成功调用返回后,msg_namelen 将包含返回地址的长度。 如果应用程序不需要知道源地址,可以将 msg_name 指定为 NULL。

The fields msg_iov and msg_iovlen describe scatter-gather locations, as discussed in readv(2).
需要注意的是 msg_iov 和 msg_iovlen 描述了一个 struct iovec 类型的数组, msg_iovlen 表示数组的元素个数,而struct iovec则是描述了一个缓冲区

字段 msg_control 指向用于其他协议控制相关消息或杂项辅助数据的缓冲区。 当recvmsg()被调用时, msg_controllen 为 msg_contro l中可用缓冲区的长度; 从成功调用返回时,它将被设置为控制消息序列的长度。

控制消息的格式为:

只能通过 cmsg(3) 中定义的宏访问辅助数据。

例如,Linux 使用这种辅助数据机制通过 UNIX 域套接字传递扩展错误、IP 选项或文件描述符。 有关在各种套接字域中使用辅助数据的更多信息,请参阅 unix(7) 和 ip(7)。

msghdr 中的 msg_flags 字段在 recvmsg() 返回时设置 。 它可以包含几个标志:


这些调用返回接收到的字节数,如果发生错误,则返回 -1。 如果发生错误,则设置 errno 以指示错误。

当流套接字对等端执行有序关闭(orderly shutdown)时,返回值将为 0(传统的“文件结束”返回)。

各种域(例如 UNIX 和 Internet 域)中的数据报套接字允许零长度数据报。 当收到这样的数据报时,返回值为 0。

如果从流套接字接收的请求字节数为 0,则也可能返回值 0。


这些是套接字层生成的一些标准错误。 底层协议模块可能会产生和返回额外的错误; 查看他们的手册页。


POSIX.1-2001, POSIX.1-2008, 4.4BSD (these interfaces first appeared in 4.2BSD).

POSIX.1 describes only the MSG_OOB, MSG_PEEK, and MSG_WAITALL flags.


如果零长度数据报未决,则带有零标志参数的 read(2) 和 recv() 提供不同的行为。 在这种情况下, read(2) 不起作用(数据报保持挂起),而 recv() 消耗挂起的数据报。

socklen_t 类型是由 POSIX 发明的。 另见 accept(2) 。

根据 POSIX.1,msghdr 结构的 msg_controllen 字段类型为 socklen_t,而 msg_iovlen 字段类型为 int,但 glibc 目前将两者设置为 size_t。

有关可用于在单个调用中接收多个数据报的 Linux 特定系统调用的信息,请参阅 recvmmsg(2)。


getaddrinfo(3) 中显示了使用 recv() 的示例。

❹ linux下socket文件传输问题

如果你的客户端在发送文件时,每次都重新connect,再进行数据传输,则你的程序无回法解决数据的区分。答
如果客户端是一次connect循环发送,后台服务循环接收,则
(1)如果你的服务端只有一个进程(不支持并发),则A和B不会同时运行,只能按顺序接收完A再接收B
(2)如果,每一个新链接上来,你都建立一个新的进程去工作,则不会有问题。

❺ Linux系统I/O模型及select、poll、epoll原理和应用

理解Linux的IO模型之前,首先要了解一些基本概念,才能理解这些IO模型设计的依据

操作系统使用虚拟内存来映射物理内存,对于32位的操作系统来说,虚拟地址空间为4G(2^32)。操作系统的核心是内核,为了保护用户进程不能直接操作内核,保证内核安全,操作系统将虚拟地址空间划分为内核空间和用户空间。内核可以访问全部的地址空间,拥有访问底层硬件设备的权限,普通的应用程序需要访问硬件设备必须通过 系统调用 来实现。

对于Linux系统来说,将虚拟内存的最高1G字节的空间作为内核空间仅供内核使用,低3G字节的空间供用户进程使用,称为用户空间。

又被称为标准I/O,大多数文件系统的默认I/O都是缓存I/O。在Linux系统的缓存I/O机制中,操作系统会将I/O的数据缓存在页缓存(内存)中,也就是数据先被拷贝到内核的缓冲区(内核地址空间),然后才会从内核缓冲区拷贝到应用程序的缓冲区(用户地址空间)。

这种方式很明显的缺点就是数据传输过程中需要再应用程序地址空间和内核空间进行多次数据拷贝操作,这些操作带来的CPU以及内存的开销是非常大的。

由于Linux系统采用的缓存I/O模式,对于一次I/O访问,以读操作举例,数据先会被拷贝到内核缓冲区,然后才会从内核缓冲区拷贝到应用程序的缓存区,当一个read系统调用发生的时候,会经历两个阶段:

正是因为这两个状态,Linux系统才产生了多种不同的网络I/O模式的方案

Linux系统默认情况下所有socke都是blocking的,一个读操作流程如下:

以UDP socket为例,当用户进程调用了recvfrom系统调用,如果数据还没准备好,应用进程被阻塞,内核直到数据到来且将数据从内核缓冲区拷贝到了应用进程缓冲区,然后向用户进程返回结果,用户进程才解除block状态,重新运行起来。

阻塞模行下只是阻塞了当前的应用进程,其他进程还可以执行,不消耗CPU时间,CPU的利用率较高。

Linux可以设置socket为非阻塞的,非阻塞模式下执行一个读操作流程如下:

当用户进程发出recvfrom系统调用时,如果kernel中的数据还没准备好,recvfrom会立即返回一个error结果,不会阻塞用户进程,用户进程收到error时知道数据还没准备好,过一会再调用recvfrom,直到kernel中的数据准备好了,内核就立即将数据拷贝到用户内存然后返回ok,这个过程需要用户进程去轮询内核数据是否准备好。

非阻塞模型下由于要处理更多的系统调用,因此CPU利用率比较低。

应用进程使用sigaction系统调用,内核立即返回,等到kernel数据准备好时会给用户进程发送一个信号,告诉用户进程可以进行IO操作了,然后用户进程再调用IO系统调用如recvfrom,将数据从内核缓冲区拷贝到应用进程。流程如下:

相比于轮询的方式,不需要多次系统调用轮询,信号驱动IO的CPU利用率更高。

异步IO模型与其他模型最大的区别是,异步IO在系统调用返回的时候所有操作都已经完成,应用进程既不需要等待数据准备,也不需要在数据到来后等待数据从内核缓冲区拷贝到用户缓冲区,流程如下:

在数据拷贝完成后,kernel会给用户进程发送一个信号告诉其read操作完成了。

是用select、poll等待数据,可以等待多个socket中的任一个变为可读,这一过程会被阻塞,当某个套接字数据到来时返回,之后再用recvfrom系统调用把数据从内核缓存区复制到用户进程,流程如下:

流程类似阻塞IO,甚至比阻塞IO更差,多使用了一个系统调用,但是IO多路复用最大的特点是让单个进程能同时处理多个IO事件的能力,又被称为事件驱动IO,相比于多线程模型,IO复用模型不需要线程的创建、切换、销毁,系统开销更小,适合高并发的场景。

select是IO多路复用模型的一种实现,当select函数返回后可以通过轮询fdset来找到就绪的socket。

优点是几乎所有平台都支持,缺点在于能够监听的fd数量有限,Linux系统上一般为1024,是写死在宏定义中的,要修改需要重新编译内核。而且每次都要把所有的fd在用户空间和内核空间拷贝,这个操作是比较耗时的。

poll和select基本相同,不同的是poll没有最大fd数量限制(实际也会受到物理资源的限制,因为系统的fd数量是有限的),而且提供了更多的时间类型。

总结:select和poll都需要在返回后通过轮询的方式检查就绪的socket,事实上同时连的大量socket在一个时刻只有很少的处于就绪状态,因此随着监视的描述符数量的变多,其性能也会逐渐下降。

epoll是select和poll的改进版本,更加灵活,没有描述符限制。epoll使用一个文件描述符管理多个描述符,将用户关系的文件描述符的事件存放到内核的一个事件表中,这样在用户空间和内核空间的只需一次。

epoll_create()用来创建一个epoll句柄。
epoll_ctl() 用于向内核注册新的描述符或者是改变某个文件描述符的状态。已注册的描述符在内核中会被维护在一棵红黑树上,通过回调函数内核会将 I/O 准备好的描述符加入到一个就绪链表中管理。
epoll_wait() 可以从就绪链表中得到事件完成的描述符,因此进程不需要通过轮询来获得事件完成的描述符。

当epoll_wait检测到描述符IO事件发生并且通知给应用程序时,应用程序可以不立即处理该事件,下次调用epoll_wait还会再次通知该事件,支持block和nonblocking socket。

当epoll_wait检测到描述符IO事件发生并且通知给应用程序时,应用程序需要立即处理该事件,如果不立即处理,下次调用epoll_wait不会再次通知该事件。

ET模式在很大程度上减少了epoll事件被重复触发的次数,因此效率要比LT模式高。epoll工作在ET模式的时候,必须使用nonblocking socket,以避免由于一个文件句柄的阻塞读/阻塞写操作把处理多个文件描述符的任务饿死。

【segmentfault】 Linux IO模式及 select、poll、epoll详解
【GitHub】 CyC2018/CS-Notes

❻ Linux网络 - 数据包在内核中接收和发送的过程(转)

本文将介绍在Linux系统中, 数据包是如何一步一步从网卡传到进程手中的 以及 数据包是如何一步一步从应用程序到网卡并最终发送出去的

如果英文没有问题,强烈建议阅读后面参考里的文章,里面介绍的更详细。

本文只讨论以太网的物理网卡,不涉及虚拟设备,并且以一个UDP包的接收过程作为示例.

网卡需要有驱动才能工作,驱动是加载到内核中的模块,负责衔接网卡和内核的网络模块,驱动在加载的时候将自己注册进网络模块,当相应的网卡收到数据包时,网络模块会调用相应的驱动程序处理数据。

下图展示了数据包(packet)如何进入内存,并被内核的网络模块开始处理:

软中断会触发内核网络模块中的软中断处理函数,后续流程如下

由于是UDP包,所以第一步会进入IP层,然后一级一级的函数往下调:

应用层一般有两种方式接收数据,一种是recvfrom函数阻塞在那里等着数据来,这种情况下当socket收到通知后,recvfrom就会被唤醒,然后读取接收队列的数据;另一种是通过epoll或者select监听相应的socket,当收到通知后,再调用recvfrom函数去读取接收队列的数据。两种情况都能正常的接收到相应的数据包。

了解数据包的接收流程有助于帮助我们搞清楚我们可以在哪些地方监控和修改数据包,哪些情况下数据包可能被丢弃,为我们处理网络问题提供了一些参考,同时了解netfilter中相应钩子的位置,对于了解iptables的用法有一定的帮助,同时也会帮助我们后续更好的理解Linux下的网络虚拟设备。

ndo_start_xmit会绑定到具体网卡驱动的相应函数,到这步之后,就归网卡驱动管了,不同的网卡驱动有不同的处理方式,这里不做详细介绍,其大概流程如下:

在网卡驱动发送数据包过程中,会有一些地方需要和netdevice子系统打交道,比如网卡的队列满了,需要告诉上层不要再发了,等队列有空闲的时候,再通知上层接着发数据。

❼ linux下设置recvfrom为非阻塞

可以使用
1 select pselect
2 poll

3可以使用fcntl给文件描述符添加O—UNBLOCK

❽ Linux下的recvfrom为什么一直阻塞

UDP的client端是需要bind的,server端不需要bind,

关于阻塞的问题,应该在于你的socket的属性,设置成O_NONBLOCK就可以了。

❾ linux网络编程中 如何退出接受数据等待。。。recv recvfrom。。。

先用select或者poll或者epoll对你的socket进行监听...
上面几个API都可以设置超时时间,时间比较精确.
他们的作用都是监听文件描述符上的事件(可读,可写等等),你的sockfd也是一个文件描述符...

具体用法自己查吧...

❿ linux用udp协议时,为什么recefrom函数会卡住

数据包丢失。
如果数据包在传输中丢失,recvfrom函数将会一直阻塞,直到谨弯运祥梁超时或者重新发送数据包闹蚂。
UDP(UserDatagramProtocol)是一种无连接的、不可靠的传输层协议。UDP协议是因特网传输协议族中的一员,和TCP协议一样都处于传输层。

阅读全文

与recvfromlinux相关的资料

热点内容
maya粒子表达式教程 浏览:84
抖音小视频如何挂app 浏览:283
cad怎么设置替补文件 浏览:790
win10启动文件是空的 浏览:397
jk网站有哪些 浏览:134
学编程和3d哪个更好 浏览:932
win10移动硬盘文件无法打开 浏览:385
文件名是乱码还删不掉 浏览:643
苹果键盘怎么打开任务管理器 浏览:437
手机桌面文件名字大全 浏览:334
tplink默认无线密码是多少 浏览:33
ipaddgm文件 浏览:99
lua语言编程用哪个平台 浏览:272
政采云如何导出pdf投标文件 浏览:529
php获取postjson数据 浏览:551
javatimetask 浏览:16
编程的话要什么证件 浏览:94
钱脉通微信多开 浏览:878
中学生学编程哪个培训机构好 浏览:852
荣耀路由TV设置文件共享错误 浏览:525

友情链接