网络IPC:套接字

函数摘要

函数 说明 成功 失败
创建套接字
socket 创建一个套接字 文件(套接字)描述父 -1
getsockename 获取套接字的绑定地址 0 -1
getpeername 获取对方的地址 0 -1
shudown 禁用一个套接字的I/O的读写功能 0 -1
htonl、htons 机器字节序和网络字节序之间实现转换 以网络字节序表示的32/16位整数 -1
inet_ntop、inet_pton 网络字节顺序的地址转换(二进制<---->文本字符串格式) 地址字符串指针/格式无效 -1
网络地址信息
gethostent 查询网络地址信息 指针 NULL
sethostent 会打开文件,如果文件被打开,将其回绕
endhostent 关闭文件
getprotobyname 协议名称和协议号之间映射 指针 NULL
getprotobynumber
getproto
getservbyname 服务器名和端口 指针 NULL
getservbyport
getservbynet
getaddrinfo 将一个主机名和一个服务器名的关系映射到一个地址空间 0 非0错误码
freeaddrinfo 释放缓冲空间
get_strerror 将getaddrinfo失败的错误码转换成错误消息 指向描述错误的字符串的指针
getnameinfo 套接字地址(addr)被翻译成一个主机名和一个服务器名映射到一个地址 0 非0错误码
服务器
bind 关联服务器地址和套接字 0 -1
listen 监听 0 -1
accept 阻塞直到获得客户端请求并建立连接 文件(套接字)描述符,参数中地址获取客户端地址 -1
客户端
connect 连接服务器地址 0 -1
数据传输
send 发送 放回发送的字节数 -1
sendto sendto可以在无连接的套接字上指定一个目标地址 放回发送的字节数 -1
sendmsg 调用带有msghdr结构的sengmsg来指定多重缓冲区数据,和writev函数相似 放回发送的字节数 -1
recv 接受 返回数据的字节长度;若无可用数据或对等方已经按序结束,返回0 -1
recvfrom 接受 返回数据的字节长度;若无可用数据或对等方已经按序结束,返回0 -1
recvmsg 从指定多重缓冲区数据接受带有msghdr结构的sengmsg来,和readv函数相似 返回数据的字节长度;若无可用数据或对等方已经按序结束,返回0 -1
套接字选项
setsockopt 设置套接字选项 0 -1
getsockopt 获取套接字选项 0 -1

名称解释

套接字描述符

  • 套接字描述符:套接字是通信端点的抽象。在UNIX系统中被当作是一种文件描述符,可以在上面直接使用I/O函数(read/write)。

寻址

  • 字节顺序:一个处理器架构特性,用于指示像整数这样的大数据类型的字节如何排序。
    • 大端(big-endian):最大字节地址出现在最低有效字节(Least Significant Byte,LSB)上。
    • 小端(little-endian):最小字节地址出现在最低有效字节(Least Significant Byte,LSB)上。
    • TCP/IP协议栈使用了大端字节序。

输入图片说明

地址格式:

  • 不同通信域有不同的地址格式:一个地址标识一个特定通信域的套接字端点。
  • 为使不同格式地址能够传入到套接字函数,地址强制转换成通用结构sockaddr
struct sockaddr {
    sa_family_t     sa_family;  /*地址协议:ipv4、ipv6、*//
    char            sa_data[];  /*地址变量长度*/
    ...
}
// linux的地址格式
struct sockaddr_t   {
    sa_family_t sin_family; /*地址协议:ipv4、ipv6、*/
    in_port_t sin_port; /*端口号*/
    struct in6_addr sin6_addr; /*IPv4 address*/
    unsigned char sin_size[8]; /*filer*/
}

地址查询:

  • 理想状态下应用程序不需要了解一个套接字地址的内部结构,一般把地址信息存在:

    • 静态文件:/etc/hosts和/etc/services
    • 网上服务器:域名系统(Domain Name System,DNS)或网络信息服务(Network Information Service,NIS)

套接字选项

  • 通用选项,工作在所有套接字
  • 套接字层次管理的选项,但是依赖于下层协议的支持。
  • 特定于某协议的选项,每个协议独有的。

