套接字端点表示为文件描述符,只要建立连接,就可以使用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;
}
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端显示:
receive host ip:192.168.18.25
i am client.