linux网络编程笔记

基础知识

       socket是一种IPC方法,它允许位于同一主机(计算机)或使用网络(IPV4或IPV6)连接起来的不同主机上的应用程序之间交换数据。P945

       socket存在于一个通信domain中,现代操作系统至少支持下列domain:

              UNIXdomain(即AF_UNIX):允许同一主机上的应用程序间通信;

              Ipv4domain (即AF_INET):允许通过使用IPV4连接起来的主机上的应用程序通信;

              Ipv6domain (即AF_INET6):允许通过使用IPV6连接起来的主机上的应用程序通信;P946

 

它确定以下信息:

Domain

执行的通信

应用程序间的通信

地址格式

地址结构

AF_UNIX

(即UNIX domain)

通过内核

 

同一主机

 

路径名

 

Struct sockaddr_un

AF_INET

通过IPv4

 

通过Ipv4网络连接起来的主机

32位IPV4地址+16位端口号

Struct sockaddr_in

AF_INET6

通过IPV6

通过IPV6连接起来的主机

128位IPV6地址+16位端口号

Struct sockaddr_in6

       socket有两种类型:流socket和数据报socket,这两种类型在以上三种domain中都得到了支持。P946

  TCP/IP

联网协议是指定义如何在网络上传输信息的一组规则。(P969)

  TCP/IP协议套件是使用最广泛的一种联网协议套件。P968

  之所以成为套件,是因为TCP/IP协议套件分为很多层。

  分为多层,优点是每一个协议层都对上一层隐藏下层的复杂性。

  如果应用程序使用流socket类型,应用层通过TCP协议和网络层通信(即将数据打包成TCP报头+TCP数据的形式,发送给网络层);如果使用了数据报socket类型,应用层通过UDP协议与网络层通信(即将数据打包成UDP报头+UDP数据的形式,发送给网络层);如果使用了裸SOCKET类型,直接与网络层通信。P970

  裸SOCKET 即SOCK_RAW,P972,p970

 

       传输层协议(TCP或者UDP)的任务是向位于不同主机(或者同一主机)上的应用程序提供端到端的通信服务。

       同一台主机上,一个IP可以绑定多个应用程序(比如一台主机可能就一个IP),因此仅靠IP无法确定是目的地是哪个应用程序,因此需要一个16位的端口号对这些程序区分。P975

       SOCKET在IPV4、IPV6连接起来的主机上的应用程序间通信时,给connect函数的地址参数为IP地址和端口号,此处的IP地址为远端主机IP,端口号为远端主机上应用程序的端口号。

       服务:有些众所周知的端口号已经被永久的分配给特定的应用程序了,这些应用程序叫做服务。windows中计算机管理的中的服务应该就是这里所谓的服务。P976

 

绑定IP和端口号

       两台通过网络(ipv4或ipv6)连接起来的主机通信时,服务器端的程序需要将socket绑定一个地址,32位IP地址+16位端口号(IPV4domain),或者128位IP地址+16位端口号(IPV6domain),客户端也需要这个IP地址和端口号(与主机相同)。客户端上该地址信息需要填写在structsockaddr_in或structsockaddr_in6中。如下结构,sin_port填写16位端口号,sin_addr填写32位IP地址。

 

struct sockaddr_in{                      /*Ipv4 socket address*/

       sa_family_t   sin_family;        /*address family (AF_INET)*/

       in_port_t     sin_port;               /*portnumber*/

       structin_addr  sin_addr;       /*ipv4 address*/

       unsignedchar  _pad[X];       /*pad*(填满) tosize of  struct sockaddr,sockaddr=16bytes,因此X=16-2-1-4=9*/

}; (P986)

struct in_addr{

       in_addr_t      s_addr;       /*unisgned 32-bit integer,32位的整型数*/

};

       定义一个变量struct sockaddr_in addr,那么接下来就要给struct sockaddr_in赋值了,很明显sin_family=AF_INET,那么addr.sin_addr.s_addr应该赋予什么值呢?               

addr.sin_addr.s_addr=192.168.0.1?肯定不行,因为192.168.0.1称为点分十进制,只是ip地址的一种表示方法,不属于任何数据类型。addr.sin_addr.s_addr=”192.168.0.1”?也不行,因为定义中,s_addr是一个32位整型。

       只能把一个32位整型IP地址赋给s_addr。。如,想绑定IP:192.168.0.1,其对应二进制1100000010101000 00000000 00000001,对应十六进制0XC0A80001,那么这样赋值:

                            addr.sin_addr.s_addr=0XC0A80001;

可以吗?接近了,但还是不行。

       根据数据存储的顺序——最小地址处先存最高有效位还是最低有效位,可以分为大端存储和小端存储,先存储最高有效的是大端,先存储最低有效位的是小端,。由于该结构体中数据都是网络字节序(大端),而主机是小端的,因此必须把数据转化为网络字节序,再赋值给结构体。把该IP地址0XC0A80001用htonl()(P983)转换成网络字节序,赋给sin_addr,把该端口号5000用htons()(P983)转换为网络字节序,赋给sin_port。

       将sockaddr_in结构体赋值完后,发现一个问题,bind()函数中第二个参数类型为struct sockaddr *,而非struct sockaddr_in *,那该怎么办呢?

       对比struct sockaddr和struct sockaddr_in的定义,发现两者是并列结构,即字节数相同(自己的理解,structsockaddr_in中pad[x]的作用就是使两个结构体字节数相等)。因此,可以使用强制转换:

              structsockaddr_in *addr;

bind(sockfd,(struct sockaddr *)addr, sizeof(struct sockaddr))

      

struct sockaddr{                      /*通用SOCKET地址结构*/

    sa_family_t  sa_family;     /* address family :AF_INET or AF_INET6 or AF_UNIX*/

    char        sa_data[14];   

};  ( P949 )

 

上文中,将IP:192.168.0.1赋给struct sockaddr_in中的sin_addr,要先由点分十进制表示得到其16进制数据,再将该数据用htonl()函数转化为网络字节序,赋给sin_addr。这个过程未免繁琐。其实Linux提供了一个函数可以一步到位,直接将192.168.0.1转化为网络字节序十六进制,并且赋给sin_addr,这个函数就是inet_pton()(P989)。如下,

    struct sockaddr_in addr;

       inet_pton(AF_INET,”192.168.0.1”,&(addr.sin_addr),INET_ADDRSTRLEN);

//INET_ADDRSTRLEN=16,在<netinet/in.h>中

       该函数名中,p表示展现presentation,n表示network。展现即为点分十进制表示192.168.0.1。

 

由主机名获取IP地址,由服务名获取端口号

       主机名是连接在网络上的一个系统(可能拥有多个IP,即有多个网口)的符号标识符,服务名是端口的符号表示。在Linux系统中,主机名与IP的对应关系在/etc/hosts文件中,服务名与端口号的对应关系在/etc/services文件中。(992,994)

       上文中,我们直接指定想要绑定的IP地址,还有一中获取IP地址的方法,由主机名获取。这就要用到getaddrinfo()函数(P996)。该函数示例如下:

       structaddrinfo *hints,*result;

       getaddrinfo(“localhost.locaodomain”,”smtp”,&hints,&result);

其中,localhost.locaodomain是在/etc/hosts文件中查到的主机名,smtp是在/etc/services中查的主机名。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值