套接字端点表示为文件描述符,只要建立连接,就可以使用read和write来通过套接字通信。在套接字描述符上采用read和
write是非常有意义的,因为可以传递套接字描述符到那些原来设计为处理本地文件的函数。而且可以安排传递套接字描述
符到执行程序的子进程,该子进程并不解释套接字。
但是如果想指定选项,从多个客户端接受数据报或者发送带外数据,则需要采用六个传递数据的套接字函数中的一个。
最简单的send,它和write很像,但是可以指定标志来改变处理传输数据的方式。
#include <sys/socket.h> ssize_t send(int sockfd, const void *buf, size_t nbytes, int flag); //成功则返回发送的字节数,出错则返回-1.send支持第四个参数flags。下表总结了这些标志:
send成功返回仅保证数据已经无错误地发送到网络上,但并一定能被另一端接受到。
对于支持为报文设限的协议,如果单个报文超过协议所支持的最大长度,send失败并将errno设为EMSGSIZE;对于字节流协议,
send会阻塞知道整个数据被传输。
函数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.对于面向连接的套接字,目的地址是忽略的,因为目的地址蕴含在连接中。对于无连接的套接字,不能使用send,除非在
调用connect时预先设定了目标地址,或者采用sendto来提供另一种发送报文方式。
可以使用不止一个的选择来通过套接字发送数据,可以调用带有msghdr结构的sendmsg来制定多重缓冲区传输数据。
#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 IO buffers
int msg_iovlen; //number of elemnets in array;
void *msg_control; //ancillary data;
socklen_t msg_controllen; //number of ancillary bytes;
int msg_flags; //flags for received message;
......
};
函数recv和read很像,但是允许指定选项来控制如何接受数据。
#include<sys/socket.h> ssize_t recv(int sockfd, void *buf, size_t nbytes, int flags); //返回值:以字节计数的消息长度,若无可用消息或对方已经按序结束则返回0,若出错则返回-1.下图总结了flags的值:
可以使用recvfrom函数来得到数据发送者的源地址。
#include<sys/socket.h> ssize_t recvfrom(int sockfd, void *restrict buf, size_t len, int flags, struct sockaddr *restrict addr, socklen *restrict addrlen); //返回值:以字节计数的消息长度,若无可用消息或对方已经按序结束则返回0,出错则返回-1.如果addr非空,它将包含数据发送者的套接字端点地址。当调用recvfrom时,需要设置addrlen参数指向一个包含addr所指的套接字缓冲
区字节大小的整数。返回时,该整数设为该地址的实际字节大小。
因为可以获得发送者的地址,recvfrom通常用于无连接套接字。
为了将接收到的数据送入多个缓冲区,或者想接受辅助数据,可以使用recvmsg。
#include<sys/socket.h> ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags); //返回值:以字节计数的消息长度,若无可用消息或对方已经按序结束则返回0,若出错则返回-1.返回时,msghdr结构中的msg_flags字段被置为所接受数据的各种特征,具体如下表。
实践:
1.面向连接的服务器和客户端:
server.c 运行在ip地址为192.168.18.249的机器上
#include<stdio.h> #include<sys/socket.h> #include<arpa/inet.h> #include<string.h> #define MAX_SIZE 256 int main(void){ int listenfd,connfd; struct sockaddr_in servaddr,clientaddr; int n,addrlen = sizeof(struct sockaddr_in); char addr_p[16]; char rbuf[MAX_SIZE]; char sbuf[MAX_SIZE] = "i am server."; if(inet_pton(AF_INET,"192.168.18.249",&servaddr.sin_addr) < 0){ perror("inet_pton"); return -1; } memset(&servaddr,0,sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(5555); if((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1){ perror("listen"); return -1; } if(bind(listenfd, (struct sockaddr*)&servaddr,sizeof(servaddr)) == -1){ perror("bind"); return -1; } if(listen(listenfd, 10) == -1){ perror("listen"); return -1; } while(1){ if((connfd = accept(listenfd, (struct sockaddr*)&clientaddr,(socklen_t*)&addrlen)) == -1){ perror("accept"); continue; } break; } if(inet_ntop(AF_INET, &clientaddr.sin_addr.s_addr,addr_p,(socklen_t)sizeof(addr_p)) == NULL){ perror("inet_ntop"); return -1; } printf("connect client ip:%s\n",addr_p); n = recv(connfd, rbuf, MAX_SIZE, 0); if(n == -1){ perror("recv"); return -1; } rbuf[n] = '\0'; printf("%s\n",rbuf); n = send(connfd, sbuf, strlen(sbuf)+1, 0); if(n == -1){ perror("send"); return -1; } close(connfd); close(listenfd); return 0; }
client.c
#include<stdio.h> #include<sys/socket.h> #include<string.h> #include<arpa/inet.h> #define MAX_SIZE 256 int main(void){ int sockfd,n; char rbuf[MAX_SIZE],sbuf[MAX_SIZE]="i am client."; struct sockaddr_in servaddr; memset(&servaddr,0,sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(5555); if(inet_pton(AF_INET,"192.168.18.249",&servaddr.sin_addr) < 0){ perror("inet_pton"); return -1; } if((sockfd = socket(AF_INET,SOCK_STREAM,0)) == -1){ perror("socket"); return -1; } if(connect(sockfd,(struct sockaddr*)&servaddr,(socklen_t)sizeof(servaddr))<0){ perror("connect"); return -1; } if(send(sockfd, sbuf, strlen(sbuf)+1, 0) < 0){ perror("send"); return -1; } if((n =recv(sockfd,rbuf,MAX_SIZE,0)) < 0){ perror("send"); return -1; } rbuf[n]='\0'; printf("%s\n",rbuf); close(sockfd); return 0; }先运行server,再运行client:
server:
connect client ip:192.168.18.25
i am client.
client:
i am server.
2.不面向连接的服务器和客户端:
server.c 运行在ip地址为192.168.18.249的机器上
#include<stdio.h> #include<sys/socket.h> #include<arpa/inet.h> #include<string.h> #define MAX_SIZE 256 int main(void){ int sockfd; struct sockaddr_in servaddr,clientaddr; int n,addrlen = sizeof(struct sockaddr_in); char addr_p[16]; char rbuf[MAX_SIZE]; if(inet_pton(AF_INET,"192.168.18.249",&servaddr.sin_addr) < 0){ perror("inet_pton"); return -1; } memset(&servaddr,0,sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(5555); if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1){ perror("listen"); return -1; } if(bind(sockfd, (struct sockaddr*)&servaddr,sizeof(servaddr)) == -1){ perror("bind"); return -1; } n = recvfrom(sockfd, rbuf, MAX_SIZE, 0, (struct sockaddr*)&clientaddr, (socklen_t*)&addrlen); if(n == -1){ perror("recvfrom"); return -1; } if(inet_ntop(AF_INET, &clientaddr.sin_addr.s_addr,addr_p,(socklen_t)sizeof(addr_p)) == NULL){ perror("inet_ntop"); return -1; } printf("receive host ip:%s\n",addr_p); rbuf[n] = '\0'; printf("%s\n",rbuf); close(sockfd); return 0; }
#include<stdio.h> #include<sys/socket.h> #include<string.h> #include<arpa/inet.h> #define MAX_SIZE 256 int main(void){ int sockfd,n; char rbuf[MAX_SIZE],sbuf[MAX_SIZE]="i am client."; struct sockaddr_in servaddr; memset(&servaddr,0,sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(5555); if(inet_pton(AF_INET,"192.168.18.249",&servaddr.sin_addr) < 0){ perror("inet_pton"); return -1; } if((sockfd = socket(AF_INET,SOCK_STREAM,0)) == -1){ perror("socket"); return -1; } if(connect(sockfd,(struct sockaddr*)&servaddr,(socklen_t)sizeof(servaddr))<0){ perror("connect"); return -1; } if(send(sockfd, sbuf, strlen(sbuf)+1, 0) < 0){ perror("send"); return -1; } if((n =recv(sockfd,rbuf,MAX_SIZE,0)) < 0){ perror("send"); return -1; } rbuf[n]='\0'; printf("%s\n",rbuf); close(sockfd); return 0; }先运行server,再运行client
server端显示:
receive host ip:192.168.18.25
i am client.