带外数据

  • 带外数据(out-of-band data):是一些同i性能协议所支持的可选功能,与普通数据相比,它语序更高优先级的数据传送。
    • TCP将带外数据称为紧急数据(urgent data):TCP仅支持一个字节的紧急数据,如果带MSG_OOB标志发送的字节数超过一个,最后一个字节将呗视为紧急数据字节。
    • 3个send函数中的任意一个里指定了MSG_OOB标志。

非阻塞和异步I/O

  • recv没有数据可用时会阻塞等待。当套接字输出队列没有足够空间来发送消息时,send函数会阻塞。
  • 启动异步I/O的过程:
    • 建立套接字所有权,这样信号可以呗传递到合适的进程
      • fcntl中使用F_SETOWN
    • 通知套接字当I/O操作不会阻塞时发信号。
      • fcntl中使用F_SETFL命令并且启用文件标志O_ASYNC

函数详情

socket:创建一个套接字

#include <sys/socket.h>

int socket(int domain,int type,int protocal);

    -- '成功:文件(套接字)描述父;出错:-1'
  • 参数:

    • domain: 确定通信的特性,包括域,以及每种域不同的地址格式
    • type套接字的类型,进一步确定通信特征。
    • protocal: 为给定的域和套接字类型选择默认协议,通常为0
描述
AF_INET IPv4因特网域
AF_INET6 IPv6因特网域
AF_UNIX UNIX域
AF_UPSPEC 未指定

|类型|描述| |:--|:--| |SOCK_DGRAM|固定长度、无连接的、不可靠的报文传递(UDP)| |SOCK_RAW|IP协议的数据报接口| |SOCK_SEQPACKET|固定长度、有序的、可靠的、面向连接的报文传递| |SOCK_STREAM|有序的、可靠的、双向的、面向连接的字节流(TCP)|

协议 描述
IPPROTO_IP IPv4网际协议
IPPROTO_IP6 IPv6网际协议
IPPROTO_ICMP 英特网控制报文协议(Internet Control Message Protocol)
IPPROTO_RAW 原始IP数据包协议
IPPROTO_TCP 促昂数控制协议
IPPROTO_UDP 用户数据报协议(User Datagram Protocol)
  • 特点:

    • 和open类似,获取可用于I/O操作的文件描述符。
    • lseek不能以套接字描述符为参数,因为套接字不支持文件描述符偏移量的概念。
    • SOCK_DGRAM:两个对等进程之间通信时不需要逻辑连接,只需要向对等进程所使用的套接字发送一个报文。例如:数据报是自含报文,像邮寄信件
    • SOCK_STREAM:要求在本地套接字和通信的对等进程的套接字之间建立一个逻辑连接,套接字提供字节流服务,所以应用程序分辨不出报文的界限。例如: 面向连接向打电话

shudown:禁用一个套接字的I/O

#include <sys/socket.h>

int shutdown(int sockfd, int how);

    -- '成功:0;失败:-1'
  • 参数:

    • how:操作方式
      • SHUT_RD:关闭读端,无法从套接字读取数据。
      • SHUT_WR:关闭写端,无法使用套接字发送数据。
      • SHUT_RDWR:无法读写。
  • 特点:

    • close:必须要所有的活动引用都关闭,才会完全释放。当多次dup的时候,可以用shutdown来直接关闭套接字描述符。

机器字节序和网络字节序之间实现转换

#include <arpa/inet.h>

uint32_t htonl(uint32_t hostint32); '返回值:以网络字节序表示的32位整数'

uint16_t htons(uint16_t hostint16); '返回值:以网络字节序表示的16位整数'

uint32_t ntonl(uint32_t hostint32); '返回值:以主机字节序表示的32位整数'

uint16_t ntons(uint16_t hostint16); '返回值:以主机字节序表示的16位整数'

网络字节顺序的地址转换(二进制<---->文本字符串格式)

#include <arpa/inet.h>

const char *inet_ntop(int domain, const void *restrict addr, char *restrict str, socklen_t size);    
    
        -- '成功:地址字符串指针;出错:NULL'
        
const char *inet_pton(int domain, const char *restrict str, void *restrict addr);     
        -- '成功:1;格式无效:0;出错:-1'

  • 特点:

    • inet_ntop:将网络字节序的二进制地址转换成文本字符串格式。
    • inet_pton:将文本字符串格式转换成网络字节序的二进制地址。

