对于多字节数据在内存中有两种存储方式:
Little-endian:低字节在前,高字节在后;
Big-endian:高字节在前,低字节在后
网络协议在处理多字节整数时,采用的是高端字节序,在编程时一定要考虑主机字节顺序与网络字节顺序的相互转换。
1 socket定义
socket是一种文件描述符。常用的socket类型有两种:流式socket(SOCK_STREAM)和数据报式socket(SOCK_DGRAM)。流式是一种面向连接的socket,针对于面向连接的TCP服务应用;数据报式socket是一种无连接的socket,对应于无连接的UDP服务应用。
1.1 socket地址信息
struct sockaddr类型是用来保存socket信息的:
structsockaddr
{
unsigned short sa_family; /* 地址协议族,一般为AF_INET*/
char sa_data[14]; /* 14字节的协议地址,包含该socket的IP地址和端口号 */
};
另外还有一种结构类型sockaddr_in:
structsockaddr_in
{
short int sin_family; /* 地址族 */
unsigned short int sin_port; /* 网络字节顺序的端口号*/
struct in_addr sin_addr; /* IP地址 */
unsigned char sin_zero[8]; /* 填充0 以保持与结构sockaddr同样大小 */
};
sockaddr_in结构使用更为方便,sockaddr_in.sin_zero应该用bzero( )或memset( )将其置为0。sockaddr_in的指针和sockaddr的指针可以通用。
structin_addr
{
unsigned long s_addr; /* 网络字节顺序的IP地址*/
};
可以将s_addr指定为htonl( INADDR_ANY ),表示的是由系统自己指定IP地址。
在选择端口sin_port时,必须特别小心:
1.0~1023由IANA控制,是为固定服务保留的;
2.1024~49151是IANA列出来的、已注册的端口,供普通用户的普通用户进程或程序使用;
3.49152~65535是动态或私用端口,IANA这些端口上没有注册服务,可自由使用。
1.2 地址格式转换函数
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet>
intinet_aton( const char *cp, struct in_addr *inp ) // 将点式IP转换成网络字节顺序的IP
char* inet_ntoa( struct in_addr in ) // 将网络字节顺序的IP转换成点式IP
1.3 网络字节顺序转换函数
#include<netinet/in.h>
主机字节顺序---->网络字节顺序:
uint16_t htons(uint16_t hostshort);
uint32_t htonl(uint32_t hostlong);
网络字节顺序---->主机字节顺序:
uint16_t ntohs(uint16_t netshort);
uint32_t ntohl(uint32_t netlong);
1.4 名字解析
structhostent
{
char * h_name; //主机名
char ** h_aliases; //主机别名数组
int h_addrtype; //主机地址类型
int h_length; //主机地址长度(字节)
char ** h_addr_list; //主机地址数组,其中的地址是网络字节顺序。
}
#include<netdb.h>
structhostent *gethostbyname( const char *name )
参数name可以是主机名,也可以是点式IP地址。
#include<sys/socket>
structhostent *gethostbyaddr( const char *addr, int len, int type )
参数addr是指向主机地址的指针,该主机地址按照网络字节顺序。
参数len指定参数addr的字节长度。
参数type为AF_INET。
2 socket的使用
2.1 socket的创建
structprotoent
{
char * p_name; //协议名
char ** p_aliases; //协议别名数组
int p_proto; //协议号
}
#include<netdb.h>
structprotoent *getprotoent( void )
#include<sys/types.h>
#include<sys/socket.h>
intsocket( int domain, int type, int protocol )
参数domain可以是PF_INET,表示IPv4地址家族;PF_INET6,表示IPv6地址家族。
参数type可以取SOCK_STREAM、SOCK_DGRAM或SOCK_RAW。
参数protocol可以是getprotoent()取回的任何一个协议号,一般取0表示IP。
2.2 面向连接的socket
2.2.1 服务器端函数
2.2.1.1 bind( )
#include<sys/types.h>
#include<sys/socket.h>
intbind( int sockfd,struct sockaddr *my_addr,intaddrlen )
2.2.1.2 listen( )
#include<sys/types.h>
#include<sys/socket.h>
intlisten( int sockfd,int backlog )
backlog指定在请求队列中允许的最大请求数,连接请求将在队列中等待accept( )它们。
2.2.1.3 accept( )
#include<sys/types.h>
#include<sys/socket.h>
intaccept(int sockfd,void *addr,int*addrlen)
addr通常是结构sockaddr_in的指针,用来存放发出连接请求的主机的信息。
addrten作为输入通常指定为sizeof(