Socket(套接字)
socket可以看成是用户进程与内核网络协议栈的编程接口(API函数)。
socket不仅可以用于本机的进程间通信,还可以用于网络上不同主机的进程间通信。
IPv4套接字地址结构
IPv4套接字地址结构通常也称为“网际套接字地址结构”,它以“sockaddr_in”命名,
定义在头文件netinet/in.h中。(可以在Linux上用命令man 7 ip查看其结构)
struct sockaddr_in {
uint8_t sin_len;
sa_family_t sin_family;
in_port_t sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};
sin_len:整个sockaddr_in结构体的长度,在4.3BSD-Reno版本之前的第一个成员是sin_family
sin_family:指定该地址家族,对于IPv4来说必须设为AF_INET(Socket不仅可以用于TCP/IP还可以用于UNIX域协议)
sin_port:端口
sin_addr:IP地址
sin_zero:暂不使用,一般将其设置为0
通用套接字地址结构
通用地址结构用来指定与套接字关联的地址(可以支持其他协议)。从应用程序开发人员的观点看,它的唯一用途就是对指向特定于协议的套接字地址结构的指针执行强制转换。
struct sockaddr {
uint8_t sin_len;
sa_family_t sin_family;
char sa_data[14];
};
sin_len:整个sockaddr结构体的长度
sin_family:指定该地址家族
sa_data:由sin_family决定它的形式
网络字节序
大端字节序:
最高有效位(MSB:Most Significant Bit)存储于最低内存地址处,最低有效位(LSB:Lowest Significant Bit)存储于最高内存地址处。
小端字节序:
最高有效位(MSB:Most Significant Bit)存储于最高内存地址处,最低有效位(LSB:Lowest Significant Bit)存储于最低内存地址处。
(这两种字节序之间没有标准可言,两种格式都有系统使用)
主机字节序:
不同的主机有不同的字节序,如x86为小端字节序,Motorola 6800为大端字节序,ARM字节序是可配置的。
网络字节序:
网络字节序规定为大端字节序。
//测试我们的电脑主机字节序是大端字节序还是小端字节序
#include<stdio.h>
#include<arpa/inet.h>
int main(void)
{
unsigned int x = 0x12345678;
unsigned char *p = (unsigned char *)&x;
printf("%x %x %x %x\n", p[0], p[1], p[2], p[3]);//若本机是小端字节序则输出78 56 34 12
unsigned int y = htonl(x);//调用htonl()转换为网络字节序
p = (unsigned char *)&y;
printf("%x %x %x %x\n", p[0], p[1], p[2], p[3]);//网络字节序规定为大端字节序,所以输出12 34 56 78
return 0;
}
为使网络程序具有可移植性,使同样的代码在大端和小端计算机上编译后都能正常运行,可调用以下库函数做网络字节序和主机字节序的转换。
//h表示host,n表示network,l表示32位长整数,s表示16位短整数
#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
地址转换函数(用于IP地址转换)
地址转换函数用于点分十进制数串(如”206.168.112.96”)和网络字节序的二进制值(这是存放在套接字地址结构中的值)之间转换网际地址。
#include <netinet/in.h>
#include <arpa/inet.h>
int inet_aton(const char *cp, struct in_addr *inp);
in_addr_t inet_addr(const char *cp);
char *inet_ntoa(struct in_addr in);
//实战
//该程序输出结果: add=3232235620 192.168.0.100
#include<stdio.h>
#include<arpa/inet.h>
int main(void)
{
unsigned int addr = inet_addr("192.168.0.100"); //转换后是网络字节序(大端)
printf("add=%u\n", ntohl(addr));//在打印之前需将addr转化为主机字节序
struct in_addr ipaddr;
ipaddr.s_addr = addr;
printf("%s\n", inet_ntoa(ipaddr));
return 0;
}
套接字类型
1)流式套接字(SOCK_STREAM)
提供面向连接的、可靠的数据传输服务,数据无差错,无重复的发送,且按发送顺序接收,对应TCP协议。
2)数据报式套接字(SOCK_DGRAM)
提供无连接服务。不提供无错保证,数据可能丢失或重复,并且接收顺序混乱, 对应UDP协议。
3)原始套接字(SOCK_RAW)
使我们可以跨越传输层直接对IP层进行封装传输。(应用层直接和IP层)