网络地址信息

查询
#include <netdb.h>

struct hostent *gethostent(void);

     -- '成功:指针;出错:NULL'

void sethostent(int stayopen);      '会打开文件,如果文件被打开,将其回绕'

void endhostent(void);          '关闭文件'

//hostent结构

struct hostnet {
    char *h_name;           /* name of host*/
    char **h_aliases;       /* pointer to alernate host name array */
    int  h_addrtype;        /* address type */
    int  h_length;          /* length in bytes of address */
    char **h_addr_list;     /* pointer to array of network addresses */
    ...
}
  • 特点:gethostent每次调用都会刷新缓冲区。

协议名称和协议号之间映射

#include <netdb.h>

struct protoent *getprotobyname(const char *name);

struct protoent *getprotobynumber(int proto);

struct protoent *getproto(void);
    
    -- '成功:返回指针;出错:NULL'
    
void setprotoent(int stayopen);

void endprotoent(void);

struct protoent {
    char *p_name;           /* protocol name */
    char **p_aliases;       /* pointer to altername protocol name array */
    int  p_proto;       /* protocol number */
    ...
}
服务器名和端口
#include <netbd.h>

struct servent *getservbyname(const char *name, const char *proto); 

struct servent *getservbyport(int port, const char *proto);

struct servent *getservbynet(void);

    --'成功:指针;出错:NULL'
    
void setprotoent(int stayopen);

void endprotoent(void);

struct servent {
    char *p_name;           /* protocol name */
    char **p_aliases;       /* pointer to altername protocol name array */
    int  p_port;            /* port number */
    int  *s_proto;          /* name of protocol */
    ...
}
  • 特点:

    • getservbyname:将一个服务名映射到一个端口号。
    • getservbyport:将一个端口号映射到一个服务名。
    • getservbynet: 顺序少买服务数据库。
getaddrinfo、freeaddrinfo: 将一个主机名和一个服务器名映射到一个地址
#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);

    -- '成功:0;出错:非0错误码'
    
void freeaddrinfo(struct addrinfo *ai);

struct addrinfo {
    int         ai_flags;            /*customize behavior */
    int         ai_family;           /* address family */
    int         ai_socketype;        /* socket type */
    int         ai_protocol;         /* protocol */
    socklen_t   ai_addrlen;          /* length in bytes of address */
    struct sockaddr  *ai_addr;       /* address */
    char        *ai_canonname;       /* canonical name of host */
    struct addrinfo  *ai_next;       /* next in list  */
    ...
}
  • 参数:

    • ai_flags
标志 描述
AI_ADDRCONFIG 查询配置的地址类型(IPv4或IPv6)
AI_ALL 查询IPv4或IPv6地址
AI_CANONNAME 需要一个规范的名字(与别名相对)
AI_NUMERICHOST 以数字格式指定主机地址,不翻译
AI_NUMERICSERV 以服务指定为数字端口号,不翻译
AI_PASSIVE 套接字地址用于监听绑定
AI_V4MAPPED 如果没有找到IPv6地址,返回映射到IPv6个数的IPv4地址
get_strerror:将getaddrinfo失败的错误码转换成错误消息
#include <netdb.h>

const char *gai_strerror(int error);

    -- '指向描述错误的字符串的指针'

getnameinfo: 套接字地址(addr)被翻译成一个主机名和一个服务器名映射到一个地址
#include <sys/socket.h>
#include <netdb.h>

int getnameinfo(const struct sockaddr *restrict addr, socklen_t alen, char *restict host, socklen_t hostlen, char *restict service, socklen_t servlen, int flags );

    -- '成功:0;出错:非0错误码'
  • 参数:

    • socklen_t:addr所指向的地址结构体的字节长度。
    • flags
标志 描述
NI_DGRAM 服务基于数据报而非基于流
NI_NAMEREQD 如果找不到主机名,将其作为一个错误对待
NI_NOFQDN 对于本地主机,仅返回全限定域名的节点名部分
NI_NUMERICHOST 返回主机地址的数字形式,而非主机名
NI_NUMRICSCOPE 对于IPv6,返回范围ID的数字形式,而非名字
NI_NUMERICSERV 返回服务地址的数字形式(即端口号),而非名字

