UNP1——3. 套接字编程简介

套接字编程基础

1. 套接字结构

1. IPV4


truct in_addr{
        in_addr_t       s_addr;
};
 
struct sockaddr_in{
        uint8_t         sin_len;
        sa_family_t     sin_family;
        in_port_t       sin_port;
        struct in_addr  sin_addr;
        char            sin_zero[8];
};
  • sin_len 是为了增加对OSI协议的支持而随4.3 BSD-Reno 添加的。在此之前,第一个成员是 sin_family,它是一个无符号短整数。
  • 即使有长度字段,我们也无需设置和检查它
  • POSIX 标准只需要这个结构中的三个字段:sin_family、sin_addr和sin_port。对于符合POSIX的实现来说,定义额外的结构字段是可以接受的。

1.2 IPV6

IPV6的套接字地址结构定义在头文件<netinet6/in6.h>中,具体如下:

/*
 * IPv6 address
 */
typedef struct in6_addr {
    union {
        __uint8_t   __u6_addr8[16];
        __uint16_t  __u6_addr16[8];
        __uint32_t  __u6_addr32[4];
    } __u6_addr;                    /* 128-bit IP6 address */
} in6_addr_t;


/*
 * Socket address for IPv6
 */
#define SIN6_LEN                                    /* required for compile-time tests */
struct sockaddr_in6 {
    __uint8_t       sin6_len;       /* length of this struct(sa_family_t) */
    sa_family_t     sin6_family;    /* AF_INET6 (sa_family_t) */
    in_port_t       sin6_port;      /* Transport layer port # (in_port_t) */
    __uint32_t      sin6_flowinfo;  /* IP6 flow information */
    struct in6_addr sin6_addr;      /* IP6 address */
    __uint32_t      sin6_scope_id;  /* scope zone index */
};

地址族sin_family,IPV4为AF_INET,IPV6为AF_INET6
在这里插入图片描述

2. 值、结果参数 (还是有点不理解)

从进程到内核

img

从进程到内核传递套接字地址结构的函数有3个:

  1. bind()
  2. connect()具体为connect(sockfd, (struct sockaddr*) &serv, sizeof(serv))
  3. sendto()

即,把套接字地址结构的指针和所指内容的大小都传递给了内核(kernel),于是*kernel知道它需要从进程中复制多少数据进来*

从内核到进程

img

从内核到进程传递套接字地址结构的函数有4个:

  1. accept()
  2. recvfrom()
  3. getsockname()
  4. getpeername()具体为getpeername(unixfd, (struct sockaddr*) &cli, &len)

为什么在这里不传递地址结构的大小而是传其大小的指针呢?

原因在于:

当函数被调用时,结构大小是一个value,它告诉内核该结构的大小,这样内核在写该结构时不至于越界

当函数返回时,结构大小是一个result,它通过该指针指向的整数*告诉进程内核在该结构中究竟存储了多少信息*

3. 字节排序函数

考虑一个16位整数,它由2个字节组成。内存中储存的这两个字节有两种方法:一种是将低序字节储存在起始地址,这称为小端字节序;另一种方法是将高序字节储存在起始地址,这称作大端字节序。如下图:
在这里插入图片描述
遗憾的是这两种字节序之间没有标准可循,我们称之为主机字节序。而网络协议必须指定一个网络字节序。故我们需要在这两种字节序转换。

#include <netinet/in.h>

/*
 * h: host              n: network
 * s: short(16-bit)     l: long(32-bit)
 */
// 返回:网络字节序
uint16_t htons (uint16_t host16bitvalue);
uint32_t htonl (uint32_t host32bitvalue);

// 返回主机字节序
uint16_t ntohs (uint16_t net16bitvalue);
uint32_t ntohl (uint32_t net32bitvalue);

4. 地址转换函数

地址转换函数:将ASCII字符串(IPV4点分十进制)和网络字节序的二进制序互换。

4.1 IPV4

IPV4主要有下列3个:

#include <arpa/inet.h>

/* 
 * a: ASCII         n: network byte order
 */

/*ASCII-->网络二进制
 *将strptr所指的字符串转换成32位网络字节序的二进制地址,通过addrptr存储
 *返回:1成功  0失败
 */
int inet_aton (const char* strptr, struct in_addr* addrptr);    

/*ASCII-->网络二进制【已经被废弃】
 *将strptr所指的字符串转换成32位网络字节序的二进制地址
 *返回:32位网络字节序的二进制地址     失败返回INADDR_NONE(全1,意味着本函数不能正常处理广播地址255.255.255.255)
 */
in_addr_t inet_addr (const char* strptr);


/*网络二进制-->ASCII
 *将32位网络字节序的二进制地址inaddr转换成ASCII字符串
 *返回:指向点分十进制字符串的指针
 */
char* inet_ntoa (struct in_addr inaddr);

4.2 IPV6 & IPV4

下面介绍的这两组函数,IPV6和IPV4都适用。

#include <arpa/inet.h>

/* 
 * p: presentation(表达,ASCII字符串)     n: numeric(数值,二进制)
 */

/*ASCII-->网络二进制
 *将strptr所指的字符串转换成32位网络字节序的二进制地址,通过addrptr存储,family指定协议族(AF_INET, AF_INET6)
 *返回:1成功  0非有效表达格式  -1出错
 */
int inet_pton (int family, const char* strptr. void* addrptr)/*网络二进制-->ASCII
 *将addrptr所指的网络字节序的二进制地址转换成ASCII字符串,通过strptr存储,family指定协议族,len指定目标存储单元的大小
 *<netinet/in.h>中定义了len的大小,IPV4 INET_ADDRSTRLEN 16,   IPV6 INET6_ADDRSTRLEN 46
 *返回:指向ASCII字符串的指针
 */
const char* inet_ntop (int family, const void* addrptr, char* strptr, size_t len);

4.3 协议无关的地址转换函数sock_ntop

上述的函数inet_ntop()需要调用前根据某个协议创建关于它的struct sockaddr_in addrstruct sockaddr_in6 addr6.这样在函数调用时就会编出“协议相关性”程序。

如何才能编出“协议无关性”的地址转换程序?

下面给出1组协议无关性的地址转换函数sock_ntop().这个函数的参数是指向某个套接字地址结构的指针,它通过查看该结构的内部,确定其协议族,如何调用适当的函数来返回该地址的字符串表达。

下面给出的了该函数仅支持AD_INET的情况。

#include "unp.h"

char * sock_ntop (const struct sockaddr* sa, socklen_t salen)
{
    char        portstr[8];
    static char str[128];       /* Unix domain is largest */

    switch (sa->sa_family) {
    case AF_INET: {
        struct sockaddr_in  *sin = (struct sockaddr_in *) sa;

        if (inet_ntop(AF_INET, &sin->sin_addr, str, sizeof(str)) == NULL)
            return(NULL);
        if (ntohs(sin->sin_port) != 0) {
            snprintf(portstr, sizeof(portstr), ":%d", ntohs(sin->sin_port));
            strcat(str, portstr);
        }
        return(str);
    }
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

东阳z

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值