Fork me on GitHub

网络编程(二)


基本函数

本小结介绍Linux网络编程中常用到的基本函数,这些函数通常是为后续的套接口编程做准备工作的。读者在后面的程序实例中可以看到它们的应用。

  1. 字节排序函数

    数据在计算机里的存储方式可分为小端模式和大端模式两种。内存的低地址存储数据的低字节,高地址存储数据的高字节的方式称为小端模式,相反,称之为大端模式。

    在网络上有着许多类型的机器,这些机器表示数据的字节顺序使不同的,比如i386芯片采用的是小端模式,而alpha芯片却相反。网络协议必须指定一个网络字节序。

    端口号和IP地址都是以网络字节存储的,网络字节序采用的是大端模式。要把主机字节序和网络字节序对应起来,就要用到字节的排序函数。在Linux下有4个专门的字节排序函数。

    1
    2
    3
    #include<netinet/in.h>	// 返回的是网络字节序
    uint32_t htonl(uint32_t hostlong);
    uint16_t htons(uint16_t hostshort);
    1
    2
    3
    // 返回的是主机字节序
    uint32_t ntohl(uint32_t netlong);
    uint16_t ntohs(uint16_t netshort);
  2. 字节操纵函数

    在套接口编程中,经常需要一起操纵结构体中的某几个字节,这就要用到字节操纵函数了。

    1
    2
    3
    4
    5
    6
    7
    #include<string.h>
    void bzero(void *dest,size_t nbytes);
    void bcopy(const void *src,void *dest,size_t nbytes);
    int bcmp(const void *ptr1,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);

    其中以b开头的函数由任何支持套接口函数的系统所提供,以mem 开头的函数由ANSI C提供。

    bzero函数:将从dest指定的起始地址起,长度为nbytes(字节)的内存段设置为0。

    bcopymemcpy函数:复制内存的数据,参数src指向原地址,dest指向目的地址。nbytes表示复制的长度

    bcmpmemcmp函数:比较内存数据的大小,参数ptr1ptr2指向两个将要进行比较的存储区,nbytes是以字节为单位的存储区长度。函数的比较结果取决于第一个不相等的字节。如果ptr1>ptr2,函数返回大于0的值;如果ptr1=ptr2,函数返回0;如果ptr1<ptr2,函数返回小于0的值。

    memset函数:用于给由dest指定的目标中的指定数目len的字节设置位值。

  3. IP地址转换函数

    TCP/IP网络中,我们用到的IP都是以.隔开的十进制的数表示(例如:192.168.0.1),而在套接口的数据结构中用的则是32位的网络字节序的二进制数值。要实现两者之间的转换,就要用到一下3个函数

    1
    2
    3
    4
    5
    6
    7
    #include<arpa/inet.h>
    int inet_aton(const char *straddr,struct in_addr *addrptr);
    // 返回:转换成功返回1,不成功返回0
    char *inet_ntoa(struct in_addr inaddr);
    // 返回:若成功则返回指向点分十进制数串的指针,若失败,则返回NULL
    in_addr_t inet_addr(const char *straddr);
    // 返回:若成功则返回32位二进制的网络字节序地址,若出错,则返回INADDR_NONE

    INADDR_NONE是Linux下定义的一个常量,表示一个不存在的IP地址,一般将这个常量定义成255.255.255.255,用二进制表示再转换成有符号数就是-1了

    inet_aton函数:将点分十进制数的IP地址转换成为网络字节序的32位二进制数值。输入的点分十进制数IP存放在straddr中,作为返回结果的二进制数值存放在addrptr中。

  4. IP和域名的转换

    在网络上标识一台计算机可以使用IP地址或者是域名,在网络编程中很自然的会遇到两者的转换。Linux提供了一下两个函数实现两者的转换

    1
    2
    3
    #include<netdb.h>
    struct hostent *gethostbyname(const char *hostname);
    struct hostent *gethostbyaddr(const char *addr,size_t len,int family);

    两个函数的返回:若成功则返回一个指向hostent结构体的指针,若失败则返回空指针NULL,同时设置全局变量h_errno为相应的值

    h_errno有如下几种可能的取值:

    HOST_NOT_FOUND:找不到主机

    TRY_AGAIN:出错重试。

    NO_RECOVERY:不可修复性错误

    NO_DATA:指定的名字有效,但没有记录

    调用h_strerror()函数可以得到关于h_errno的详细出错地址

    其中,gethostbyname实现域名或主机名到IP地址的转换,参数hostname指向存放域名或主机名的字符串。

    gethostbyaddr函数实现IP地址到域名或主机名的转换。参数addr是一个指向含有地址结构in_addr的指针,参数len是此结构的大小,对于IPv4,其值为4,则IPv6的值为16,参数family为协议族。

    另外,结构体struct hostent<netdb.h>中定义,形式如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    struct hostent
    {
    char *h_name; // 主机的正式名称
    char *h_aliases; // 主机的别名
    int h_addrtype; // 主机的地址类型,IPv4为AF_INET
    int h_length; // 主机的地址长度,对于IPv4是4字节,即32位
    char **h_addr_list; // 主机的IP地址列表
    };
    #define h_addr h_addr_list[0] // 主机的第一个IP地址

    TCP套接口通信工作流程

    TCP套接口通信工作流程

通信工作的大致流程如下:

  1. 服务器先用socket()函数来建立一个套接口,用这个套接口完成通信的监听及数据的收发
  2. 服务器用bind()函数来绑定一个端口号和IP地址,使套接口与指定的端口号和IP地址向关联
  3. 服务器调用listen()函数,使服务器的这个端口和IP处于监听状态,等待网络中某一个客户机的连接请求。
  4. 客户机用socket()函数建立一个套接口,设定远程IP和端口
  5. 客户机调用connect()函数连接远程计算机指定的端口
  6. 服务器调用accept()函数来接收远程计算机的连接请求,建立起与客户机之间的通信连接
  7. 建立连接以后,客户机用write()函数向socket中写入数据。也可以用read()函数(或recv()函数)读取服务器发送来的数据
  8. 服务器用read()函数(或recv()函数)读取客户机发送来的数据,也可以用write()函数(或send()函数)来发送数据。
  9. 完成通信后,使用close()函数关闭socket连接。
坚持原创技术分享,您的支持将鼓励我继续创作
-------------本文结束感谢您的阅读-------------
0%