网络IPC:套接字接口概述

套接字接口实现了通过网络连接的不同计算机之间的进程相互通信的机制。

套接字描述符(创建套接字)

套接字是通信端点的抽象,为创建套接字,调用socket函数

#include<sys/socket.h>
int socket(int domain,int type,int protocol);
/*返回:成功,返回套接字描述符,失败,-1*/
  • 参数domain:确定通信特性,包括地址格式。如AF_INET IPV4 因特网域
  • 参数type:确定套接字类型,进一步确定套接字类型,如SOCK_DGRAM 固定长度,无连接,不可靠的报文传递(默认协议 UDP)。SOCK_STREAM 有序的,可靠的,双向的,面向连接的字节流(默认协议 TCP)。
  • 参数protocol:通常为零,表示为给定的域和套接字类型选取默认协议。

例如

clientfd = socket(AF_INET,SOCK_STREAM,0);

其中,AF_INET 表明我们正在使用因特网,SOCK_STREAM表明这个套接字是因特网连接的一个端点,0默认为使用TCP协议。

套接字地址结构

从UNIX内核角度来看,一个套接字就是通信的一个端点,从unix程序来看,一个套接字就是一个相应描述符的打开文件
在利用套接字接口进行通信时,必须通过地址标识来标识一个目标通信进程,这个地址标识被称为套接字地址,是由一个计算机的网络地址和一个端口号表示,计算机网络地址用来标识特定计算机,端口号用来标识特定进程。
一个地址标识一个特定通信域的套接字端点,地址格式与这个特定的通信域有关,为使不同的地址格式能传入套接字函数,地址会被强制转换为一个通用的地址结构sockaddr:

struct sockaddr{
    sa_family_t   sa_family;    //address family
    char          sa_data[ ];   //variable-length address
    ......
    ......
};

地址查询

POSIX.1定义了若干新函数,允许一个应用程序将一个主机名和一个服务名映射到一个地址,或者反之。
getaddrinfo 函数允许将一个主机名和一个服务名映射到一个地址。

#include<sys/socket.h>
#include<netdb.h>
int getaddrinfo(const char *restrict host,
                const char *restrict service,
                const struct addrinfo *restrict hint,
                struct addrinfo **restrict res);
void freeaddrinfo (struct addrinfo *ai);

getnameinfo函数将一个地址转化为一个主机名和服务名

#include<sys/socket.h>
#include<netdb.h>
int getnameinfo(const struct sockaddr *restrict addr,socklen_t alen,
                char *restrict host,socklen_t hostlen.
                char *restrict service,socklen_t servlen,int flages);

将套接字与地址关联

对于服务器需要,需要给一个接收客户端请求的服务器套接字关联一个众所周知的地址。
使用bind函数来关联地址和套接字

#include<sus/socket.h>
int bind(int sockfd,const struct sockaddr *addr,socklen_t len);
/*返回值:成功,0;失败,-1*/

对于使用的地址有以下一些限制

  • 在进程正在运行的计算机上指定的地址必须有效;不能指定其他机器的地址。
  • 地址必须和创建套接字的地址族所支持的格式相匹配。
  • 地址中的端口号必须小于1024,除非该进程具有超级权限
  • 一般只能将一个套接字端点绑定在一个给定的地址上,尽管有些协议支持多重绑定。

建立连接

如果要处理一个面向连接的网络服务(SOCK_STREAM或SOCK_SEQPACKET),那么在开始交换数据之前,需要在请求服务的进程套接字和提供服务的进程套接字之间建立一个连接,使用connect函数建立连接。

#include<sys/socket.h>
int connect(int sockfd,const struct sockaddr* addr ,socklen_t len);
/*返回:成功 0,失败 -1*/

服务器调用listen函数来宣告它愿意接受连接请求.

#include<sys/socket.h>
int listen(int sockfd,int backlog);
/*返回:成功 0;失败 -1*/

参数backlog提供了一个提示,提示系统进程所要入队未完成连接的请求数量。
一但服务器调用了listen,所用的套接字就能完成接受连接请求。使用accept函数获得连接请求并建立连接。

#include<sys/socket.h>
int accept(int sockfd,struct sockaddr *restrict addr,socklen_t *restrict len);
/*返回值 成功:套接字描述符;失败 -1*/

注:

  • sockfd:为监听描述符,用来保持可用状态,并接受其他连接请求。
  • 函数返回的描述符为连接描述符,该描述符用来连接到调用connect的客户端。

数据传输

套接字端点表示为一个文件描述符,那么只要建立连接,就可以使用read,和writes来通过套接字通信。
还有6个专为套接字设计的数据传输函数
1.send函数和write很像,但是通过指定标志来改变处理传输数据的方式。

#include<sys/socket.h>
ssize_t send(int sockfd, const void *buf ,size_t nbytes ,int flags);
/*返回值 成功:返回发送的字节数;失败:-1*/

2.sendto函数,和send函数区别在于sendto函数可以在无连接的套接字上指定一个目标地址

#include<sys/socket.h>
ssize_t sendto (int sockfd ,const void *buf ,size_t nbytes ,int flages, const struct sockaddr *destaddr ,socklen_t destlen);
/*返回值 成功:返回发送的字节数;失败:-1*/

3.sendmsg函数

#include<sys/socket.h>
ssize_t sendmsg(int sockfd, const struct msghdr *msg  ,int flags);
/*返回值 成功:返回发送的字节数;失败:-1*/

4.recv函数,与read类似,但recv函数可以指定标志来控制如何接收数据

#include<sys/socket.h>
ssize_t recv(int sockfd,void *buf, size_t nbytes, int flags);
/*返回值:成功,返回数据的字节长度;若无可用数据或对等方已经按序结束,返回 0;失败 -1*/

5.recvfrom函数,可以得到数据发送者源地址。

#include<sys/socket.h>
ssize_t recvfrom(int sockfd,void *buf, size_t nbytes, int flags,struct sockaddr *restrict addr,socklen_t *restrict addrlen);
/*返回值:成功,返回数据的字节长度;若无可用数据或对等方已经按序结束,返回 0;失败 -1*/

6.recvmsg函数,类似readv。

#include<sys/socket.h>
ssize_t recvmsg(int sockfd,struct msghdr *msg, int flags);
/*返回值:成功,返回数据的字节长度;若无可用数据或对等方已经按序结束,返回 0;失败 -1*/