网络编程,从某种程度上说就是进程间通信,套接口类似于进程的ID,通过IP地址指定通信的主机,通过端口号指定和主机上的那个进程通信,也就是通过IP+端口号唯一确定了通信的进程。
结构:
struct sockaddr{
uint8_t sa_len;//数据长度
sa_family sa_family;//协议名
char sa_data[14];//14位的协议地址
}
头文件:sys/socket.h
说明:通用套接字接口结构
数据类型 |
说明 |
头文件 |
int8_t |
带符号的8位整数 |
sys/types.h |
uint8_t |
无符号的8位整数 |
sys/types.h |
int16_t |
带符号的16位整数 |
sys/types.h |
uint16_t |
无符号的16位整数 |
sys/types.h |
int32_t |
带符号的32位整数 |
sys/types.h |
uint32_t |
无符号的32位整数 |
sys/types.h |
sa_family_t |
套接口地址结构的地址簇 |
sys/socket.h |
socklen_t |
套接口地址的长度,一般为uint32_t |
sys/socket.h |
in_port_t |
TCP或UDP端口,一般为uint16_t |
netinet/in.h |
in_addr_t |
Ipv4地址,一般为uint32_t |
netinet/in.h |
结构:
struct sockaddr_in {
short int sin_family; //IPV4协议为AF_INET
unsigned short int sin_port;//16位端口号,网络字节序列
struct in_addr sin_addr; //
unsigned char sin_zero[8]; //备用域,为了和struct sockaddr字
//节数保持相同;
};
struct in_addr{
in_addr_t s_addr;//32位IP地址,网络字节序列
} ;
头文件:neitnet/in.h
说明:略
计算机内存中有两种数据存储方式,一种为小端字节序,也就是低地址存储数据低字节,高地址存储数据高字节;一种为大端字节序,也就是低地址存储高字节,高地址存储低字节。网络字节序采用大端字节序,而主机字节序有可能为小端字节序,因此,存在着字节序的转换问题。
函数:
uint16_t htons(uint16_t hostvalue);
uint32_t htonl(uint32_t hostvalue);
uint16_t ntohs(uint16_t netvalue);
uint32_t ntohl(uint32_t netvalue);
头文件:netinet/in.h
说明:函数中h表示host,n表示network,s表示short,l表示long,一般而言,使用htonshentohs转换端口号,htonl和ntohl转换IP地址。
字节操作函数主要是用于读取结构体中的某几个字节。
函数:
void bzero(void *dest,size_t nbytes);
void bcopy(const void *src,void *dest,size_t nbytes);
int bcmp(const void *prt1,const void *ptr2,size_t nbytes);
void *memset(void *dest,int c,size_t len);
void *memcpy(void *dest,const void *src,size_t nbytes);
int memcmp(const void *ptr1,const void ptr2,size_t nbytes);
头文件:string.h
说明:以b打头的函数为支持套接口函数的系统所提供,mem为支持ANSI C库提供的函数;其中,bzero将制定的起始地址设置为0(nbytes表示字长),bcopy和memcpy为复制,bcmp和memcmp为比较,memset将目标中指定数据的字节设置为指定的值。
在TCP/IP的网络上,使用ASCII地址,也就是使用“.”隔开的4个十进制数表示,但在套接字中使用32位网络字节序,因此必须进行地址转换。
函数: in_addr_t inet_addr(const char *straddr);
int inet_aton(const char* straddr,struct in_addr *addrp);
char* inet_ntoa(struct in_addr inaddr);
头文件:sys/socket.h netinet/in.h arpa/inet.h
说明:
inet_addr成功返回32位网络字节序地址,出错返回INADDR_NONE。INADDR_NONE为linux定义的一个常数,是一个不存在的ip地址;
inet_aton将ASCII转换成网络字节序的32位二进制值,输入的ASCII放在straddr中,转换后放在addrp中,成功返回1,失败返回0;
inet_ntoa将32位二进制地址转换成ASCII地址,成功返回ASCII值,失败返回NULL。
仍然为read和write函数,和文件I/O不同在于其读取的字节数可能比要求的少,但不是读取错误。
函数:int socket(int family,int type,int protocol);
头文件:sys/socket.h
说明:创建一个套接口描述字,也就是套接字,返回值大于等于0,表示成功,否则表示失败,protocol一般为0,其中family的取值如下:
AF_LOCAL UNIX协议簇 AF_ROUTE 路由套接口
AF_INET IPv4协议 AF_INET6 IPv6协议
AF_KEY 密钥套接口
type的取值如下:
SOCK_STREAM TCP套接口 SOCK_DGRAM UDP套接口
SOCK_PACKET 支持数据链路访问 SOCK_RAW 原始套接口
函数:int bind(int sockd,const struct sockaddr *myaddr,socklen_t addrlen)
头文件:sys/socket.h
说明:成功返回值为0,失败返回-1,并设置errno值。该函数的调用不是必须的,只有需要和固定的某个特定网络地址和端口通信时才会使用到,一般而言调用connect和listen函数时,linux内核会自动分配一个地址和端口号。该函数的调用,对于服务器端而言,myaddr必须为从该地址接受客户信息的地址,对于客户端而言,myaddr指定了套接口的源地址。对于端口号,可以任意指定,但是不能为系统保留端口。此外,bind允许内核指定地址,当套接口设定port为0时,表示有内核指定端口号,设置sin_addr为INADDR_ANY时,有内核指定IP地址,
函数:int connect(int sockfd,const struct sockaddr *myaddr,socklen_t addrlen)
头文件:sys/socket.h sys/types.h
说明:链接成功返回0,失败返回-1,并设置errno的值
函数:(1)int listen(int sockfd,int backlog);
(2)int accept(int sockfd,const struct sockaddr *myaddr,socklen_t *addrlen)
头文件:sys/socket.h
说明:
listen用来监听,其中backlog表示内核为此套接口设定的接受客户端请求的最大连接数。成功返回0,失败返回-1,并设置errno的值。
accept主要用来处理各个连接,其功能为从已经完成的来你接队列的队头返回下一个已完成连接,如果已完成连接队列为空,则进程休眠。返回非负描述字表示成功,出错返回-1。成功时,返回值用来标识新建立的链接。其与参数sockfd的区别在于,监听套接口描述字只有一个,而且一直存在,而函数返回值表示已连接的套接口描述字,当连接断开时就关闭该描述字,如果不需要客户端的地址和端口信息,可以将第二个和第三个参数设置为空指针。
函数:
(1) getsockname(int sockfd,struct sockaddr *localaddr,socklen_t *addrlen);
(2) getpeername(int sockfd,struct sockaddr *peeraddr,socklen_t *addrlen);
头文件:sys/socket.h
说明:(1)返回本地协议地址,(2)返回远程协议地址
当不调用bind或调用bind没有指定本地IP地址和端口时,可以调用函数getsockname来返回内核分配的本地IP地址和端口号,还可以获取到套接口的协议簇。当一个新的连接建立时,服务器端也可以调用getsockname来获取分配给此连接的本地IP地址;当服务器端的一个子进程调用函数exec启动执行时,只能调用getpeername来获取客户端IP地址和端口号。
函数:struct hostent *gethostbyname(const char *hastname);
头文件:netdb.h
说明:函数返回成功返回hostent指针,失败返回空指针,并设置h_errno的值。其中hostent的结构如下:
struct hostent{
char *h_name;//主机的规范化名字;
char **h_aliases;//足迹的别名列表;
char h_addrtype;//返回主机地址类型(ipv4,ipv6)
char h_length;//返回地址长度(以字节为单位)
char **h_addr_list;// 主机的一组网络地址列表,使用网络字节序
};
函数:struct hostent *gethostbyaddr(const void *addr,socklet_t len,int type);
头文件:netdb.h
说明:addr为一个含有地址结构(in_addr或in_addr6)的指针,len为该结构的大小,IPV4为4,IPV6为6,type为协议簇。
获取当前主机名字
函数:uname(struct utsname *name);
头文件:sys/utsname.h
说明:返回当前主机的名字,常和gethostbyname一起使用来确定本地主机的IP地址。返回值大于等于0为成功,如果为-1表示失败。Struct ustname结构如下:
struct utsname{
char sysname[];
char nodename[];
char release[];
char version[];
char machine[];
#ifdef _GUN_SOURCE
char domainname[];
#endif
};
函数:struct servent *getservbyname(const char *name,const char *proto);
头文件:netdb.h
说明:实现将服务器名转换成端口号的功能,返回非空指针表示成功,否则为失败,serv为服务器名,proto为协议名,可以为空,其中servent定义如下:
struct servent{
char *s_name;//规范服务器名
char *s_aliases;//别名成员列表
int s_port;//端口号
char *s_proto;//函数调用时指定的协议名
}
函数:struct servant *getservbyport(int port,const char* proto);
头文件:netdb.h
说明:实现从端口号到服务器名的转换,返回结果为空指针表示成功,否则失败,port为网络字节序,proto为协议名。