美文网首页Linux学习之路Linux
APUE读书笔记-16网络通信(6)

APUE读书笔记-16网络通信(6)

作者: QuietHeart | 来源:发表于2020-07-31 19:36 被阅读0次

5、数据传输

由于socket末端使用文件描述符号来替代,当被连接起来的时候,我们就可以使用read和write来和socket进行通信了。需要记住的是如果我们使用connect函数的时候设置了默认的通信对等端,那么数据报的socket可以被认为是连接好了的。使用read和write与套接字描述符号进行通信是非常有意义的,因为它以为着我们可以把套接字描述符号传递给本来是用来操作本地文件的函数。我们也可以把套接字描述符号传递给子进程,子进程执行exec另外一个程序而那个程序却不用知道关于套接字的信息。

尽管我们能够使用read和write交换数据,但是这也只是我们使用这两个函数所能做的所有的事情了。如果我们想要指定选项,从多个客户端接受包,或者发送带外的数据,我们需要使用六个特定的socket函数来进行数据传输。

数据传输函数

有三个函数用来发送数据,同时也有三个函数用来接收数据。

发送数据

首先我们来看看发送数据的函数。

send

最简单的就是send,它和write的功能类似,但是允许我们指定标记来改变我们看待被传输的数据的方式。

#include <sys/socket.h>
ssize_t send(int sockfd, const void *buf, size_t nbytes, int flags);

返回:如果成功那么返回发送的字节数目,如果错误则返回1。

和write类似,套接字使用send的时候需要被连接。buf和nbytes参数和write中相应的参数的含义一样。

和write不同的是,send支持第四个参数flags。Single UNIX Specification定义了两个参数,但是一般实现都会定义一些额外的参数。大致如下面列出的,具体哪个系统支持,请参见参考资料,详细这里就不一一列举了。

  • MSG_DONTROUTE 不在本地网络之外对包进行路由。
  • MSG_DONTWAIT 打开非阻塞操作(与使用O_NONBLOCK等价)。
  • MSG_EOR 如果协议支持,那么这个表示记录的结尾。
  • MSG_OOB 如果协议支持,那么发送带外数据。

如果send返回成功,并不意味这另一端的进程接收到了数据。所有我们能够保证的只是:当send成功的时候,数据被发送到网络中了,并且没有错误。

对于一个支持消息边界的协议,如果我们尝试发送一个单个的消息,这个消息的大小比协议支持的最大大小要大,那么send将会返回失败并且设置errno为EMSGSIZE。对于一个字节流协议,send将会阻塞,直到整个数据都被传输完毕。

sendto

sendto函数和send函数类似。不同的是,sendto允许我们指定一个无连接套接字使用的目标地址。

#include <sys/socket.h>
ssize_t sendto(int sockfd, const void *buf, size_t nbytes, int flags, const struct sockaddr *destaddr,
               socklen_t destlen);

返回:如果成功返回字节数目,如果错误返回1。

使用面向连接的套接字,目标地址被忽略,因为目标已经隐含在了连接中。 使用无连接的套接字,如果我们不首先通过connect函数设置目标地址,那么我们就无法使用send发送 ,所以sendto函数给了我们一个可以选择的方法来发送消息。

sendmsg

我们有不只一种方法通过套接字传输数据。我们可以调用sendmsg,通过msghdr结构指定用来传输数据的多个缓存,这个和使用writev函数有点类似。

#include <sys/socket.h>
ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);

返回:如果成功,返回发送的字节数目,如果错误返回1。

POSIX.1定义msghdr结构至少包含如下成员:

struct msghdr {
        void          *msg_name;         /* optional address */
        socklen_t      msg_namelen;      /* address size in bytes */
        struct iovec  *msg_iov;          /* array of I/O buffers */
        int            msg_iovlen;       /* number of elements in array */
        void          *msg_control;      /* ancillary data */
        socklen_t      msg_controllen;   /* number of ancillary bytes */
        int            msg_flags;        /* flags for received message */
        .
        .
        .
};

对于msg_iov成员,我们前面看见过iovec结构(14章讲解readv和writev的时候),我们将在后面看到辅助数据的使用。

接收数据

接下来我们看看接收数据的函数。

recv

函数recv和read函数类似,但是允许我们指定一些如何接收数据的控制选项。

#include <sys/socket.h>
ssize_t recv(int sockfd, void *buf, size_t nbytes, int flags);

返回:返回消息的字节长度,如果没有消息返回0并且对等端做顺序的shutdown(???),或者如果错误返回1。

传递给recv的flags参数通过一个表格的形式进行列出,其中只有三个是Single UNIX Specification定义的。这里只给出一个大致的列举,具体那个实现支持它们,请参见参考资料。

                                                recv套接字调用使用的标记
+-------------------------------------------------------------------------------------------------+
|    Flag     |                                    Description                                    |
|-------------+-----------------------------------------------------------------------------------|
| MSG_OOB     | 如果协议支持,那么获取带外数据。                                                  |
|-------------+-----------------------------------------------------------------------------------|
| MSG_PEEK    | 返回包内容但是不会消耗包。                                                        |
|-------------+-----------------------------------------------------------------------------------|
| MSG_TRUNC   | 返回一个包的实际长度,即使它被截断了。                                            |
|-------------+-----------------------------------------------------------------------------------|
| MSG_WAITALL | 等待,直到所有数据可用(只对SOCK_STREAM)                                           |
+-------------------------------------------------------------------------------------------------+

