1、socket地址API

字节序

1)大小端

字节序分为大端字节序( big endian)和小端字节序( little endian)。
大端字节序是指一个整数的高位字节(23~31bit)存储在内存的低地址处,低位字节(0 ~7bit)存储在内存的高地址处。
小端字节序则是指整数的高位字节存储在内存的高地址处,而低位字节则存储在内存的低地址处。

2)转换函数

#include <netinet/in. h>
unsigned long int htonl( unsigned long int hostlong );
unsigned short int htons( unsigned short int hostshort );
unsigned long ntohl( unsigned long int netlong );
unsigned short int ntohs( unsigned short int netshort;

其中h表示主机字节序,n表示网络字节序,l表示长整型,s表示短整型。

2、通用socket地址

socket网络编程接口中表示 socket地址的是结构体 sockaddr,其定义如下:
在这里插入图片描述
sa_ family成员是地址族类型( sa_family_t)的变量。常见的协议族( protocol family,也称 domain)和对应的地址族如下所示:
在这里插入图片描述

3、IP地址的转换函数

#include <arpa/inet.h>
in_addr_t inet_addr( const char* strptr);
int inet_aton( const char* cp, struct in addr* inp );
char* inet_ntoa( struct in_addr in );

上面三个函数可用于用点分十进制字符串表示的IPv4地址和用网络字节序整数表示的IPv4地址之间的转换。其中:
inet_addr函数将用点分十进制字符串表示的IPv4地址转化为用网络字节序整数表示的IPv4地址。它失败时返回 INADDR NONE。
inet_aton函数完成和 inet_addr同样的功能,但是将转化结果存储于参数inp指向的地址结构中。它成功时返回1,失败则返回0。
inet_ntoa函数将用网络字节序整数表示的IPv4地址转化为用点分十进制字符串表示的IPv4地址

4、socket的创建

#include <sys/t ypes. h>
#include <sys/socket.h>
int socket( int domain, int type, int protocol );

domain参数告诉系统使用哪个底层协议族。对 TCP/IP协议族而言,该参数应该设置为PF_INET( Protocol Family of Internet,用于IPv4)或 PF_INET6(用于IPv6);对于UNIX本地域协议族而言,该参数应该设置为 PF UNIX。
type参数指定服务类型。服务类型主要有 SOCK_STREAM服务(流服务)和SOCK_UGRAM(数据报)服务。对TCP/IP协议族而言,其值取 SOCK_STREAM表示传输层使用TCP协议,取 SOCK_DGRAM表示传输层使用UDP协议。
protocol参数是在前两个参数构成的协议集合下,再选择一个具体的协议。不过这个值通常都是唯一的(前两个参数已经完全决定了它的值)。几乎在所有情况下,我们都应该把它设置为0,表示使用默认协议。
socket系统调用成功时返回一个 socket文件描述符,失败则返回-1并设置errno。

5、socket的命名-bind

创建 socket时,我们给它指定了地址族,但是并未指定使用该地址族中的哪个具体socket地址。将一个 socket与 socket地址绑定称为给 socket命名。

#include <sys/types.h>
#include <sys/socket. h>
int bind( int sockfd, const struct sockaddr* my_addr, socklen t addrlen);

bind将 my_addr所指的 socket地址分配给未命名的 sockfd文件描述符, addrlen参数指出该 socket地址的长度。

6、socket的监听-listen

socket被命名之后,还不能马上接受客户连接,我们需要使用如下系统调用来创建一个监听队列以存放待处理的客户连接:

#include <sys/socket. h>
int listen( int sockfd, int backlog );

sockfd参数指定被监听的 socket。
backlog参数提示内核监听队列的最大长度。监听队列的长度如果超过 backlog,服务器将不受理新的客户连接,客户端也将收到ECONNREFUSED错误信息。 backlog参数的典型值是5。

7、接收连接-accept

accept用于从 listen监听队列中接受一个连接:

#include <sys/types. h>
#include <sys/socket.h>
int accept( int sockfd, struct sockaddr . addr, socklen t *addrlen );

sockfd参数是执行过listen系统调用的监听 socket 。
addr参数用来获取被接受连接的远端 socket地址,该 socket地址的长度由 addrlen参数指出。
accept成功时返回一个新的连接socket,该 socket唯一地标识了被接受的这个连接,服务器可通过读写该 socket来与被接受连接对应的客户端通信。 accept失败时返回-1并设置ernno。

8、发起与关闭连接

1)连接-connect

服务器通过 listen调用来被动接受连接,而客户端需要通过connect来主动与服务器建立连接:

include <sys/types. h>
include <sys/socket.h>
int connect( int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen );

