建设淘宝类博客网站的主要目的是什么?

摘要:博客类网站建设,淘宝网站的建设目的是什么,长沙网站建设维护,长沙网络营销招聘完整版文章请参考: TCPIP网络编程完整版文章 文章目录第 13 章 多种 IO 函数13.1 send & r
博客类网站建设,淘宝网站的建设目的是什么,长沙网站建设维护,长沙网络营销招聘完整版文章请参考#xff1a; TCP/IP网络编程完整版文章 文章目录第 13 章 多种 I/O 函数13.1 send recv 函数13.1.1 Linux 中的 send recv13.1.2 MSG_OOB#xff1a;发送紧急消息13.1.3 紧急模式工作原理13.1.4 检查输入缓冲13.2 readv writev 函数13.2.1…完整版文章请参考 TCP/IP网络编程完整版文章 文章目录第 13 章 多种 I/O 函数13.1 send recv 函数13.1.1 Linux 中的 send recv13.1.2 MSG_OOB发送紧急消息13.1.3 紧急模式工作原理13.1.4 检查输入缓冲13.2 readv writev 函数13.2.1 使用 readv writev 函数13.2.2 合理使用 readv writev 函数第 13 章 多种 I/O 函数 13.1 send recv 函数 13.1.1 Linux 中的 send recv 首先看 send 函数定义 #include sys/socket.h ssize_t send(int sockfd, const void *buf, size_t nbytes, int flags); /* 成功时返回发送的字节数失败时返回 -1 sockfd: 表示与数据传输对象的连接的套接字和文件描述符 buf: 保存待传输数据的缓冲地址值 nbytes: 待传输字节数 flags: 传输数据时指定的可选项信息 */下面是 recv 函数的定义 #include sys/socket.h ssize_t recv(int sockfd, void *buf, size_t nbytes, int flags); /* 成功时返回接收的字节数收到 EOF 返回 0失败时返回 -1 sockfd: 表示数据接受对象的连接的套接字文件描述符 buf: 保存接受数据的缓冲地址值 nbytes: 可接收的最大字节数 flags: 接收数据时指定的可选项参数 */end 和 recv 函数的最后一个参数是收发数据的可选项该选项可以用位或bit OR运算符| 运算符同时传递多个信息。 send recv 函数的可选项意义 可选项Option含义sendrecvMSG_OOB用于传输带外数据Out-of-band dataOOMSG_PEEK验证输入缓冲中是否存在接受的数据XOMSG_DONTROUTE数据传输过程中不参照本地路由Routing表在本地Local网络中寻找目的地OXMSG_DONTWAIT调用 I/O 函数时不阻塞用于使用非阻塞Non-blockingI/OOOMSG_WAITALL防止函数返回直到接收到全部请求的字节数XO 13.1.2 MSG_OOB发送紧急消息 MSG_OOB 可选项用于创建特殊发送方法和通道以发送紧急消息。下面为 MSG_OOB 的示例代码 oob_send.c 程序 #include stdio.h #include unistd.h #include stdlib.h #include string.h #include sys/socket.h #include arpa/inet.h#define BUF_SIZE 30 void error_handling(char *message);int main(int argc, char *argv[]) {int sock;struct sockaddr_in recv_adr;if (argc ! 3){printf(Usage : %s IP port\n, argv[0]);exit(1);}sock socket(PF_INET, SOCK_STREAM, 0);memset(recv_adr, 0, sizeof(recv_adr));recv_adr.sin_family AF_INET;recv_adr.sin_addr.s_addr inet_addr(argv[1]);recv_adr.sin_port htons(atoi(argv[2]));if (connect(sock, (struct sockaddr *)recv_adr, sizeof(recv_adr)) -1)error_handling(connect() error);write(sock, 123, strlen(123));send(sock, 4, strlen(4), MSG_OOB);write(sock, 567, strlen(567));send(sock, 890, strlen(890), MSG_OOB);close(sock);return 0; }void error_handling(char *message) {fputs(message, stderr);fputc(\n, stderr);exit(1); }oob_recv.c 程序 #include stdio.h #include unistd.h #include stdlib.h #include string.h #include signal.h #include sys/socket.h #include netinet/in.h #include fcntl.h#define BUF_SIZE 30 void error_handling(char *message); void urg_handler(int signo);int acpt_sock; int recv_sock;int main(int argc, char *argv[]) {struct sockaddr_in recv_adr, serv_adr;int str_len, state;socklen_t serv_adr_sz;struct sigaction act;char buf[BUF_SIZE];if (argc ! 2){printf(Usage : %s port\n, argv[0]);exit(1);}act.sa_handler urg_handler;sigemptyset(act.sa_mask);act.sa_flags 0;acpt_sock socket(PF_INET, SOCK_STREAM, 0);memset(recv_adr, 0, sizeof(recv_adr));recv_adr.sin_family AF_INET;recv_adr.sin_addr.s_addr htonl(INADDR_ANY);recv_adr.sin_port htons(atoi(argv[1]));if (bind(acpt_sock, (struct sockaddr *)recv_adr, sizeof(recv_adr)) -1)error_handling(bind() error);listen(acpt_sock, 5);serv_adr_sz sizeof(serv_adr);recv_sock accept(acpt_sock, (struct sockaddr *)serv_adr, serv_adr_sz);//将文件描述符 recv_sock 指向的套接字拥有者F_SETOWN改为把getpid函数返回值用做id的进程fcntl(recv_sock, F_SETOWN, getpid());state sigaction(SIGURG, act, 0); //SIGURG 是一个信号当接收到 MSG_OOB 紧急消息时系统产生SIGURG信号while ((str_len recv(recv_sock, buf, sizeof(buf), 0)) ! 0){if (str_len -1)continue;buf[str_len] 0;puts(buf);}close(recv_sock);close(acpt_sock);return 0; } void urg_handler(int signo) {int str_len;char buf[BUF_SIZE];str_len recv(recv_sock, buf, sizeof(buf) - 1, MSG_OOB);buf[str_len] 0;printf(Urgent message: %s \n, buf); }void error_handling(char *message) {fputs(message, stderr);fputc(\n, stderr);exit(1); }编译运行 gcc oob_send.c -o send gcc oob_recv.c -o recv运行结果 从运行结果可以看出send 是客户端recv 是服务端客户端给服务端发送消息服务端接收完消息之后显示出来。可以从图中看出每次运行的效果并不是一样的。 代码中关于: fcntl(recv_sock, F_SETOWN, getpid());的意思是 文件描述符 recv_sock 指向的套接字引发的 SIGURG 信号处理进程变为 getpid 函数返回值用作 ID 进程. 上述描述中的「处理 SIGURG 信号」指的是「调用 SIGURG 信号处理函数」。但是之前讲过多个进程可以拥有 1 个套接字的文件描述符。例如通过调用 fork 函数创建子进程并同时复制文件描述符。此时如果发生 SIGURG 信号应该调用哪个进程的信号处理函数呢可以肯定的是不会调用所有进程的信号处理函数。因此处理 SIGURG 信号时必须指定处理信号所用的进程而 getpid 返回的是调用此函数的进程 ID 。上述调用语句指当前为处理 SIGURG 信号的主体。 输出结果可能出乎意料 通过 MSG_OOB 可选项传递数据时只返回 1 个字节而且也不快 的确通过 MSG_OOB 并不会加快传输速度而通过信号处理函数 urg_handler 也只能读取一个字节。剩余数据只能通过未设置 MSG_OOB 可选项的普通输入函数读取。因为 TCP 不存在真正意义上的「外带数据」。实际上MSG_OOB 中的 OOB 指的是 Out-of-band 而「外带数据」的含义是 通过完全不同的通信路径传输的数据 即真正意义上的 Out-of-band 需要通过单独的通信路径高速传输数据但是 TCP 不另外提供只利用 TCP 的紧急模式Urgent mode进行传输。 13.1.3 紧急模式工作原理 MSG_OOB 的真正意义在于督促数据接收对象尽快处理数据。这是紧急模式的全部内容而 TCP 「保持传输顺序」的传输特性依然成立。TCP 的紧急消息无法保证及时到达但是可以要求急救。下面是 MSG_OOB 可选项状态下的数据传输过程如图 上面是: send(sock, 890, strlen(890), MSG_OOB);图上是调用这个函数的缓冲状态。如果缓冲最左端的位置视作偏移量 0 。字符 0 保存于偏移量 2 的位置。另外字符 0 右侧偏移量为 3 的位置存有紧急指针Urgent Pointer。紧急指针指向紧急消息的下一个位置偏移量加一同时向对方主机传递以下信息 紧急指针指向的偏移量为 3 之前的部分就是紧急消息。 也就是说实际上只用了一个字节表示紧急消息。这一点可以通过图中用于传输数据的 TCP 数据包段的结构看得更清楚如图 TCP 数据包实际包含更多信息。TCP 头部包含如下两种信息 URG1载有紧急消息的数据包URG指针紧急指针位于偏移量为 3 的位置。 指定 MSG_OOB 选项的数据包本身就是紧急数据包并通过紧急指针表示紧急消息所在的位置。 紧急消息的意义在于督促消息处理而非紧急传输形式受限的信息。 13.1.4 检查输入缓冲 同时设置 MSG_PEEK 选项和 MSG_DONTWAIT 选项以验证输入缓冲是否存在接收的数据。设置 MSG_PEEK 选项并调用 recv 函数时即使读取了输入缓冲的数据也不会删除。因此该选项通常与 MSG_DONTWAIT 合作用于以非阻塞方式验证待读数据存在与否。下面的示例是二者的含义 peek_recv.c 程序 #include stdio.h #include unistd.h #include stdlib.h #include string.h #include sys/socket.h #include arpa/inet.h#define BUF_SIZE 30 void error_handling(char *message);int main(int argc, char *argv[]) {int acpt_sock, recv_sock;struct sockaddr_in acpt_adr, recv_adr;int str_len, state;socklen_t recv_adr_sz;char buf[BUF_SIZE];if (argc ! 2){printf(Usage : %s port\n, argv[0]);exit(1);}acpt_sock socket(PF_INET, SOCK_STREAM, 0);memset(acpt_adr, 0, sizeof(acpt_adr));acpt_adr.sin_family AF_INET;acpt_adr.sin_addr.s_addr htonl(INADDR_ANY);acpt_adr.sin_port htons(atoi(argv[1]));if (bind(acpt_sock, (struct sockaddr *)acpt_adr, sizeof(acpt_adr)) -1)error_handling(bind() error);listen(acpt_sock, 5);recv_adr_sz sizeof(recv_adr);recv_sock accept(acpt_sock, (struct sockaddr *)recv_adr, recv_adr_sz);while (1){//保证就算不存在待读取数据也不会阻塞str_len recv(recv_sock, buf, sizeof(buf) - 1, MSG_PEEK | MSG_DONTWAIT);if (str_len 0)break;}buf[str_len] 0;printf(Buffering %d bytes : %s \n, str_len, buf);//再次调用 recv 函数这一次没有设置任何可选项所以可以直接从缓冲区读出str_len recv(recv_sock, buf, sizeof(buf) - 1, 0);buf[str_len] 0;printf(Read again: %s \n, buf);close(acpt_sock);close(recv_sock);return 0; }void error_handling(char *message) {fputs(message, stderr);fputc(\n, stderr);exit(1); }peek_send.c 程序 #include stdio.h #include unistd.h #include stdlib.h #include string.h #include sys/socket.h #include arpa/inet.h void error_handling(char *message);int main(int argc, char *argv[]) {int sock;struct sockaddr_in send_adr;if (argc ! 3){printf(Usage : %s IP port\n, argv[0]);exit(1);}sock socket(PF_INET, SOCK_STREAM, 0);memset(send_adr, 0, sizeof(send_adr));send_adr.sin_family AF_INET;send_adr.sin_addr.s_addr inet_addr(argv[1]);send_adr.sin_port htons(atoi(argv[2]));if (connect(sock, (struct sockaddr *)send_adr, sizeof(send_adr)) -1)error_handling(connect() error);write(sock, 123, strlen(123));close(sock);return 0; }void error_handling(char *message) {fputs(message, stderr);fputc(\n, stderr);exit(1); }编译运行 可以通过结果验证仅发送了一次的数据被读取了 2 次因为第一次调用 recv 函数时设置了 MSG_PEEK 可选项。 13.2 readv writev 函数 13.2.1 使用 readv writev 函数 readv writev 函数的功能可概括如下 对数据进行整合传输及发送的函数 也就是说通过 writev 函数可以将分散保存在多个缓冲中的数据一并发送通过 readv 函数可以由多个缓冲分别接收。因此适用这 2 个函数可以减少 I/O 函数的调用次数。下面先介绍 writev 函数。 #include sys/uio.h ssize_t writev(int filedes, const struct iovec *iov, int iovcnt); /* 成功时返回发送的字节数失败时返回 -1 filedes: 表示数据传输对象的套接字文件描述符。但该函数并不仅限于套接字因此可以像 read 一样向向其传递文件或标准输出描述符. iov: iovec 结构体数组的地址值结构体 iovec 中包含待发送数据的位置和大小信息 iovcnt: 向第二个参数传递数组长度 */上述第二个参数中出现的数组 iovec 结构体的声明如下 struct iovec {void *iov_base; //缓冲地址size_t iov_len; //缓冲大小 };下图是该函数的使用方法 writev 的第一个参数是文件描述符因此向控制台输出数据ptr 是存有待发送数据信息的 iovec 数组指针。第三个参数为 2因此从 ptr 指向的地址开始共浏览 2 个 iovec 结构体变量发送这些指针指向的缓冲数据。 下面是 writev 函数的使用方法 #include stdio.h #include sys/uio.hint main(int argc, char *argv[]) {struct iovec vec[2];char buf1[] ABCDEFG;char buf2[] 1234567;int str_len;vec[0].iov_base buf1;vec[0].iov_len 3;vec[1].iov_base buf2;vec[1].iov_len 4;str_len writev(1, vec, 2);puts();printf(Write bytes: %d \n, str_len);return 0; }结果 ABC1234 Write bytes: 7readv 函数功能和 writev 函数正好相反.函数为 #include sys/uio.h ssize_t readv(int filedes, const struct iovc *iov, int iovcnt); /* 成功时返回接收的字节数失败时返回 -1 filedes: 表示数据传输对象的套接字文件描述符。但该函数并不仅限于套接字因此可以像 write 一样向向其传递文件或标准输出描述符. iov: iovec 结构体数组的地址值结构体 iovec 中包含待数据保存的位置和大小信息 iovcnt: 第二个参数中数组的长度 */下面是示例代码 readv.c 程序 #include stdio.h #include sys/uio.h #define BUF_SIZE 100int main(int argc, char *argv[]) {struct iovec vec[2];char buf1[BUF_SIZE] {0,};char buf2[BUF_SIZE] {0,};int str_len;vec[0].iov_base buf1;vec[0].iov_len 5;vec[1].iov_base buf2;vec[1].iov_len BUF_SIZE;str_len readv(0, vec, 2);printf(Read bytes: %d \n, str_len);printf(First message: %s \n, buf1);printf(Second message: %s \n, buf2);return 0; }运行结果 从图上可以看出首先截取了长度为 5 的数据输出然后再输出剩下的。 13.2.2 合理使用 readv writev 函数 实际上能使用该函数的所有情况都适用。例如需要传输的数据分别位于不同缓冲数组时需要多次调用 write 函数。此时可通过 1 次 writev 函数调用替代操作当然会提高效率。同样需要将输入缓冲中的数据读入不同位置时可以不必多次调用 read 函数而是利用 1 次 readv 函数就能大大提高效率。 其意义在于减少数据包个数。假设为了提高效率在服务器端明确禁用了 Nagle 算法。其实 writev 函数在不采用 Nagle 算法时更有价值如图