套接字域地址关联

关联服务器地址和套接字
#include <sys/socket.h>

int bind(int sockfd, const struct sockaddr *addr ,socklen_t len);

    -- '成功:0;错误:-1'
  • 参数:

    • socklen_t:addr所指向的地址结构体的字节长度。
获取套接字的绑定地址
#include <sys/socket.h>

int getsockename(int sockfd, struct sockaddr *restrict addr, socklen_t *restrict alenp);
    
        -- '成功:0;出错:-1'
  • 参数:

    • addr:返回的地址缓冲区
    • alenp:返回的地址长度。如果长度不匹配,地址自动阶段而不报错。
获取对方的地址
#include <sys/socket.h>

int getpeername(int sockfd, struct sockaddr *restrict addr, socklen_t *restrict alenp);
    
        -- '成功:0;出错:-1'
  • 参数:

    • addr:返回的地址缓冲区
    • alenp:返回的地址长度。如果长度不匹配,地址自动阶段而不报错。

客户端

连接
#include <sys/socket.h>

int connect(int sockfd, const struct sockaddr *addr, socklen_t len);

    -- '成功:0;出错:-1'
  • 参数:

    • socklen_t:addr所指向的地址结构体的字节长度。

服务器

监听
#include <sys/socket.h>

int listen(int sockfd, int backlog);

    -- '成功:0;出错:-1'
    
  • 参数:

    • backlog:提示系统该进程所要入队的未处理的连接请求书。由 <sys/socket.h>中的SOMAXCONN指定上限
      • 队列满,系统会拒绝多余的连接请求。
获得客户端请求并建立连接
#include <sys/socket.h>

int accept(int sockfd, struct sockaddr *restrict addr, socklen_t *restrict len);

        -- '成功:文件(套接字)描述符;出错:-1'

  • 参数:

    • socklen_t:addr所指向的地址结构体的字节长度。
  • 特点:

    • 返回的文件描述符是套接字描述符,和原始套接字(sockfd)具有相同的套接字类型和地址族。
    • 阻塞直到获得客户端请求并建立连接,如果sockfd处于非阻塞模式,accept会返回-1,并将errno=EAGEAIN。
    • 可以使用poll或select来等待一个请求到来。

数据传输:指定选项、从多个客户端接受数据包、发送外带数据

发送:
#include <sys/socket.h>

ssize_t send(int sockfd, const void *buf, size_t nbytes, int flags );


    -- '成功:放回发送的字节数;出错:-1'

  • 参数:

    • flags
标志 描述
MSG_CONFIRM 提供链路层有效以保持地址映射有效
MSG_DONTROUTE 勿将数据包路由出本地网络
MSG_DONTWAIT 允许非阻塞操作(等价于使用O_NONBLOCK)
MSG_EOF 发送数据后关闭套接字的发送端
MSG_EOR 如果协议支持,标记记录结束
MSG_MORE 延迟发送数据包允许写更多数据
MSG_NOSIGNAL 写无连接的套接字时不产生SIGPIPE信号
MSG_OOB 如果协议支持,发送带外数据
  • 特点:

    • send成功返回,只能保证无错误的发送到网络驱动程序上。并不能保证另一端进程就一定接受了数据
    • 通过指定标志来改变处理传送函数的方式
#include <sys/socket.h>

ssize_t sendto(inft sockfd, const void *buf ,size_t nbytes, int flags, const struct sockaddr *destaddr, socklen_t destlen);

        -- '成功:发送的字节数;出错:-1'
  • 特点:sendto可以在无连接的套接字上指定一个目标地址。
#include <sys/socket.h>

ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);

    -- '成功:发送的字节数;出错:-1'
    
struct iovec {
    iov_base;   //buffer的起始位置
    iov_len;    //buffer的长度
}
struct msghdr {
    void *msg_name;             /*optional addrss */
    socklen_t *msg_name;        /* address size in bytes */
    struct iovec *msg_iov;      /* iovec buffer 指针*/
    int  msg_iovlen;            /* buffer数组长度*/
    void *msg_control;          /* ancillary data */
    socklen msg_controllen;     /* number of ancillary bytes */
    int  *msg_flags;            /* flags for reveived message */
    ...
}
  • 特点:

    • 调用带有msghdr结构的sengmsg来指定多重缓冲区数据,和writev函数相似。