当我们指定MSG_PEEK标记的时候,我们可以查看下一次将要读取的数据而不用消耗实际的数据(也就是说数据不会在被看到之后被清除掉)。下次调用read或者recv相关的函数的时候,将会返回我们这里查看到的数据。

通过使用SOCK_STREAM套接字,我们可以接收比我们请求少的数据。MSG_WAITALL标记会阻止这个现象发生,不让recv返回直至所有请求的数据被接收到。对于SOCK_DGRAM和SOCK_SEQPACKET套接字,MSG_WAITALL标记对它们的行为没有区别,因为这些基于消息的套接字类型在单个读取的时候已经返回了整个的消息。

如果发送者调用了shutdown函数来结束数据的传输,或者如果网络协议默认支持依次shutdown并且发送者关闭了套接字,那么recv将会在我们已经接收到所有的数据的时候返回0。

recvfrom

如果我们对发送者是谁很感兴趣,那么我们可以使用recvfrom来获得数据得以发送的源地址。

#include <sys/socket.h>
ssize_t recvfrom(int sockfd, void *restrict buf, size_t len, int flags, struct sockaddr *restrict addr,
                 socklen_t *restrict addrlen);

返回:会返回消息的字节长度,如果没有消息返回0并且对等端会顺序调用shutdown,或者如果错误返回1。

如果addr参数非空,它将会包含发送数据所来自的套接字末端地址。当调用recvfrom的时候,我们需要设置addrlen参数指向一个整数,这个整数用来包含addr参数所指向的套接字缓存的字节大小。当返回的时候,这个整数会被设置成实际字节的大小。

因为通过这个函数我们可以获取发送者的地址,recvfrom实际和无连接的套接字一块使用。否则,recvfrom表现的行为和recv一样。

recvmsg

和readv类似地如果想要接收数据到多个缓存,或者我们想要接收辅助的数据,我们可以使用recvmsg。

#include <sys/socket.h>
ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);

返回:消息的字节长度,如果没有消息返回0并且对等端允许调用shutdown,或者如果错误返回1。

recvmsg函数使用msghdr结构(我们看到sendmsg中使用了)指定接收数据的输入缓存。我们可以设置flags参数来更改默认的recvmsg行为。返回的时候,msghdr结构的msg_flags成员会被设置,以表示接收数据的各种特性(msg_flags成员在进入recvmsg的时候被忽略)。对于recvmsg返回的可能值,在下面的表格中列出来了。后面会对recvmsg函数的使用举一个例子。Specification定义的。这里只给出一个大致的列举,具体那个实现支持它们,请参见参考资料。

                        recvmsg返回的msg_flags中的标记
+---------------------------------------------------------+
|     Flag     |               Description                |
|--------------+------------------------------------------|
| MSG_CTRUNC   | 控制数据被截断。                         |
|--------------+------------------------------------------|
| MSG_DONTWAIT | recvmsg 以一种非阻塞的模式被调用。       |
|--------------+------------------------------------------|
| MSG_EOR      | 返回接收的记录结尾。                     |
|--------------+------------------------------------------|
| MSG_OOB      | 接收带外数据。                           |
|--------------+------------------------------------------|
| MSG_TRUNC    | 截断正常数据。                           |
+---------------------------------------------------------+

相关文章

  • APUE读书笔记-16网络通信(6)

    5、数据传输 由于socket末端使用文件描述符号来替代,当被连接起来的时候,我们就可以使用read和write来...

  • 信号函数编写研究

    APUE读书笔记 ToDoList [ ] sleep初级实现 2017年12月6日 10:41:30 [ ] ...

  • APUE读书笔记-16网络通信(2)

    3、寻址 在前面的小节里面,我们学到了如何创建和销毁一个套接字。在我们使用套接字之前,我们需要知道如何定位到我们交...

  • APUE读书笔记-16网络通信(5)

    4、建立连接 如果我们处理一个面向连接的网络服务(SOCK_STREAM或者SOCK_SEQPACKET),那么在...

  • APUE读书笔记-16网络通信(9)

    6、套接字选项 套接字机制提供了两个套接字选项接口来控制套接字的行为。一个接口用来设置选项,另外一个接口用来允许我...

  • APUE读书笔记-16网络通信(4)

    (4)将地址和套接字进行关联 客户的套接字关联的地址并不是重点,我们可以让系统选择一个默认的地址。但是对于一个服务...

  • APUE读书笔记-16网络通信(7)

    面向连接与面向无连接通讯举例 面向连接的客户端的例子 下面的程序,显示了一个客户的命令,这个命令和服务进行通信,从...

  • APUE读书笔记-16网络通信(10)

    7、带外数据 带外数据是一个被一些通信协议支持的可选的特性,它允许比普通数据优先级高的数据被传送。带外数据会被优先...

  • APUE读书笔记-16网络通信(1)

    1、简介 在前面的章节,我们查看了pipes,FIFOs,消息队列,信号量,和共享内存这些unix提供的经典的IP...

  • APUE读书笔记-16网络通信(3)

    (3)地址查询 理想情况,一个应用程序不用知道套接字地址的内部结构。如果一个应用程序简单地把套接字地址作为sock...

网友评论

    本文标题:APUE读书笔记-16网络通信(6)

    本文链接:https://www.haomeiwen.com/subject/gjijlktx.html