套接字类型
创建套接字
- Linux
#include <sys/socket.h>
int socket(int domain,int type,int protocol);
//成功时返回文件描述符,失败时返回-1
domain:套接字中使用的协议族信息。
type:套接字数据传输类型信息。
protocol:计算机间通信中使用的协议信息。
- Windows
#include <winsock2.h>
int socket(int af,int type,int protocol);
//成功时返回socket句柄,失败时返回INVALID_SOCKET(本质为-1)
协议族(domain)
头文件sys/socket.h中声明的协议族
名称 | 协议族 |
---|---|
PF_INET | IPv4互联网协议族 |
PF_INET6 | IPv6互联网协议族 |
PF_LOCAL | 本地通信的UNIX协议族 |
PF_PACKET | 底层套接字的协议族 |
PF_IPX | IPX Novell协议族 |
套接字类型(type)
- 套接字类型1:面向连接的套接字(SOCK_STREAM)
特点:可靠的、按序传递的、基于字节的面向连接的数据传输方式的套接字。 - 套接字类型2:面向消息的套接字(SOCK_DGRAM)
特点:不可靠的、不按序传递的、以数据的高速传输为目的套接字。
协议的最终选择(protocol)
明确表示哪种协议:
int tcp_socket = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP);
int udp_socket = socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP);
协议设置
地址族
地址信息的表示
- POSIX中定义的数据类型
数据类型名称 | 数据类型说明 | 声明的头文件 |
---|---|---|
int8_t | signed 8-bit int | sys/types.h |
uint8_t | unsigned 8-bit int(unsigned char) | |
int16_t | signed 16-bit int | |
uint16_t | unsigned 16-bit int(unsigned short) | |
int32_t | signed 32-bit int | |
uint32_t | unsigned 32-bit int(unsigned long) | |
sa_family_t | 地址族(address family) | sys/socket.h |
socklen_t | 长度(length of struct) | |
int addr_t | IP地址,声明为unit32_t | netinet/in.h |
int port_t | 端口号,声明为unit16_t |
- 基于IPv4地址的结构体
struct sockaddr_in
{
sa_family_t sin_family; //地址族(Address Family)
uint16_t sin_port; //16位TCP/UDP端口号
struct in_addr sin_addr; //32位IP地址
char sin_zero[8]; //不使用
}
struct in_addr
{
in_addr_t s_addr; //32位IPv4地址
}
- 结构体sockaddr_in的成员属性
成员sin_family:
地址族(Address Family) | 含义 |
---|---|
AF_INET | IPv4网络协议中的使用的地址族 |
AF_INET6 | IPv6网络协议中的使用的地址族 |
AF_LOCAL | 本地通信中采用的UNIX协议的地址族 |
成员sin_port:端口号
成员sin_addr:IP地址
成员sin_zero:无特殊含义,只是为了使结构体socketaddr_in的大小与socketaddr结构体保持一致而插入的成员,必需填充为0,否则无法得到想要的结果。
数据序列
CPU向内存保存数据的方式有2种,这意味这CPU解析数据的方式也分为2种。
- 大端序(Big Endian):高位字节存放到低位地址。
- 小端序(Little Endian):高位字节存放到高位地址。
为了保证网络传输的一致性,在通过网络传输数据时约定统一方式,这种约定称为网络字节序,统一约定位大端序
。即先把数据数组转化位大端序格式再进行网络传输。
注1:Intel和AMD系列的CPU都采用小端序标准。
注2:除了向socketaddr_in结构体变量填充数据外,其他情况都无需考虑字节序问题(这个过程时自动的)。
字节序转换
转换函数(两个平台都可以使用):
- unsigned short htons(unsigned short):把short类型数据从主机字节序转化为网络字节序。
- unsigned short ntohs(unsigned short):把short类型数据从网络字节序转化为主机字节序。
- unsigned long htonl(unsigned long):把long类型数据从主机字节序转化为网络字节序。
- unsigned long ntohl(unsigned long):把long类型数据从网络字节序转化为主机字节序。
函数名命名规则: - htons中的
h
代表主机(host)字节序 - htons中的
n
代表网络(network)字节序 - htons中的
s
代表short,其中htonl中的l
代表long(Linux中long类型占用4个字节)
IP字符串信息转换为网络字节序
- inet_addr函数
将点分十进制表示法(例如211.214.107.99)的IP转换为32位整型数据(满足网络字节序),同时科研检测无效的IP地址:
#include <arpa/inet.h>
in_addr_t inet_addr(const char *string);
//成功时返回32位大端序整型值,失败时返回INADDR_NONE
- inet_aton函数
该函数只存在于Linux之中,Windows中不存在
,与inet_addr函数功能相同,只不过该函数利用了in_addr结构体,且其使用频率更高。
#include <arpa/inet.h>
int inet_aton(const char *string,struct in_addr *addr);
//成功时返回1(true),失败时返回0(false)
参数string:含有需转换的IP地址信息的字符串地址值。
参数addr:将保存转换结果的in_addr结构体变量的地址值。
注意:实际编程中若要调用inet_addr函数,需将转换后的IP地址信息代入sockaddr_in结构体中声明的in_addr结构体变量。而inet_atong函数则不需要此过程。原因在于,若传递in_addr结构体变量地址值,函数会自动把结果填入该结构体变量中。
inet_aton(addr,&addr_inet.sin_addr);
网络字节序转换为IP字符串信息
#include <arpa/inet.h>
char *inet_ntoa(struct in_addr adr);
//成功时返回转换的字符串地址值,失败时返回-1。
需要注意的是,函数返回值类型位char指针,返回字符串地址意味着字符串已保存到内存空间,但该函数未向程序员要求分配内存,而是再内部申请了内存并保存了字符串。也就是说,调用完该函数后,应立即将字符串信息复制到其他内存空间。
char *str_ptr;
char str_arr[20];
str_ptr = inet_ntoa(addr1.sin_addr);
strcpy(str_arr,str_ptr);
实例
网络地址初始化:
struct sockaddr_in addr;
char *serv_ip = "211.217.168.13"; //IP字符串
char *serv_port = "9190"; //端口字符串
memset(&addr,0,sizeof(addr)); //结构体变量addr各成员初始化为0
addr.sin_family = AF_INET; //指定地址族
addr.sin_addr.s_addr = inet_addr(serv_ip); //IP初始化
addr.sin_port = htons(atoni(serv_port)); //端口初始化
注:atoi用于将数字字符串转换成int输出。
常量INADDR_ANY
可自动获取运行服务器端的计算机IP地址,不必亲自输入。
addr.sin_addr.s_addr = inet_addr(INADDR_ANY); //IP初始化
WSAStringToAddress & WSAAddressToString
这两个函数在功能上与inet_ntoa和inet_addr完全相同,优点是支持多种协议,缺点是降低兼容性。
WASAStringToAddress函数(字符串转地址):
#include <winsock2.h>
INT WSAStringToAddress(
LPTSTR AddressString,INT AddressFamily,LPWSAPROTOCOL_INFO lpProtocolInfo,LPSOCKADDR lpAddress,LPINT lpAddressLength
);
//成功时返回0,失败时返回SOCKET_ERROR
- AddressString:含有IP和端口号的字符串地址值。
- AddressFamily:第一个参数中地址所属的地址族信息。
- lpProtocolInfo:设置协议提供者(Provider),默认为NULL。
- lpAddress:保存地址信息的结构体变量地址值。
- lpAddressLength:第四个参数中传递的结构体长度所在的变量地址值。
WASAStringToAddress函数(字符串转地址):
#include <winsock2.h>
INT WSAAddressToString(
LPTSTR lpsaAddress,DWORD dwAddressLength,LPWSAPROTOCOL_INFO lpProtocolInfo,LPSTR lpszAddressString,LPDWORD lpdwAddressStringLength
);
//成功时返回0,失败时返回SOCKET_ERROR
- lpsaAddress:需要转换的地址信息结构体变量地址值。
- dwAddressLength:第一个参数中结构体的长度。
- lpProtocolInfo:设置协议提供者(Provider),默认为NULL。
- lpszAddressString:保存转换结果的字符串地址值。
- lpdwAddressStringLength:第四个参数中存有地址信息的字符串长度。
#undef UNICODE
#undef _UNICODE
#include <stdio.h>
#include <winsock2.h>
int main(int argc,char *argv[])
{
char *strAddr = '203.211.218.102:9190';
char strAddrBuf[50];
SOCKADDR_IN servAddr;
int size;
WSADATA wsaData;
WSAStartup(MAKEWORD(2,2,),&wsaData);
size = sizeof(servAddr);
WSAStringToAddress(
strAddr,AF_INET,NULL,(SOCKADDR*)&servAddr,&size
);
size = sizeof(strAddrBuf);
WSAAddressToString(
(SOCKADDR*)&servAddr,sizeof(servAddr),NULL,strAddrBuf,&size
);
printf("second conv result:%s\n",strAddrBuf);
WSACleanup();
return 0;
}