Linux Socket编程-基础知识点
套接字(Socket)按照操作类型可以分为三类:
1.流式套接字(SOCK_STREAM):面向连接的Socket,只能收发 TCP 协议的数据;
2.数据报式套接字(SOCK_DGRAM):面向无连接的Socket,只能收发 UDP 协议的数据;
3.原始套接字(SOCK_RAW)可以用来自行组装数据包,可以接收本机网卡上所有的数据帧(数据包),直接操作系统网络核心(Network Core)。
Socket基本数据结构
1.套接字地址结构
IPV4
A.新式机构
<netinet/in.h>
struct sockaddr_in
{
unsigned short sin_len; //IPv4地址长度
short int sin_family; //指代协议簇,在TCP套接字编程只能是AF_INET
unsigned short sin_port; //存储端口号(使用网络字节顺序),数据类型是一个16为的无符号整形类型
struct in_addr sin_addr;//存储IP地址,IP地址是一个in_add结构体(结构在下面)
unsigned char sin_zero[8]; //为了让sockaddr与sockaddr_in两个数据结构保持大小相同而保留的空字节
};
struct in_addr
{
unsigned long s_addr; //按照网络字节顺序存储IP地址
};
大端模式和小端模式
1)小端就是低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。
2)大端就是高位字节排放在内存的低地址端,低位字节排放在内存的高地址端。
举一个例子,比如数字0x12 34 56 78在内存中的表示形式为:
A. 大端模式:
低地址 -----------------> 高地址
0x12 | 0x34 | 0x56 | 0x78
B. 小端模式:
低地址 ------------------> 高地址
0x78 | 0x56 | 0x34 | 0x12
B.老式结构
struct sockaddr
{
unsigned short sa_family; //套接字的协议簇地址类型,TCP/IP协议对于IPv4地址类型为AF_INET
char sa_data[14];//存储具体的协议地址
};
IPV6
A.新式结构
<netinet/in.h>
struct sockaddr_in6
{
unsigned short int sin6_len; //IPv6结构长度,是一个无符号的8为整数,表示128为IPv6地址长度
short int sin6_family; //地址类型AF_INET6
unsigned short int sin6_port; //存储端口号,按网络字节顺序
unsigned short int sin6_flowinfo; //低24位是流量标号,然后是4位的优先级标志,剩下四位保留
struct in6_addr sin6_addr; //IPv6地址,网络字节顺序
};
B.老式结构
struct in6_addr
{
unsigned long s6_addr; //128位的IPv6地址,网络字节顺序
};
常用函数
1.socket函数
函数:int socket(int, int, int);
功能:创建一个套接字
解释:int socket(int af, int type, int protocol);
af:表示一个地址家族,IPv4连接时填AF_INET,IPv6连接时对应AF_INET6
type:表示套接字类型,如果是SOCK_STREAM表示创建面向连接的流式套接字;如果是SOCK_DGRAM表示面向无连接的数据包套接字;为SOCK_RAW表示创建原始套接字
protocol:表示套接口所用的协议,如果用户不指定,可以设置为0
返回值:int类型的一个指向传输提供者的句柄(套接字)
socket函数的family常值
socket函数的type常值
socket函数的protocol常值
socket函数中family和type参数的组合
2.bind函数
函数:int bind(int, const struct sockaddr *, socklen_t)
功能:将套接字绑定到指定端口和地址上
解释:
int bind( int sockfd , const struct sockaddr * my_addr, socklen_t addrlen);
sockfd:表示套接字标识
my_addr:是一个指向struct sockaddr结构体类型的指针,一般绑定 IPv4地址传入struct sockaddr_in指针,IPv6地址传入struct sockaddr_in6指针(PS:这里我也不是很清楚,sockaddr_in结构体里面有地址族,改变地址族为IPv6的不知道行不行,下面的演示和注释以前面的struct sockaddr_in结构体为主),都包含地址和端口号信息,强转成需要的类型
addrlen:确定my_addr缓冲区的长度,就是前面结构体的大小
返回值:如果函数执行成功,则返回值为0,失败则为-1
3.listen函数
函数:int listen(int, int)
功能:将套接字设置为监听模式,对于流式套接字,必须处入监听模式才能够接收客户端套接字的连接
解释:int listen(int sockfd, int backlog);
sockfd:表示套接字标识
backlog:表示等待连接的最大队列长度。例如:如果backlog被设置为2,此时有3个客户端同时发出连接请求,那么前两个客户端会放置在等待连接队列中,第三个客户端会得到错误信息。
返回值:成功则返回0,失败则返回-1
4.accept函数
函数:int accept(int, struct sockaddr * __restrict, socklen_t * __restrict)
功能:接受客户端的连接,在流式套接字中,只有在套接字处入监听状态,才能接受客户端的连接
解释:int accept(int sockfd, struct sockaddr *addr, int *addrlen);
sockfd:表示套接字标识
addr:是一个sockaddr_in结构体指针,接收连接端地址信息
addrlen:前面sockaddr_in结构体地址长度指针,用来接收连接端地址信息结构体长度
返回值:一个新的套接字,它对应于已经接受的客户端连接,对于该客户端的所有后续操作,都应使用这个新的套接字
5.close函数 函数:int close(int)
功能:关闭套接字
解释:int close(int sockfd);
6.connect函数
函数:int connect(int, const struct sockaddr *, socklen_t)
功能:发送一个连接请求
解释:int connect(int sockfd,const struct sockaddr * addr_server, int addrlen)
sockfd:表示一个正准备连接的套接字。客户端连接套接字int socket_client
addr_server:存储套接字要连接主机的地址信息结构体指针
addrlen:前面结构体的也就是addr_server结构体缓冲区的长度
返回值:成功则返回0,失败则返回-1
connect函数将激发TCP的三次握手过程,而且仅在连接建立成功或出错时才返回,其中出错有如下几种情况:
1).若TCP客户没有收到SYN包的响应,则返回ETIMEDOUT错误。如调用该函数时,内核发送一个SYN,若无响应则等待6s后再发一个,若仍无响应,则等待24s再发一个,若总共等了75s后仍未收到响应消息则返回该错误(因内核而异)。
2).若响应时RST,表明该服务器主机在我们指定的端口上没有进程等待,客户收到RST包后马上返回ECONNREFUSED错误。
3).若客户发出的SYN在中间的路由器上引发了一个“destination unreachable”的ICMP错误,则按第一种情形继续发送SYN,若在规定的时间内没有收到回应,则将ICMP错误作为EHOSTUNREACH或ENETUNREACH错误返回。
7.recv函数
函数:ssize_t recv(int, void *, size_t, int)
功能:从流式套接字中接受数据,平时开发针对TCP套接字接收数据
解释:ssize_t recv(int sockfd, void *buff, size_t nbytes, int flags)
sockfd表示流式套接字
buff用来存放recv函数接收到的数据的缓冲区
nbytes缓冲区的长度
flags 表示函数的调用方式,一般填0
成功时返回接收的字节数(收到EOF是返回0),失败是返回-1
8.send函数
函数:ssize_t send(int, const void *, size_t, int)
功能:在流式套接字中发送数据
解释:ssize_t send(int sockfd, const void *buff, size_t nbytes, int flags //表示函数的调用方式,一般填0,具体看下面表格
)
sockfd表示流式套接字
buff存放要发送数据的缓冲区
nbytes实际要改善的数据的字节数
int flags 表示函数的调用方式,一般填0
返回值:成功返回发送的字节数,错误返回-1
9.recvfrom函数
函数:ssize_t recvfrom(int, void *, size_t, int, struct sockaddr * __restrict, socklen_t * __restrict)
功能:用于接收一个数据包,并保存源地址
解释:
ssize_t recvfrom(int sockfd, void * buff, size_t nbytes, int flags, struct sockaddr * __restrict from, socklen_t * __restrict fromLen)
sockfd表示准备接收的套接字
buff指向缓冲区的指针,用来接收数据
nbytes表示缓冲区的长度
flags表示调用方式,一般填0
__restrict from是一个指向地址结构体的指针,用来接收发送数据方的地址信息
__restrict fromLen 前面结构体长度指针
返回值:如果正确接收返回接收到的字节数,失败返回-1.
10.sendto函数
函数:ssize_t sendto(int, const void *, size_t, int, const struct sockaddr *, socklen_t)
功能:向指定的目的地方发送数据
解释:ssize_t sendto(int sockfd, const void * buff, size_t nbytes, int flags, const struct sockaddr * to, socklen_t tolen)
sockfd表示准备发送数据的套接字
buff指向缓冲区的指针,该缓冲区包含将要发送的数据
nbytes缓冲区数据长度
flags表示调用方式,一般填0
to指向目标套接字地址的结构体指针
tolen前面结构体长度
成功则返回实际传送出去的字符数,失败返回-1,错误原因存于errno 中
辅助函数
1.htons() 函数 和 ntohs()函数
函数:u_short htons(u_short hostshort) 功能:将一个16位的无符号端整形数据由主机排列方式转换成网络排列方式,所谓的网络排列方式就是大端排列方式,MacOS是采用小端的存储方式存储数据
使用地方:在有关主机地址和端口号结构体中struct sockaddr_in里面,属性in_port_t sin_port表示端口号,因为端口号要用网络排列方式,使用该函数转换后赋值
函数: u_short ntohs(u_short netshort) 功能: 与htons()功能相反,将16位无符网络排列端口转换成主机排列方式,也就是将16位大端排列数字转换成小端排列方式
使用地方: 得到地址结构体struct sockaddr_in,将里面的in_port_t sin_port转换成我们平时看到的小端排列的端口号
2.htonl() 函数 和 ntohl()函数
函数:u_long htonl(u_long hostlong)
功能:将一个32位无符号整形由主机排列方式转换成网络排列方式,所谓的网络排列方式就是大端排列方式
使用地方:在有关主机地址和端口号结构体中struct sockaddr_in里面,结构体属性struct in_addr sin_addr中的in_addr_t s_addr属性表示IP地址信息,因为IP地址信息要用网络排列方式,使用该函数转换后赋值
函数:u_long ntohl(u_long netlong)
功能:与函数 htonl()功能相反,将网络排列的32位无符数据转换成主机排列,就是将32位大端排列数字转换成小段排列数据
使用地方:得到地址结构体struct sockaddr_in,将里面的in_addr_t s_addr转换成我们平时看到的小端排列32位IP地址
3.inet_addr 函数
函数:in_addr_t inet_addr(const char *)
功能:将存储IP地址的char字符串转换成网络排列方式的32位无符号整形,跟上面htonl()函数功能一样
使用地方:转换的结果可直接用来给地址信息结构体里面的IP地址赋值,因为转换出来的结果是网络排列的
4.inet_aton 函数
函数:int inet_aton(const char *, struct in_addr *);
功能:与函数inet_addr功能一样,将char字符串IP地址转换成网络排序的无符整形,传入struct in_addr结构体指针,直接赋给结构体
5.inet_pton 函数
函数:int inet_pton(int, const char *, void *);
功能:与辅助函数htonl()、inet_addr()、inet_aton()的功能一样,将char字符串IP地址转换成网络排序的无符整形,直接赋给struct in_addr结构体指针里面,不一样的是可以根据地址族的不同转换IPv6还是IPv4的地址
6.inet_ntoa 函数
函数:char *inet_ntoa(struct in_addr)
功能:正好与上面的函数inet_aton功能相反,需要传入一个关于地址信息的结构体,解析出来C字符串的IP地址
7.inet_ntop 函数
函数:const char *inet_ntop(int, const void *, char *, socklen_t);
功能:跟上面函数inet_ntoa()功能相似,于函数inet_pton()功能相反,不一样的是他可以传入地址族,传入AF_INET则解析出IPv4的地址,传入AF_INET6则解析
8.getpeername 函数
函数:int getpeername(int, struct sockaddr * __restrict, socklen_t * __restrict) 功能:获取socket套接字对方的地址信息,返回0时正常,否则错误
解释:int getpeername(int sockfd, struct sockaddr * peerAddr, socklen_t * addrLen)
sockfd:表示套接字
peerAddr:接收地址信息结构体,传入结构体指针外面申明里面赋值,获取到的是连接对象的地址信息
addrLen:地址信息结构体的长度指针
返回值:返回0时可以通过地址信息指针取里面的信息,<0时错误
8.getsockname 函数
函数:int getsockname(int, struct sockaddr * __restrict, socklen_t * __restrict) 功能:获取socket套接字的地址信息,返回0时正常,否则错误
解释:int getsockname(int sockfd, struct sockaddr * addr, socklen_t *addrLen)
sockfd:表示套接字
addr:接收地址信息结构体,传入结构体指针外面申明里面赋值,获取到的是sockfd的地址信息
addrLen:地址信息结构体的长度指针
返回值:返回0时可以通过地址信息指针取里面的信息,<0时错误