recvfrom函数
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了recvfrom函数相关的知识,希望对你有一定的参考价值。
int ret;
srtuct sockaddr_in from;
ret=revcfrom(sock,recvbuf,BUFSIZE,0,
(struct sockaddr*)&from,&len))==SOCKET_ERROR
from事先不要赋值吗?
通过sock,revcfrom 会把它赋值吗??
书上没给它赋值,有
printf("RECV:'%s FROM <%s>\n'",recvbuf,inet_ntoa(from.sin_addr));
本函数用于从(已连接)套接口上接收数据,并捕获数据发送源的地址。
对于SOCK_STREAM类型的套接口,最多可接收缓冲区大小个数据。如果套接口被设置为线内接收带外数据(选项为SO_OOBINLINE),且有带外数据未读入,则返回带外数据。应用程序可通过调用ioctlsocket()的SOCATMARK命令来确定是否有带外数据待读入。对于SOCK_STREAM类型套接口,忽略from和fromlen参数。
对于数据报类套接口,队列中第一个数据报中的数据被解包,但最多不超过缓冲区的大小。如果数据报大于缓冲区,那么缓冲区中只有数据报的前面部分,其他的数据都丢失了,并且recvfrom()函数返回WSAEMSGSIZE错误。
若from非零,且套接口为SOCK_DGRAM类型,则发送数据源的地址被复制到相应的sockaddr结构中。fromlen所指向的值初始化时为这个结构的大小,当调用返回时按实际地址所占的空间进行修改。
如果没有数据待读,那么除非是非阻塞模式,不然的话套接口将一直等待数据的到来,此时将返回SOCKET_ERROR错误,错误代码是WSAEWOULDBLOCK。用select()或WSAAsynSelect()可以获知何时数据到达。
如果套接口为SOCK_STREAM类型,并且远端“优雅”地中止了连接,那么recvfrom()一个数据也不读取,立即返回。如果立即被强制中止,那么recv()将以WSAECONNRESET错误失败返回。 参考技术A 不需要赋值...recvfrom()应该是UDP通讯的接收函数,一般是socket绑定一个端口,然后就会接收该端口的数据,recvfrom()函数执行的时候,那个from参数输出的是对方的地址。是一个输出参数,不是输入的。本回答被提问者采纳
linux socket-recvfrom系统调用
文章目录
recvfrom库函数
应用程序调用recv或recvfrom库函数接收数据。recv和recvfrom主要区别是是否设置socket地址。
__socketcall ssize_t recvfrom(int, void*, size_t, int, const struct sockaddr*, socklen_t*);
ssize_t recv(int, void*, size_t, int);
ssize_t recv(int socket, void *buf, size_t len, int flags)
return recvfrom(socket, buf, len, flags, NULL, 0); //对于recv,指定后两个参数为空
armv8系统调用:
ENTRY(recvfrom)
mov x8, __NR_recvfrom
svc #0
cmn x0, #(MAX_ERRNO + 1)
cneg x0, x0, hi
b.hi __set_errno_internal
ret
END(recvfrom)
recvfrom系统调用
先看数据结构
struct msghdr
struct msghdr为socket层和协议层进行通信的消息形式。
struct iovec存储用户空间传递的buf地址和长度
struct iovec
void __user *iov_base; /* BSD uses caddr_t (1003.1g requires void *) */
__kernel_size_t iov_len; /* Must be size_t (1003.1g) */
;
/*
* As we do 4.4BSD message passing we use a 4.4BSD message passing
* system, not 4.3. Thus msg_accrights(len) are now missing. They
* belong in an obscure libc emulation or the bin.
*/
struct msghdr
void * msg_name; /* Socket name */ //包括socket地址等
int msg_namelen; /* Length of name */
struct iovec * msg_iov; /* Data blocks */ //用户空间buf block,可以是数组
__kernel_size_t msg_iovlen; /* Number of blocks */ //用户空间buf block个数,数组长度
void * msg_control; /* Per protocol magic (eg BSD file descriptor passing) */ //控制消息
__kernel_size_t msg_controllen; /* Length of cmsg list */
unsigned int msg_flags;
;
struct kiocb
每个IO请求都对应一个kiocb结构体,ki_ctx指示是否为同步IO还是异步IO,默认NULL为同步IO
ki_users指示引用数量。tsk存储当前进程。
struct kiocb
atomic_t ki_users;
struct file *ki_filp;
struct kioctx *ki_ctx; /* NULL for sync ops */
kiocb_cancel_fn *ki_cancel;
void (*ki_dtor)(struct kiocb *);
union
void __user *user;
struct task_struct *tsk;
ki_obj;
__u64 ki_user_data; /* user's data for completion */
loff_t ki_pos;
void *private;
/* State that we remember to be able to restart/retry */
unsigned short ki_opcode;
size_t ki_nbytes; /* copy of iocb->aio_nbytes */
char __user *ki_buf; /* remaining iocb->aio_buf */
size_t ki_left; /* remaining bytes */
struct iovec ki_inline_vec; /* inline vector */
struct iovec *ki_iovec;
unsigned long ki_nr_segs;
unsigned long ki_cur_seg;
struct list_head ki_list; /* the aio core uses this
* for cancellation */
/*
* If the aio_resfd field of the userspace iocb is not zero,
* this is the underlying eventfd context to deliver events to.
*/
struct eventfd_ctx *ki_eventfd;
;
struct sock_iocb
struct sock_iocb为sock 类型io 请求控制块。struct kiocb 中private指向sock_iocb类型。
/* sock_iocb: used to kick off async processing of socket ios */
struct sock_iocb
struct list_head list;
int flags;
int size;
struct socket *sock; //指向socket
struct sock *sk;//指向socket中sock
struct scm_cookie *scm;
struct msghdr *msg, async_msg; //指向msghdr消息
struct kiocb *kiocb;
;
recvfrom
/*
* Receive a frame from the socket and optionally record the address of the
* sender. We verify the buffers are writable and if needed move the
* sender address from kernel to user space.
*/
SYSCALL_DEFINE6(recvfrom, int, fd, void __user *, ubuf, size_t, size,
unsigned int, flags, struct sockaddr __user *, addr,
int __user *, addr_len)
struct socket *sock;
struct iovec iov;
struct msghdr msg;
struct sockaddr_storage address;
int err, err2;
int fput_needed;
//根据文件描述符fd找到对应的socekt结构指针
sock = sockfd_lookup_light(fd, &err, &fput_needed);
//构造struct msghdr,用于socket和协议栈层交互
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_iovlen = 1;
msg.msg_iov = &iov; //指向struct iovec,存储了用户空间buf
iov.iov_len = size;
iov.iov_base = ubuf;//存储了用户空间buf
/* Save some cycles and don't copy the address if not needed */
msg.msg_name = addr ? (struct sockaddr *)&address : NULL;//用于存储发送端的地址
/* We assume all kernel code knows the size of sockaddr_storage */
msg.msg_namelen = 0;
if (sock->file->f_flags & O_NONBLOCK)
flags |= MSG_DONTWAIT; //标志是否为阻塞io
//调用sock接口
err = sock_recvmsg(sock, &msg, size, flags);
if (err >= 0 && addr != NULL) //保存发送端地址到用户空间 地址buf
err2 = move_addr_to_user(&address,
msg.msg_namelen, addr, addr_len);
if (err2 < 0)
err = err2;
fput_light(sock->file, fput_needed);
out:
return err;
int sock_recvmsg(struct socket *sock, struct msghdr *msg,
size_t size, int flags)
struct kiocb iocb;
struct sock_iocb siocb;
int ret;
//构造一个kiocb ,并且iocb.private存储siocb;用于io请求
init_sync_kiocb(&iocb, NULL);
iocb.private = &siocb;
ret = __sock_recvmsg(&iocb, sock, msg, size, flags);
if (-EIOCBQUEUED == ret)
ret = wait_on_sync_kiocb(&iocb);
return ret;
__sock_recvmsg---》__sock_recvmsg_nosec:
static inline int __sock_recvmsg_nosec(struct kiocb *iocb, struct socket *sock,
struct msghdr *msg, size_t size, int flags)
struct sock_iocb *si = kiocb_to_siocb(iocb);
//填充上面构造的sock_iocb
si->sock = sock;
si->scm = NULL;
si->msg = msg; //指向了msghdr
si->size = size;
si->flags = flags;
//调用具体socket type的socket接口,比如SOCK_STREAM,SOCK_DGRAM,或者SOCK_RAW等
//这里io请求控制块从kiocb 转换为了sock_iocb ,也就是从通用socekt层到具体socket type的socket 层面,使用具体的对应iocb
return sock->ops->recvmsg(iocb, sock, msg, size, flags);
实际看SOCK_STREAM,SOCK_DGRAM,或者SOCK_RAW 类型socket的socket管理面recvmsg 接口都为inet_recvmsg
int inet_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg,
size_t size, int flags)
struct sock *sk = sock->sk;
int addr_len = 0;
int err;
sock_rps_record_flow(sk);
//调用sock中 sk_prot-》recvmsg,也就是调用socekt类型下,某种具体协议的recvmsg函数,从socekt层面转到协议栈层面
//比如SOCK_STREAM类型socekt下的tcp,SOCK_DGRAM类型socekt下的udp
err = sk->sk_prot->recvmsg(iocb, sk, msg, size, flags & MSG_DONTWAIT,
flags & ~MSG_DONTWAIT, &addr_len);
if (err >= 0)
msg->msg_namelen = addr_len;
return err;
以上是关于recvfrom函数的主要内容,如果未能解决你的问题,请参考以下文章
C语言 recv()函数recvfrom()函数recvmsg()函数
C语言 recv()函数recvfrom()函数recvmsg()函数