套接字类型

创建套接字

  • 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);

协议设置

地址族

地址信息的表示

  1. 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

  1. 基于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地址
}
  1. 结构体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字符串信息转换为网络字节序

  1. inet_addr函数
    将点分十进制表示法(例如211.214.107.99)的IP转换为32位整型数据(满足网络字节序),同时科研检测无效的IP地址:
#include <arpa/inet.h>
in_addr_t inet_addr(const char *string);
//成功时返回32位大端序整型值,失败时返回INADDR_NONE
  1. 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;
}