一,socket基本
1,socket(3)函数原型
用来创建一个主动类型的socket套接字文件。
返回值:
>=0
成功创建socket,返回socket对应的文件描述符。
=-1
创建失败。
#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
int domain//协议族。例如AF_INET、AF_INET6即(ipv4,ipv6)等。
AF_INET 选择ipv4协议族
AF_INET6 选择ipv6协议族
int type//指定socket类型。常用的socket类型有,SOCK_STREAM、SOCK_DGRAM等。
SOCK_STREAM 流式的协议栈,代表协议有TCP等。
SOCK_DGRAM 数据报文的协议栈,代表协议有UDP等。
int protocol//最终确定的协议,设为0时,会自动选择type类型对应的默认协议。
2,bind(3)函数原型
前面,我们socket()指定了我们会使用什么样的协议去通信,但是我们对应的ip地址和端口都还是空着的,我们bind()的作用便是给socket()绑定上确定的协议头,IP地址以及端口,当然,在绑定之前,我们需要将对应的数据初始化。
返回值:
>=0
成功返回0。
=-1
失败返回SOCKET_ERROR。
#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
int sockfd//socket的文件描述符。
const struct sockaddr *addr//一个结构体指针,指向sockfd要绑定什么样的协议的首地址。
socklen_t addrlen//地址的长度。
//通用套接字地址
struct sockaddr {
unsigned short sa_family;
char sa_data[14];
};
//IPv4套接字地址
struct sockaddr_in {
unsigned short sin_family;
unsigned short sin_port;
struct in_addr sin_addr;
unsigned char sin_zero[8];
};
struct in_addr {
unsigned int s_addr;
};
3,listen(1)函数原型
用来将一个主动类型的socket套接字变成被动类型的套接字。
返回值:
=0
成功返回0。
=-1
失败返回-1。
#include <sys/types.h>
#include <sys/socket.h>
int listen(int sockfd, int backlog);
int sockfd//要监听的socket对应的文件描述符。
int backlog//最多可以等待几个客户端,比如设置为5,那么连接第6个客户端连接服务器时,就连接不进来了。
4,accept(3)函数原型
服务端监听对应的IP和端口,监听对应的ip和端口是否被客户端连接了,accept(3)用来接收客户端connect(3)函数的连接。
返回值:
=0
成功返回0。
=-1
失败返回-1。
#include <sys/types.h>
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
int sockfd//要接收的socket的文件描述符。
struct sockaddr *addr//用于存储客户端的ip地址以及端口的一个结构体指针。
socklen_t *addrlen//用来存储客户端协议地址的长度的指针。是一个value-result argument,所以我们需要将他初始化为比struct sockaddr *addr长的值,不然就会发生参数错误。
5,connect(3)函数原型
客户端依靠此函数来发送连接请求。TCP三次握手就是这时候开始的。
返回值:
=0
成功返回0。
=-1
失败返回-1。
#include <sys/types.h>
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
int sockfd//客户端的socket对应的文件描述符。
const struct sockaddr *addr//需要连接的服务器的socket信息,含有服务器的IP地址和端口等。
socklen_t addrlen//服务器地址结构体的长度。
二,socket进阶
1,setsockopt(5)函数原型
setsockopt()用来设置参数s 所指定的socket 状态。函数用于任意类型、任意状态套接口的设置选项值。尽管在不同协议层上存在选项,但本函数仅定义了套接口级别上的选项。(一定要在bind前去调用,bind后就不生效了)。
返回值:
=0
成功返回0。
=-1
失败返回-1。
#include <sys/types.h>
#include <sys/socket.h>
int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen);
int sockfd//要设置的套接字描述符。
int level//选项定义的层次。或为特定协议的代码(如IPv4,IPv6,TCP,SCTP),或为通用套接字代码(SOL_SOCKET)。
SOL_SOCKET
IPPROTO_IP
IPPRO_TCP
int optname//选项名。level对应的选项,一个level对应多个选项,不同选项对应不同功能。
当level为SOL_SOCKET时,optname可以有以下选项(一部分)
SO_BROADCAST 允许发送广播数据 SO_REUSEADDR 允许地址复用//(常用)
SO_DEBUG 允许调试
SO_LINGER 延迟关闭连接
SO_OOBINLINE 带外数据放入正常数据流
SO_RCVBUF 接收缓冲区大小
SO_SNDBUF 发送缓冲区大小
SO_RCVLOWAT 接收缓冲区下限
SO_SNDLOWAT 发送缓冲区下限
SO_RCVTIMEO 接收超时
SO_SNDTIMEO 发送超时
当level为IPPROTO_IP时,optname可以有以下选项(一部分)
IP_HDRINCL 在数据包中包含IP首部
IP_OPTINOS IP首部选项
IP_TTL 生存时间
当level为IPPRO_TCP时,optname可以有以下选项(一部分)
TCP_MAXSEG TCP最大数据段的大小
TCP_NODELAY 不使用Nagle算法
void *optval//指向某个变量的指针,该变量是要设置新值的缓冲区。可以是一个结构体,也可以是普通变量。
socklen_t *optlen//optval的长度。
2,getsockopt(5)函数原型
getsockopt()会将参数s所指定的socket状态返回。
返回值:
=0
成功返回0。
=-1
失败返回-1。
#include <sys/types.h>
#include <sys/socket.h>
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
int sockfd//要设置的套接字描述符。
int level//选项定义的层次。或为特定协议的代码(如IPv4,IPv6,TCP,SCTP),或为通用套接字代码(SOL_SOCKET)。
SOL_SOCKET
IPPROTO_IP
IPPRO_TCP
int optname//选项名。level对应的选项,一个level对应多个选项,不同选项对应不同功能。
当level为SOL_SOCKET时,optname可以有以下选项(一部分)
SO_BROADCAST 允许发送广播数据 SO_REUSEADDR 允许地址复用//(常用)
SO_DEBUG 允许调试
SO_LINGER 延迟关闭连接
SO_OOBINLINE 带外数据放入正常数据流
SO_RCVBUF 接收缓冲区大小
SO_SNDBUF 发送缓冲区大小
SO_RCVLOWAT 接收缓冲区下限
SO_SNDLOWAT 发送缓冲区下限
SO_RCVTIMEO 接收超时
SO_SNDTIMEO 发送超时
当level为IPPROTO_IP时,optname可以有以下选项(一部分)
IP_HDRINCL 在数据包中包含IP首部
IP_OPTINOS IP首部选项
IP_TTL 生存时间
当level为IPPRO_TCP时,optname可以有以下选项(一部分)
TCP_MAXSEG TCP最大数据段的大小
TCP_NODELAY 不使用Nagle算法
const void *optval//指向某个变量的指针,该变量是要设置新值的缓冲区。可以是一个结构体,也可以是普通变量。指向存放所获得选项值的缓冲区
socklen_t *optlen//optval的长度。
三,关于域名和域名解析
1,gethostbyname(1)函数原型
用来做域名解析(将域名->IP地址)。
返回值:
=*hsotent
成功会返回一个hostent类型的结构体指针。
=null
失败返回null。
#include <netdb.h>
extern int h_errno;
struct hostent *gethostbyname(const char *name);
const char *name//域名的字符串
struct hostent
{
char *h_name;//正式的主机名称。
char **h_aliases;//这个主机的别名。
int h_addrtype;//主机名的类型。通常是AF_INET
int h_length; //地址的字节长度。
char **h_addr_list;//从域名服务器取得的主机的地址(注意只是地址,而不是ip值, 所以想要取出ip值我们需要再加一个*)。网络字节顺序。
}
在解析域名时,可能没有这个域名或域名服务器发生错误。可能返回的错误信息如下所示。可以用error来捕获错误编号。
HOST_NOT_FOUND:
主机没有找到。
NO_ADDRESS or NO_DATA:
没有IP地址或没有数据。
NO_RECOVERY:
域名服务器发生错误。
TRY_AGAIN:
请稍候再重试。
2,gethostbyaddr(3)函数原型
(用IP地址->域名)反向域名解析。
返回值:
=*hsotent
成功会返回一个hostent类型的结构体指针。
=null
失败返回null。
#include <sys/socket.h>
struct hostent *gethostbyaddr(const void *addr, socklen_t len, int type);
const void *addr//IP地址的字符串。
socklen_t len//len是这个IP地址的长度。
int type//ip地址的类型,type的值一般为AF_INET。
3,getaddrinfo(4)函数原型
gethostbyname和gethostbyaddr都已经过时了,现在我们常用的是getaddrinfo()
返回值:
=*hsotent
成功会返回一个hostent类型的结构体指针。
=null
失败返回null。
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
int getaddrinfo(const char *node, const char *service, const struct addrinfo *hints,
struct addrinfo **res);
const char *node//主机名或IP地址字符串,如果为NULL,则表示本地主机。
const char *service//服务名称或端口号字符串,如果为NULL,则返回所有可用的套接字地址结构。
const struct addrinfo *hints//用于指定期望的套接字类型、协议及其他选项的addrinfo
struct addrinfo **res//用于存储结果的addrinfo结构体指针。
struct addrinfo {
int ai_flags; /* AI_PASSIVE, AI_CANONNAME, AI_NUMERICHOST */
int ai_family; /* PF_xxx */
int ai_socktype; /* SOCK_xxx */
int ai_protocol; /* 0 or IPPROTO_xxx for IPv4 and IPv6 */
socklen_t ai_addrlen; /* length of ai_addr */
char *ai_canonname; /* canonical name for hostname */
struct sockaddr *ai_addr; /* binary address */
struct addrinfo *ai_next; /* next structure in linked list */
};
4,freeaddrinfo(1)函数原型
getaddrinfo()使用后,需要使用freeaddrinfo()释放返回的res,addrinfo结构体
返回值:无
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
void freeaddrinfo(struct addrinfo *res);
struct addrinfo *res//要释放的结构体的首地址
5,inet_pton(3)函数原型(点分十进制->网络字节序)
用来将点分十进制转换为网络字节序,相比于inet_aton()它可以适应ipv4,ipv6等,而不是只局限与ipv4。
返回值:
=1
成功会返回1。
=0
不是一个有效的IPv4点分十进制字符串或者不是一个有效的IPv6点分十进制字符串。
=-1
失败返回-1。
#include <arpa/inet.h>
int inet_pton(int af, const char *src, void *dst);
int af//地址簇类型AF_INET,AF_INET6等
const char *src//为指向字符型的地址,即ASCII的地址的首地址(ddd.ddd.ddd.ddd格式的),函数将该地址转换为in_addr的结构体,并复制在*dst中。
void *dst//in_addr 类型,用来存储网络字节序。
6,inet_ntop(4)函数原型(网络字节序->点分十进制)
将网络字节序变成点分十进制。
返回值:
=const char *
成功则返回指向存储点分十进制结构的指针。
=null
失败返回null。
#include <arpa/inet.h>
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
int af//地址簇类型AF_INET,AF_INET6等
const void *src//点分十进制字符串
char *dst//要转换的网络字节序
socklen_t size//网络字节序的长度
7,inet_aton(2)函数原型(点分十进制->网络字节序)
用来将点分十进制转换为网络字节序。inet_aton()会将点分十进制的字符串转换为网络字节序的二进制数。
此时如果要做网络用途使用的话,无需再次转换(即,无需再通过htonl做转换);但是如果想在本地上查看转换后的结果,则需要做一个转换(需要使用ntohl)。
返回值:
=1
成功会返回1。
=0
失败返回0。
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int inet_aton(const char *cp, struct in_addr *inp);
const char *cp//点分十进制字符串
struct in_addr *inp//网络字节序在这里存储
8,inet_ntoa(1)函数原型(网络字节序->点分十进制)
将网络字节序变成点分十进制。
返回值:
=char *
成功会返回点分十进制字符串。
=null
失败返回null。
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
char *inet_ntoa(struct in_addr in);
struct in_addr in//需要转换的网络字节序