《Linux-网络编程之TCP.pdf》由会员分享,可在线阅读,更多相关《Linux-网络编程之TCP.pdf(29页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、LinuxLinux 网络编程之网络编程之 TCP(TCP(上上)1.TCP 通信流程TCP 网络编程主要是用套接字来实现网络效劳器与客户端通信,一个标准的套接字是由协议族,IP 地址与端口号组成,它是 TCP 效劳器与客户端通信的根底,TCP 的效劳器端流程:(1)建立套接字(socket)(2)将地址结构绑定到套接字上(bind)(3)监听本地端口,设置监听队列的长度,效劳器将不能同时处理多个请求,将不能处理的请求放入等待队列中(listen)(4)接受客户端的请求,返回客户端套接字描述符,效劳器与客户端利用这个新的描述符进行传输数据(accept)(5)数据传输(read,write)(
2、6)关闭套接字(close)TCP 客户端流程:(1)建立套接字(socket)(2)连接目标效劳器(connect)(3)数据传输(read,write)(4)关闭客户端套接字(close)这样,就可以实现效劳器与客户端通信了.2.相关数据结构(1)通用套接字地址类型struct sockaddrsa_family_t sa_family;/协议族char data14;/协议数据typedef unsigned short sa_family以太网中常用的地址结构:struct sockaddr_in u8 sin_len;/struct sockaddr_in 的长度u8 sin_fam
3、ily;/协议族u16 sin_port;/端口号struct in_addr sin_addr;/32位 IP 地址char sin_zero8;/没使用struct in_addru32 s_addr;/32位地址;(2)相关函数#include#include int socket(int domain,int type,int protocol);参数:domain-通信的域,AF_INET 表示 IPV4协议,PF_INET6表示 IPV6协议type-设置套接字通信的类型 SOCK_STREAM 流式套接字,SOCK_DGRAM 数据报套接字,SOCK_RAW 原始套接字proto
4、col-表示某个通信类型的特定类型,如果 type 只有一个类型,那么这个参数设置为0返回值:成功返回套接字描述符,失败返回-1#include#includeint bind(int sockfd,const struct sockaddr*my_addr,socket_t addrlen);参数:sockfd-套接字描述符my_addr-sockaddr 指针类型,指向一个套接字的地址结构addrlen-地址结构的长度返回值:成功绑定返回0,失败绑定返回-1#include int listen(int sockfd,int backlog);参数:sockfd-套接字描述符backlog
5、-等待队列的长度返回值:成功返回0,失败返回-1#include#include int accept(int sockfd,struct sockaddr*addr,socklen_t*addrlen);参数:sockfd-套接字描述符addr-指向地址结构的指针addrlen-地址结构的长度指针其中 addr 包含的是客户端的地址结构,包括客户端的 IP,端口号,协议族等等.返回值:成功返回的是用于效劳器与客户端通信的套接字描述符,失败返回-1#include#include int connect(int sockfd,struct sockaddr*addr,int addrlen);
6、参数:sockfd-客户端套接字描述符addr-需要连接的效劳器的地址结构addrlen-效劳器的地址结构的长度返回值:连接成功返回0,失败返回-1#include close(int s);参数:s-套接字描述符返回值:成功关闭套接字返回0,失败返回-13.TCP 通信的实例效劳器端:#include#include#include#include#include#include#include#define PORT 8888#define BACKLOG 2/效劳器端程序/*(1)建立一个套接字描述符,定义网络类型,协议类型和具体的协议标号 socket()(2)将套接字与具体的地址结构
7、绑定,这个地址结构包括端口号,IP 地址,网络类型 bind()(3)侦听客户端发出的请求,设置队列的长度 listen()(4)在客户端发出请求后,处理客户端的连接,返回一个新的套接字描述符用于效劳器端与客户端的通信 accept()(5)读写数据 read()vs.write()(6)当效劳器处理完数据要结构通信过程,关闭连接 close()*/void process(int sc);int main(int argc,char*argv)int ss,sc;/ss 为效劳器端的套接字描述符,sc 为客户端的套接字描述符,即返回的新的套接字描述符 struct sockaddr_in s
8、erver_addr;/效劳器端的地址结构 struct sockaddr_in client_addr;/客户端的地址结构 int err;/返回值 pid_t pid;/进程的 pid/建立一个流式套接字 ss=socket(AF_INET,SOCK_STREAM,0);/第一个参数用来设置通信的域,AF_INET 表示使用 IPv4 Internet 协议,第二个参数是套接字通信的类型,表示流式套接字,第三个参数表示某种协议的特定类型,如果只有一种类型,设置为0 if(ss0)printf(socket errorn);return-1;/设置效劳器的地址结构bzero(&server_
9、addr,sizeof(server_addr);/清0server_addr.sin_family=AF_INET;/通信的域,通常与 socket 函数的 domain一致server_addr.sin_addr.s_addr=htonl(INADDR_ANY);/本地地址server_addr.sin_port=htons(PORT);/效劳器端口/绑定地址结构到套接字描述符err=bind(ss,(struct sockaddr*)&server_addr,sizeof(server_addr);/第一个参数是套接字描述符,第二个参数是sockaddr 指针,第三个参数是server_
10、addr 的长度if(err0)perror(bind error);return-1;/设置侦听err=listen(ss,BACKLOG);/第一个参数是效劳器端的套接字描述符,第二个参数是侦听队列的长度if(err0)perror(listen error);return-1;for(;)int addrlen=sizeof(struct sockaddr);/接收客户端的连接 sc=accept(ss,(struct sockaddr*)&client_addr,&addrlen);/第一个参数是效劳器端的套接字描述符,第二个参数是 sockaddr 结构体指针,第三个参数是socka
11、ddr 长度的指针if(sc0)continue;/客户端的信息存储在 sockaddr 结构体 int sin_port=client_addr.sin_port;/返回连接的客户端端口号 printf(port is%dn,sin_port);/建立一个子进程来处理连接pid=fork();if(pid=0)/子进程 close(ss);/关闭效劳器的侦听 process(sc);/处理连接,用返回的新的客户端描述符来处理连接else close(sc);/在父进程关闭客户端的连接void process(int sc)ssize_t size=0;char buffer1024;for(
12、;)size=read(sc,buffer,1024);/返回读取的字节数 if(size=0)return;write(1,buffer,size);/终端输出 sprintf(buffer,%d bytesn,size);write(sc,buffer,strlen(buffer)+1);/发送给客户端 客户端:#include#include#include#include#include#include#include#define PORT 8888/*(1)建立套接字描述符(2)connect 与效劳器端三次握手,连接效劳器端(3)read vs write(4)关闭连接*/voi
13、d process(int s);int main(int argc,char*argv)int s;struct sockaddr_in server_addr;/效劳器端的地址结构 int err;s=socket(AF_INET,SOCK_STREAM,0);/建立一个套接字描述符,参数1表示通信的域 IPv4协议,参数2表示通信类型流式套接字,参数3表示某种协议的特定类型如果只有一种类型为0 if(s0)write(s,buffer,size);/发送给客户端size=read(s,buffer,1024);/从客户端读取数据write(1,buffer,size);/将数据写到标准输
14、出测试:rootlocalhost#./Serverport is 62361hellorootlocalhost#./Client 127.0.0.1hello6 bytes总结:本文主要介绍了 TCP 网络数据发送与接收的流程,相关函数的使用,最后给出了一个具体的效劳器与客户端通信的例子.LinuxLinux 网络编程之网络编程之 TCP(TCP(下下)-I/O)-I/O 数据复用数据复用1.介绍网络数据的发送与接收有多种方式,可以直接直接从套接字读取数据或向套接字写入函数,如 read/write.也可以通过向量发送与接收数据,如 readv/writev.另外还可以通过消息发送与接收数
15、据,如 sendmsg/recvmsg.主要的 I/O 模型有以下几种:(1)阻塞 I/O-通常情况下,当数据没有到来的时候,会阻塞进程一直等待数据,如 recv 函数,它是最常用的模型.(2)非阻塞 I/O当没有数据到达时,程序不会阻塞,而是立即返回,并返回一个错误.(3)I/O 复用-阻塞 I/O 会一直阻塞程序,而I/O 复用是等待一定的时间,如果等待的时间到,那么进程就会返回.如 select 函数会按照一定的时间轮循环.(4)信号驱动 I/O 模型-进程首先注册一个信号处理回调函数,然后继续执行,当有数据到来时,通过发送信号告诉进程,然后处理数据,(5)异步 I/O 模型-与信号驱动
16、 I/O 不同的,异步 I/O 在内核完成数据复制之后才送信号通知进程。对于网络数据的处理,通常情况下采用阻塞 I/O 模型和 I/O 复用模型.2.相关函数(1)字节序的转换字节序分为大端字节序与小端字节序,所谓小端字节序就是低字节局部存放于低地址,高字节局部存放于高地址.大端字节序正好相反.在网络数据传输中,涉及到两个字节序,一个是主机字节序,另一个是网络字节序。主机字节序可以是小端字节序也可以是大端字节序,而网络字节序是大端字节 序。这样,在发送数据的时候,会进行字节序的相应转换。#include uin32_t htonl(uint32_t hostlong);/主机字节序转换成网络字
17、节序长整形uin16_t htons(uint16_t hostshort);/主机字节序转换成网络字节序短整形uint32_t ntohl(uint32_t netlong);/网络字节序转换成主机字节序长整形uint16_t ntohs(uint16_t netshort);/网络字节序转换成主机字节序短整形(2)IP 地址的转换#include#include#include in_addr_t inet_addr(const char*cp);/将字符串的 IP 地址转换为 in_addr类型char*inet_ntoa(struct in_addr in);/将 in_addr 类型
18、的二进制地址转换成字符串int inet_pton(int af,const char*src,void*dst);/将字符串类型的 IP 地址转换成 in_addr参数:af-协议族src-表示需要转换的字符串dst-指向 struct in_addr 指针返回值:返回0表示转换成功,-1表示协议 af 不支持。const char*inet_ntop(int af,const void*src,char*dst,socklen_t cnt);/把 in_addr 类型的二进制地址转换成字符串参数af-协议族src-指针 struct in_addr 的指针dst-保存转换字符串的缓冲区cn
19、t-此缓冲区的长度返回值:当发生错误时,返回 NULL.成功返回指向 dst 的指针。(3)获得主机信息#include struct hostent*gethostbyname(const char*name);返回值:成功返回指向 hostent 的指针,返回 NULL 表示发生错误。错误类型保存在 errno 中:HOST_NOT_FOUND:表示查询的主机不存在NO_ADDRESS 和 NO_DATA:请求的名称合法但没有适宜的 IP 地址NO_RECOVERY:域名效劳器不响应TRY_AGAIN:域名效劳器出现临时性错误,请重试。struct hostent char*h_name;
20、/主机的正式名称char*h_aliases;/别名列表int h_addrtype;/主机地址类型int h_length;/地址长度char*h_addr_list;/地址列表#define h_addr h_addr_list0struct hostent*gethostbyaddr(const void*addr,int len,int type);/通过地址来查看主机的信息参数:addr-struct in_addr 指针,表示主机的 IP 地址len-所指地址的长度,即 sizeof(struct in_addr)type-表示协议族返回值:成功返回 hostent 指针,错误返回
21、 NULL,同上.(4)协议名称处理函数struct protent*getprotobyname(const char*name);/通过协议名获得相关的协议项struct protent*getprotobynumber(int proto);/通过协议值获得相关协议项void setprotoent(int stayopen);/设 置 协 议 文 件 的 翻 开 状 态,翻 开/etc/protocols 文件struct protoentchar*p_name;/协议的官方名称char*p_aliases;/协议的别名int p_proto;/协议的值(5)recv 与 send 函
22、数#include#include ssize_t recv(int s,void*buf,size_t len,int flags);函数 recv 表示从套接字 s 接收数据放到数据缓冲区 buf 中,参数:s-套接字描述符buf-接收缓冲区指针len-接收缓冲区的长度(缓冲区的 size,如1024)flags-表示接收数据的方式,通常为0flags:MSG_DONTWAIT-非阻塞操作,立刻返回MSG_ERRQUEUE-错误消息从套接字错误队列接收MSG_OOB-接收带外数据MSG_PEEK-查看数据,不进行数据缓冲区的清空MSG_TRUNC-返回所有数据,即使指定缓冲区过小MSG_W
23、AITALL-等待所有消息返回值:成功返回接收的字节数,错误返回-1.查看 errno 可获得错误信息.ssize_t send(int s,const void*buf,size_t len,int flags);将缓冲区 buf 中大小为 len 的数据发送出去.参数:s-发送数据的套接字描述符buf-发送缓冲区指针len-要发送的数据长度(即使缓冲区大小为1024,只有10个字节数据要发送,那么len=10)flags-发送数据的方式返回值:成功返回发送数据的字节数,发送错误返回为-1.(6)readv 与 writev 函数这两函数可以使用向量缓冲区进行数据的发送与接收,即多个缓冲区.
24、#include ssize_t readv(int fd,const struct iovec*vector,int count);struct ivoec void*iov_base;/向量缓冲区的地址size_t iov_len;/向量缓冲区的大小参数:fd-套接字描述符vector-缓冲区指针count-vector 的个数struct iovec 就是一个缓冲区,可以接收 count 个缓冲区的数据返回值:成功返回表示接收到的字节数,失败返回-1,返回值保存在 errno#include ssize_t writev(int fd,const struct iovec*vector,
25、count);参数:fd-套接字描述符vector-向量缓冲区指针count-缓冲区的个数返回值:成功返回表示发送的字节数,失败返回-1(6)recvmsg 函数与 sendmsg 函数#include#include ssize_t recvmsg(int s,struct msghdr*msg,int flags);从套接字 s 中接收数据放入缓冲区 msg 中。参数:s-套接字描述符msg-struct msghdr 结构体指针flags-接收标志信息同 send,recv 标志.struct msghdr void*msg_name;/可选地址socklen_t msg_namelen
26、;/可选地址的长度struct iovec*msg_iov;/接收数据的缓冲区size_t msg_iovlen;/msg_iov 的数量void*msg_control;/控制 缓冲区,根据 msg_flags 放入不同的值socklen_t msg_controllen;/控制缓冲区的长度int msg_flags;/接收消息的标志返回值:成功返回接收的字节数,失败返回-1.ssize_t sendmsg(int s,const struct msghdr*msg,int flags);向 msg 中写入数据并发送.参数:s-套接字描述符msg-struct msghdr 结构体指针fla
27、gs-发送数据的标志返回值:成功返回发送的字节数,失败返回-1.(7)I/O 复用函数#include#include#include intselect(intnfds,fd_set*readfds,fd_set*writefds,fd_set*exceptfds,structtimeval*timeout);select 函数对文件描述符进行查询,查看目标是否可以进行读,写或者是错误操作,直到条件满足时才进行真正的 I/O 操作.参数:nfds-所有文件描述符最大值加1readfds-读文件描述符集合,这个文件描述符集合监视任何可读文件,当 select返回时,readfds 将去除不可读
28、的文件描述符,只留下可读的文件描述符writefds-写文件描述符集合,这个文件描述符集合监视任何可写文件,当select 返回进,writefds 将去除不可写的文件描述符,只留下可写的文件描述符excepts-异常文件描述符集合,这个文件描述符集合监视是否有错误发生.可用于其它用途,如监视带外数据 OOB,select 返回进将去除其它文件描述符,只留下带外数据。timeout-等待的最长时间,超过此时间,select 函数返回。如果 NULL,表示阻塞操作一直等待.timeout 的值为0,select 立即返回struct timevaltime_t tv_sec;/秒long tv_
29、usec;/微秒4个宏:FD_ZERO清理文件描述符集合FD_SET-将某个文件描述符集合参加文件描述符FD_CLR-将某个文件描述符从文件描述符集合中移除FD_ISSET-测试文件描述符是否在文件描述符集合中函数小结:read/write,readv/writev 可用于任何文件描述符recv/send,recvmsg/sendmsg 只用于套接字描述符readv/writev,recvmsg/sendmsg 可发送多个缓冲区数据read/write,recv/send 只能发送一个缓冲区数据recv/send,recvmsg/sendmsg 具有可选标志 flags.recvmsg/sen
30、dmsg 具有控制信息。3.例子效劳器:#include#include#include#include#include#include#include#include#define PORT 8888#define BACKLOG 2void process(int sc);/send 与 recv 函数,阻塞操作与非阻塞操作void processv(int s);/readv,writev 向量操作void processmsg(int s);/recvmsg,sendmsg 将向量挂载到消息结构上int main(int argc,char*argv)int ss,sc;/ss 为效劳
31、器端套接字描述符,sc 为客户端套接字描述符 struct sockaddr_in server_addr;/效劳器地址结构 struct sockaddr_in client_addr;/客户端地址结构 int err;pid_t pid;/建立套接字描述符 ss=socket(AF_INET,SOCK_STREAM,0);/第一个参数是域,使用 IPv4协议,第二个参数是通信类型,第三个参数表示某个具体的协议,如果只有一个为0,建立一个流式套接字 if(ss0)perror(socket error);return-1;/将端口地址结构绑定到套接字描述符 bzero(&server_add
32、r,sizeof(server_addr);/清0 server_addr.sin_family=AF_INET;/协议族 server_addr.sin_port=htons(PORT);/htons 主机字节序转换为网络字节序,端口号 server_addr.sin_addr.s_addr=htonl(INADDR_ANY);/IP 地址主机字节序长整形转换为网络字节序 err=bind(ss,(struct sockaddr*)&server_addr,sizeof(server_addr);/将地址结构绑定到套接字描述符 if(err0)perror(bind error);retur
33、n-1;/设定侦听队列的长度err=listen(ss,BACKLOG);if(err0)perror(listen error);return-1;/等待客户端连接效劳器for(;)int addrlen=sizeof(struct sockaddr);sc=accept(ss,(struct sockaddr*)&client_addr,&addrlen);/客户 端的信息保存在地址结构 client_addr 中,新返回的描述符 sc 用于客户端与效劳器端发送与接收消息 if(sc0)write(1,buffer,size);sprintf(buffer,%d bytes receive
34、,size);/向客户端发送数据send(s,buffer,strlen(buffer)+1,0);/使用 readv,writev 向量缓冲区来接收和发送数据void processv(int s)/*用 readv,writev 向量来完成数据的接收与响应ssize_t readv(int s,const struct iovec*v,int count);readv 函数可用来接收多个缓冲区数据返回值:成功读取的字节数,当返回1值表示发生错误,errno第一个参数表示套接字描述符iovect 是一个向量指针struct iovec void*iov_base 向量缓冲区的地址 size_
35、t iov_len 向量缓冲区的长度第三个参数表示 iovec 的个数,即缓冲区的个数ssize_t writev(int fd,const struct iovec*v,int count);第一个是套接字描述符struct iovec void*iov_base size_t iov_len第三个表示缓冲区的个数,即 struct iovec*的个数*/使用3个向量缓冲区char buffer30;/向量缓冲区ssize_t size=0;struct iovec*v=(struct iovec*)malloc(3*sizeof(struct iovec);/申请3个向量if(!v)per
36、ror(not enough memory);/每一个缓冲区占10个字节v0.iov_base=buffer;v1.iov_base=buffer+10;v2.iov_base=buffer+20;v0.iov_len=v1.iov_len=v2.iov_len=10;/表示接收缓冲区的大小bzero(v0.iov_base,10);bzero(v1.iov_base,10);bzero(v2.iov_base,10);for(;)size=readv(s,v,3);/先从客户端读入消息,放到3个向量缓冲区 printf(size=%dn,size);printf(v0.iov_len=%dn
37、,strlen(v0.iov_base);write(1,v0.iov_base,strlen(v0.iov_base);printf(v1.iov_len=%dn,strlen(v1.iov_base);write(1,v1.iov_base,strlen(v1.iov_base);printf(v2.iov_len=%dn,strlen(v2.iov_base);write(1,v2.iov_base,strlen(v2.iov_base);if(size=0)return;/响应客户端sprintf(v0.iov_base,%d,size);sprintf(v1.iov_base,byt
38、es alt);sprintf(v2.iov_base,ogethern);v0.iov_len=strlen(v0.iov_base);/发送数据时,表示发送数据的实际长度v1.iov_len=strlen(v1.iov_base);v2.iov_len=strlen(v2.iov_base);writev(s,v,3);/发送给客户端/使用 recvmsg,sendmsg 将向量缓冲区挂载到消息上void processmsg(int s)/*ssize_t recvmsg(int s,struct msghdr*msg,int flags);从套接字 s 中接收数据放到缓冲区 msg 中
39、,将向量挂载到消息结构 msg_iov 上。返回值:函数返回成功接收到的字节数,1时表示发生错误,错误码在 errno中.第一个参数是套接字描述符,第二个参数是struct msghdr 结构体,第三个参数是一些标志,如 MSG_DONTWAIT,MSG_WAITALL.struct msghdr void*msg_name;/可选地址,为 strcut sockaddr 指针 socketlen_t msg_namelen;/msg_name 的长度 struct iovec*msg_iov/接收数据的向量缓冲区 size_t msg_iovlen;/msg_iov 中的元素个数 void*
40、msg_control;/msg_control 指向缓冲区,根据 msg_flags 放入不同的值,控制消息 socklen_t msg_controllen;/为 msg_control 指向缓冲区的大小 int msg_flags;/接收消息的标志ssize_t sendmsg(int s,const struct msghdr*msg,int flags);向套接字描述符 s 中写入数据*/char buffer30;ssize_t size=0;struct msghdr msg;/消息结构struct iovec*v=(struct iovec*)malloc(3*sizeof(s
41、truct iovec);if(!v)perror(memory error);return;v0.iov_base=buffer;v1.iov_base=buffer+10;v2.iov_base=buffer+20;v0.iov_len=v1.iov_len=v2.iov_len=10;bzero(v0.iov_base,10);bzero(v1.iov_base,10);bzero(v2.iov_base,10);/初始化消息结构msg.msg_name=NULL;msg.msg_namelen=0;msg.msg_control=NULL;/没有控制域msg.msg_controlle
42、n=0;msg.msg_iov=v;/挂载向量指针msg.msg_iovlen=3;/表示使用的向量缓冲区的个数msg.msg_flags=0;/无特殊操作int i=0;fd_set fdset;/套接字文件描述符集合FD_ZERO(&fdset);FD_SET(s,&fdset);/将套接字文件描述符参加到文件描述符集合中去int ret;struct timeval tv;for(;)tv.tv_sec=5;/设置超时时间tv.tv_usec=0;FD_ZERO(&fdset);FD_SET(s,&fdset);/需要重新把套接字描述符 s 参加到文件描述符集合中去ret=select(
43、s+1,&fdset,NULL,NULL,&tv);/监视套接字描述符是否有数据可读if(ret=-1)perror(select);return;else if(ret=0)/如果超时,那么 fdset 会去除文件描述符集合中的文件描述符 printf(no data is available within five seconds.n);if(!FD_ISSET(s,&fdset)printf(s is not in fdsetn);continue;/return 在5秒之内如果没有数据到来,那到就直接返回到 accept等待下一个客户端else printf(data is avail
44、ablen);if(FD_ISSET(s,&fdset)/如果有满足条件的文件描述符,那么文件描述符集合仍然保存原来的文件描述符,即集合中保存满足条件的文件描述符 printf(s is in fdsetn);v0.iov_len=10;size=recvmsg(s,&msg,0);/从套接字中读取数据到缓冲区 msg,当按下 CTRL+C关闭时返回的长度为0printf(size=%dn,size);if(size=0)return;/显示收到客户端信息 write(1,v0.iov_base,size);/打印第一个缓冲区内的数据,size 是接收到的缓冲区数据长度 sprintf(v0.
45、iov_base,%d,size);sprintf(v1.iov_base,bytes alt);sprintf(v2.iov_base,pogethern);v0.iov_len=strlen(v0.iov_base);/表示第一个缓冲区发送的数据长度,第一个缓冲区发送多少数据 v1.iov_len=strlen(v1.iov_base);/表示第二个缓冲区发送数据的长度第二个缓冲区发送多少数据 v2.iov_len=strlen(v2.iov_base);/表示第三个缓冲区发送数据的长度第三个缓冲区发送多少数据 sendmsg(s,&msg,0);/向客户端发送消息客户端:#include
46、#include#include#include#include#include#include#include#define PORT 8888void process(int s);void processv(int s);void processmsg(int s);static int s;/客户端套接字描述符void sig_process(int signo);/客户端信号处理函数int main(int argc,char*argv)/客户端首先建立一个套接字描述符struct sockaddr_in server_addr;/地址结构用于连接效劳器if(argc=1)print
47、f(input server addrn);signal(SIGINT,sig_process);s=socket(AF_INET,SOCK_STREAM,0);if(s0)printf(size=%dn,size);send(s,buffer,size,0);/向效劳器端发送数据,如果 flags为0,那么send函数与 write 函数相同 /size=recv(s,buffer,1024,0);/从效劳器端接收到数据 /write(1,buffer,size);/将数据写到标准输出,即打印到终端void processv(int s)char buffer30;ssize_t size=
48、0;struct iovec*v=(struct iovec*)malloc(3*sizeof(struct iovec);if(!v)printf(not enough memoryn);return;v0.iov_base=buffer;v1.iov_base=buffer+10;v2.iov_base=buffer+20;v0.iov_len=v1.iov_len=v2.iov_len=10;bzero(v0.iov_base,10);/为防止出现乱码,先清0bzero(v1.iov_base,10);bzero(v2.iov_base,10);int i=0;for(;)size=re
49、ad(0,v0.iov_base,10);/从标准输入读入 if(size0)v0.iov_len=size;/实际发送数据的长度write(1,v0.iov_base,strlen(v0.iov_base);printf(%dn,strlen(v0.iov_base);write(1,v1.iov_base,strlen(v1.iov_base);printf(%dn,strlen(v1.iov_base);write(1,v2.iov_base,strlen(v2.iov_base);printf(%dn,strlen(v2.iov_base);writev(s,v,1);/发送给效劳器,
50、小于10个字节放在 v0发送 v0.iov_len=v1.iov_len=v2.iov_len=10;size=readv(s,v,3);/从效劳器读取数据 printf(size=%dn,size);for(i=0;i0)printf(v%d.len=%dn,i,vi.iov_len);/3段内存是连接在一起的,前10个是 v0,次之为 v1,最后为 v2 write(1,vi.iov_base,10);/标准输出 void processmsg(int s)char buffer30;/用 buffer 初始化3个缓冲区 ssize_t size=0;struct msghdr msg;/