接受

#include <sys/socket.h>

ssize_t recv(int sockfd, void *buf, size_t nbytes, int flags);


    -- '成功:返回数据的字节长度;若无可用数据或对等方已经按序结束,返回0;出错:-1'
  • 参数:

    • flags:
标志 描述
MSG_CMSG_CLOEXEC 为UNIX域套接字上接受的文件描述符社会资执行时关闭标志
MSG_DONTWAIT 允许非阻塞操作(等价于使用O_NONBLOCK)
MSG_ERRQUEUE 接受错误信息作为辅助数据
MSG_OOB 如果协议支持,获取外带数据
MSG_PEEK 返回数据包内容而不真正取走数据包
MSG_TRUNCL 及时数据包呗截断,也返回数据包的实际长度
MSG_WAITALL 如等待知道直到所有的数据可用(仅SOCK_STREAM)
  • 特点: recv可以指定标志来控制如何接受数据。
#include <sys/socket.h>

ssize_t recvfrom(int sockfd, void *restrict buf, size_t len, int flags, struct sockaddr *restrict addr, socklen_t *restrict addrlen);

    -- '成功:返回数据的字节长度;若无可用数据或对等方已经按序结束,返回0;出错:-1'
#include <sys/socket.h>

ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);

    -- '成功:返回数据的字节长度;若无可用数据或对等方已经按序结束,返回0;出错:-1'
  • 参数:

    • flags:
标志 描述
MSG_CTRUNC 控制数据呗截断
MSG_EOR 接受记录结束符
MSG_ERRQUEUE 接受错误信息作为辅助数据
MSG_OOB 如果协议支持,获取外带数据
MSG_TRUNCL 一般数据被截断

套接字选项

  • 通用选项,工作在所有套接字
  • 套接字层次管理的选项,但是依赖于下层协议的支持。
  • 特定于某协议的选项,每个协议独有的。
设置
#include <sys/socket.h>

int setsockopt(int sockfd, int level, int option, const void *val, socklen_t len);

    -- '成功:0;出错:-1
  • 参数:

    • level: 表示了选项应用的协议。
      • 如果通用套接字:level = SOL_SOCKET
      • **如果TCP:level = IPPROTO_TCP **
      • **如果TCP:level = 协议编号 **
    • option: 表示了选项应用的协议。
option 参数val的类型 描述
SO_ACCEPTIONN int 返回信息指示该套接字是否被监听
SO_BROADCAST int 如果*val非0;广播数据报
SO_DEBUG int 如果*val非0;启动过网络驱动调试功能
SO_DONTROUTE int 如果*val非0;绕过通常路由
SO_ERROR int 返回挂起的套接字错误并清除
SO_KEEPALIVE int 如果*val非0;启用周期性keep-alive报文
SO_LINGER struct linger 当还有未发报文而套接字已关闭,延迟时间
SO_OOBINLINE int 如果*val非0;将带外数据放到普通数据中
SO_RCVBUF int 接受缓冲区的字节长度
SO_RCVLOWAT int 接受调用中返回的最小数据字节数
SO_RCVTIMEO struct timeval 套接字接受调用的超时值
SO_REUSEADDR int 如果*val非0;重用bind中的地址
SO_SNDBUF int 发送缓冲区的字节长度
SO_SNDLOWAT int 发送调用中传送的最小数据字节数
SO_SNDTIMEO struct timeval 套接字发送调用的超时值
SO_TYPE int 标识套接字类型
获取套接字选项
#include <sys/socket.h>

int getsockopt(int sockfd, int level, int option , void *restrict val, socklen_t *restrict lenp);

    -- '成功:0;出错:-1'

带外数据

判断是否已经到达紧急标记
#include <sys/socket.H>

int sockatmark(int sockfd);

        --'成功:若在标记处,返回1;若妹子啊标记处,返回0;出错:-1'

猜你喜欢

转载自my.oschina.net/u/2246410/blog/1801958