sockfd参数由 socket系统调用返回一个 socket,;
serv_addr参数是服务器监听的 socket地址;
addrlen参数则指定这个地址的长度;
connect成功时返回0.一旦成功建立连接, sockfd就唯一地标识了这个连接,客户端就可以通过读写 sockfd来与服务器通信。 connect失败则返回-并设置ernno。

2)关闭-close

关闭一个连接实际上就是关闭该连接对应的 socket,可以通过如下关闭普通文件描述符的系统调用来完成:

Include <unistd. h>
int close( int fd );

9、数据读写

1)TCP

#include <sys/types.h>
#include <sys/socket. h >
ssize_t recv( int sockfd, void *buf, size_t len, int flags);
 ssize_t send( int sockfd, const void *buf, size_t len, int flags);

recv读取 sockfd上的数据,buf和len参数分别指定读缓冲区的位置和大小,flags参数通常设置为0。
recv成功时返回实际读取到的数据的长度,它可能小于我们期望的长度len。因此我们可能要多次调用recv,才能读取到完整的数据。recv可能返回0,这意味着通信对方已经关闭连接了。recv出错时返回-1并设置errno 。
send往 sockfd上写入数据,buf和len参数分别指定写缓冲区的位置和大小。send成功时返回实际写入的数据的长度,失败则返回-1并设置errno 。

2)UDP

#include <sys/types.h>
#include <sys/socket. h >
ssize_t recvfrom( int sockfd, void* buf, size_t len, int flags, struct sockaddr* src_addr,socklen_t* addrlen);
ssize_t sendto( int sockfd, const void* buf, size_t len, int flags, const struct sockaddr* dest_addr, socklen_t addrlen );

recvfrom读取 sockfd上的数据,buf和len参数分别指定读缓冲区的位置和大小。因为UDP通信没有连接的概念,所以我们每次读取数据都需要获取发送端的 socket地址,即参数src_addr所指的内容,addrlen参数则指定该地址的长度。
sendto往 sockfd上写人数据,buf和len参数分别指定写缓冲区的位置和大小。 dest_addr参数指定接收端的 socket地址, addrlen参数则指定该地址的长度。

注:recvfrom/ sendto系统调用也可以用于面向连接( STREAM)的 socket的数据读写,只需要把最后两个参数都设置为NULL以忽略发送端/接收端的 socket地址(因为我们已经和对方建立了连接,所以已经知道其 socket地址了)。

10、socket选项

include <sys/socket.h>
int getsockopt( int sockfd, int level, int option_name, void* option_value, socklen_t* restrict option_len );
int setsockopt( int sockfd, int level, int option_name, const void*option_value, socklen_t option_len);

sockfd参数指定被操作的目标 socket. level参数指定要操作哪个协议的选项(即属性),比如Pv4、IPv6、TCP等。 option_name参数则指定选项的名字。option_value和 option_len参数分别是被操作选项的值和长度。
以下是常用的选项:
在这里插入图片描述

11、网络信息API

1)获取信息

Include <netdb. h>
struct hostent* gethostbyname( const char* name );
struct hostent gethostbyaddr( const void* addr, size_t len, int type );

gethostbyname函数根据主机名称获取主机的完整信息。
gethostbyaddr函数根据IP地址获取主机的完整信息。 gethostbyname函数通常先在本地的/etc/ hosts配置文件中查找主机,如果没有找到,再去访问DNS服务器。

这两个函数返回的都是 hostent结构体类型的指针, hostent结构体的定义如下:
在这里插入图片描述

2)getaddrinfo

getaddrinfo函数既能通过主机名获得IP地址(内部使用的是 gethostbyname函数),也能通过服务名获得端口号(内部使用的是 getservbyname函数)。

#include <netdb. h>
int getaddrinfo( const char* hostname, const char* service, const struct addrinfo* hints, struct addrinfo** result );

hostname参数可以接收主机名,也可以接收字符串表示的IP地址(IP4采用点分十进制字符串,IPv6则采用十六进制字符串)。
service参数可以接收服务名,也可以接收字符串表示的十进制端口号。
hints参数是应用程序给 getaddrinfo的一个提示,以对getaddrinfo的输出进行更精确的控制。hints参数可以被设置为NULL,表示允许 getaddrinfo反馈任何可用的结果。当我们使用 hints参数的时候,可以设置其 ai_fags, ai_family., ai_socktype和ai_protocol四个字段,其他字段则必须被设置为NULL。
result参数指向一个链表,该链表用于存储 getaddrinfo反馈的结果。

getaddrinfo反馈的每一条结果都是 addrinfo结构体类型的对象,结构体 addrinfo的定义如下:
在这里插入图片描述该结构体中, ai_protocol成员是指具体的网络协议,其含义和 socket系统调用的第三个参数相同,它通常被设置为0。 ai_flags成员可以取下表的标志的按位或。
在这里插入图片描述

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