Unix网络编程学习笔记之第3章 套接字编程简介

来源:互联网 发布:java基础测试题及答案 编辑:程序博客网 时间:2024/05/16 12:43

1. 首先我们说一个IPv4的套接字地址结构:

struct in_addr {       in_addr_t s_addr;//!};struct sockaddr_in{       uint8_t sin_len;       sa_family_t sin_family;//!       int_port_t sin_port;//!       structin_addr sin_addr;//!       charsin_zero(8);};

我们关心的只有加!的几个部分,

Sin_family: 是一个无符号整型(unsignedshort) 。用来说明本地址的地址族。IPv4都有同一个地址族AF_INET。

Sin_port:是一个16位的整数。用来说明套接字的端口号

Sin_addr: 是一个结构体对象,32为的整数。用来说明套接字的IP地址。

注意

(1) 假设定义一个套接字地址结构的对象servaddr。Servaddr.sin_family是一个结构体类型的IP地址,servaddr.sin_family.s_addr是一个32位的整型。虽然都表示同一个IP地址,但是其结构是不一样的,在编程时注意,特别是传递参数时,传结构体和传整型是不一样的参数传输机制。

(2) 这里注意IP地址和端口号都是网络字节(大端存储)存储的。所以编程时,注意和自己主机的字节序转换。

 

2. 我们来看一下通用的套接字地址结构

struct sockaddr{       uint8_t sa_len;       sa_family_t sin_family;       charsa_data(4);};

我们不关心其具体都是什么,我们关心的是其表示通用的套接字地址结构,也就是说无论是IPv4、IPv6的套接字地址结构都可以转换为此通用类型。

这也就意味着,我们的socket函数,如bind,connect,accept等和套接字地址关联的函数的参数都是通用套接字结构。

这也解释了为何我们在写bind等函数时都是这样的:

bind(listenfd,(structsockaddr*) &servaddr, sizeof(servaddr));

这里我们把IPv4的套接字地址结构转换为通用的地址结构。

 

3. 套接字函数传递参数

当我们使用和套接字地址相关的函数时,我们传递的是该套接字的地址。

Unix有两种方式传递套接字地址:从进行传递到内核。从内核传递到进程。

(1) 从进程传递到内核时:

bind,connect,sendto

我们告知内核套接字地址的内容。所以其三个函数的最后一个参数是一个整数值传递:告知内核其套接字地址的长度。

如:

bind(listenfd,(struct sockaddr*) &servaddr, sizeof(servaddr));

(2) 从内核到进程

accept,recvfrom,getsockname, getpeername

这些函数的目的是从内核中得到套接字地址,所以这些函数最后一个参数是整数的地址,即用来让内核修改,让进程知道该得到的套接字地址的长度是多少。

如:

connfd=accept(listenfd, (struct sockaddr*) NULL, NULL);


4. 字节排序函数

大端和小端:起始地址存储的是低序字节为小端,起始地址存储的高序字节为大端。

首先弄明白什么是高序字节和低序字节?

二进制中,低序字节就是个位开始的,高序字节就是最高位开始的字节。

例如:short类型的整数i,2个字节,其十六进制表示为0x0102

如果是小端:02(低序字节)存储在i地址的起始位置。

如果是大端:01(高序字节)存储在i地址的起始位置。

所以判断一台机器是大端还是小端很简单的方法:

bool IsBigEnd(){       union{              shorts;              charc;       }un;       un.s = 0x0102;       if(un.c== 1)//此时c是s的起始地址字节,如果高序字节01到起始字节,就是大端。              returntrue;       return false;}

(1). 前面提到套接字地址中的字节序都是网络字节序,而网络字节序规定是大端字节序。我们如果想要编写与机器无关的程序就要用到字节序函数

 

(2). 各种字节序函数

主机字节序->网络字节序函数

uint32_t htonl (uint32_t addr);   uint16_t htons (uint16_t addr);   

h:host  n:net    l :long    s :short

所以很好理解。IP地址使用上面的,port使用下面的。


网络字节序->主机字节序函数

uint32_t ntohl (uint32_t addr);   uint16_t ntohs (uint16_t addr);   

n:  net   h : host  l :long  s  :short

和上面的类似。

注意:只有在设计多字节的数据(int short long等)时,才会有字节序的问题。如果一个程序都是单字节char,是没有什么字节序的问题的。

 

5. 字节操纵函数

void bzero(void * s, size_t nbytes)

该函数把s起始地址的nbytes字节长度内的地址置为0,一般我们用它来把一个套接字地址结构初始化0.

其中b意为byte的意思。

 

6. IP地址表达形式的转换函数

IP地址2种表示形式:表达式(pression)型:163.263.1.2.  二进制(numeric)型:01001010…

显然由于套接字地址结构中IP的表示方法是一个32位的整型,所以只能为其赋值二进制表示形式。

(1) 表达式型->二进制型

int inet_pton (int family, constchar* strp, void* addrp)

family指定地址族,strp为表达式型IP地址,经过函数处理,转换为二进制型结果存储在addrp中。如果转换成功返回1,否则失败。

(2) 二进制型->表达式型

const char * inet_ntop(intfamily, const void* strp, char* addrp , size_t len)

Strp为二进制型,转换为表达式型的结果放在addrp中。其中len指定数组addrp的大小。其大小一定要大于二进制型地址长度,否则失败。

如果转换成功,则返回addrp地址,如果失败,返回NULL。

 

注意:

1. 一般表达式型IP地址都是char*表示的。所以不会存在什么字节序的问题。

这也是为何前面的代码中,我们

if(inet_pton(AF_INET, argv[1],&servaddr.sin_addr)<=0)

只需要这一步就可以了,无需涉及字节序的问题。

这里我们看到我们IP地址使用的是结构体形式(servaddr.sin_addr)。

 

0 0
原创粉丝点击