Socket网络编程中的一些问题(Linux)

背景
  笔者最近在自学Linux网络通信编程,相信大家都不会陌生,就是使用套接字理念,运用socket,bind,listen,accept。。。一系列函数进行服务器/客户端的通信问题。
  就在刚才,作者解决了一个困扰两天的问题(其实是程序中括号加错了地方!!),可恶的GCC编译器,并没有提示错误。当然,接下来,我总不能记录我如何解决括号加错地方的问题,主要这两天在尝试各种解决方法的问题中,学到了不少知识。现记录下来,以备以后查看,并希望能对正在学习网络编程的同学一些参考。

干货
一、sockaddr和sockaddr_in

  下面首先介绍两个重要的数据类型:sockaddr和sockaddr_in,这两个结构类型都是用来保存socket信息的,如下所示:

    struct sockaddr 
    {
        unsigned short sa_family;/*地址族*/
        char sa_data[14];/*包含该socket的IP地址和端口号*/
    };
    struct sockaddr_in
    {
        short int sa_family;/*地址族*/
        unsigned short int sin_port;/*端口号*/
        struct int_addr sin_addr;/*IP地址*/
        unsigned char sin_zero[8];/*填充0以保持与sockaddr同样大小
    };

  这两个数据类型是等效的,在这里,我们不涉及具体每项参数的含义(普适整片文章)。只想告诉大家,现在一般我们都使用sockaddr_in结构体来保存socket信息,但是好多函数内的参数是默认为sockaddr数据类型的,那我们使用的时候只需要进行一次强制类型转换就可以了,如下:

    struct sockaddr_in serv_addr;/*定义serv_addr结构体*/
    int sockfd;
    sockfd=socket(AF_INET,SOCK_STREAM,0);/*创建套接字,返回套接字描述符*/
    ...
    ...
    .../*设置serv_addr结构体中的相关参数*/

    connect(sockfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr));/*调用连接函数*/

  上述代码是我从一个简单的客户端程序中摘出来的一部分,通过connect函数的第二个参数,我们可以印证上述所讲的内容。

二、地址格式转化
  通常呢,我们在表达地址时采用的是点分十进制表示的数值,例如“127.0.0.1”表示本地的IP。然而在socket编程中所使用的则是二进制地址值,这样就需要将两个数值进行转换。
  我们这里呢,就讲两个能够兼容IPV4和IPV6两种协议的地址转化函数:inet_pton函数将点分十进制地址映射为二进制地址;inet_ntop函数则与之相反。下面我们看一下inet_pton函数的语法。

    /*所需头文件*/
    #include <apra/inet.h>
    /*函数原型*/
    int niet_pton(int family,const char *strptr,void *addrptr)
    /*函数参数含义*/
    family:协议
    strptr:要转化的地址
    addrptr:转化后的地址

三、gethostbyname()函数
  上一个知识点,我们讲了地址转化函数,那么我们所需要的点分十进制IP地址到底从哪来呢?当然我们可以直接找到服务器端的IP地址,如“192.168.1.1”,然后使用地址转化函数转化成二进制地址。
  通常,我们是不愿意记忆冗长的IP地址的,尤其是到了IPV6时,地址长度多达128位,那直接输入IP地址本身就是一件困难的事情了,因此这时使用主机名称将会是很好的选择。gethostbyname就是这样一个可以实现此功能的函数。

    /*所需头文件*/
    #include <netdb.h>
    /*函数原型*/
    struct hostent *gethostbyname(const char *hostname)
    /*函数参数*/
    Hostname:主机名(可以是字符串,也可以是地址)
    /*函数返回值*/
    成功:hostent类型指针
    出错:-1

    /*hostent数据类型*/
    struct hostent
    {
        char *h_name;/*正式主机名*/
        char **h_aliases;/*主机别名*/
        int h_addrtype;/*地址类型*/
        int h_length;/*地址长度*/
        char **h_addr_list;/*指向IPV4和IPV6的地址指针数组*/
    }

四、将IP地址送出去
  其实这一小节才是笔者这两天的收获,前面的都是铺垫。第一小节,我们讲到sockaddr_in这么一个数据类型,其中里面有一项成员为:
  struct in_addr sin_addr;/*IP地址*/
这个成员变量就是用来保存IP地址的地方,但是我们应该以怎样的方式将地址赋值给sin_addr呢?我们看到它也是一个结构体,我们首先来看下它的成员变量

    struct in_addr
    {
        in_addr_t s_addr;
    };
    /*in_addr_t用来表示一个32位的IPV4地址*/

(1) 使用inet_pton()函数
  我们可以利用inet_pton函数将点分十进制地址转换成二进制地址,然后将地址送给sockaddr_in中的成员sin_addr,代码如下:

    void *addrptr;
    inet_pton(AF_INET,"127.0.0.1",addrptr);
    serv_addr.sin_addr=*((struct in_addr*)addrptr);

  有上述代码可以看到,由于sin_addr为in_addr结构体类型,所以必须先将addrptr转换成该类型指针,然后取其值送给sin_addr.
  
(2)使用gethostbyname函数
  使用gethostbyname函数将主机名称转换成二进制地址,送给sin_addr,代码如下:

    struct hostent* host;
    host=gethostbyname("127.0.0.1/Liu");
    ...
    ...
    serv_addr.sin_addr=*((struct in_addr *)host->h_addr);

  发现了没?原理是一样的,gethostbyname的返回值是个结构体,但是sin_addr需要的只是地址,所以需要将host结构体中的成员h_addr送给sin_addr。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值