unix网络编程卷一:第三章

3.2 套接口地址结构

struct in_addr {
    in_addr_t s_addr;    /* 32bit ipv4 address */
};

struct sockaddr_in {
    uint8_t        sin_len;    /* length of structure */
    sa_family_t    sin_family; /* AF_INET */
    in_port_t      sin_port;   /* 16bit TCP/UDP port number */
    struct in_addr sin_addr;   /* 32bit ipv4 address */
    char sin_zero[8];          /* unused */
};
  • in_addr_t 至少32比特
  • in_port_t 至少16比特

通用套接口地址结构

#include <sys/socket.h>
struct sockaddr {
	uint8_t sa_len;
	sa_family_t sa_family; // AF_xxx
	char sa_data[14];
};

有的函数中使用的是通用套接口,所以在程序中有时需要将特定套接口(如sockaddr_in )进行转换。

int bind(int, struct sockaddr* , socklen_t);
struct sockaddr_in serv; //ipv4 socket address structure
//bind函数中使用的是sockaddr,所以将serv传入bind函数中时需要进行类型转换,否则会有警告
bind(sockfd, (struct sockaddr*)(&serv) , sizeof(serv));

在这里插入图片描述

3.3 值-结果参数

名字有点难理解,主要说的是给函数传参时套接字的长度应该传递一个指针还是传递一个值。

  • 当从用户态到内核态时,传递套接字长度的值;
  • 当从内核态到用户态时,传递套接字长度的地址;

从进程到内核传递套接口地址结构的函数:

bind
connect 
sendto

从内核到进程传递套接口地址结构的函数:

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, 
struct sockaddr *src_addr, socklen_t *addrlen);
int getsockname(int sockfd, struct sockaddr *localaddr, socklen_t *addrlen);
int getpeername(int sockfd, struct sockaddr *peeraddr, socklen_t *addrlen);

为什么从内核态到用户态时要传递套接字长度的指针呢?
从内核到进程传递套接口地址结构的函数被调用时,结构体的大小传入时有一个值,告诉内核该结构体的大小,内核在写这个结构体时大小不能超过传入的值,但是可以小于传入的值,即:实际结构体的size可能小于传入的值,那么内核到底写入了多少个字节呢,可以通过传入的长度参数来表示,这时候,传入的长度参数就需要是一个指针,因为它的值是可能被改变的

实际上在传递指针时,结构体的长度这个参数在内核中被改变。
在这里插入图片描述

3.4 字节排序函数

主要是大端和小端的问题。
大端:高字节在低地址;
小端:低字节在低地址;
有的操作系统是小端模式,有的是大端模式,网际协议是大端,所以对于小端的系统,需要进行大小端的转换。
转换使用的函数如下:

#include <netinet/in.h>
uint16_t htons(uint16_t host16bitvalue); //host to network, s means short(16bit)
uint32_t htonl(uint32_t host32bitvalue); //host to network, l means long(16bit)

uint16_t ntohs(uint16_t net16bitvalue); //network to host, s means short(16bit)
uint32_t ntohl(uint32_t net32bitvalue); //network to host, l means long(16bit)

测试主机是大端还是小端的函数:

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<time.h>
#include<math.h>

int main(){
    union {
        short s;
        char ch[2];
    }un;
    un.s = 0x0102;
    if(sizeof(short) == 2){
        if(un.ch[0] == 1 && un.ch[1] == 2){
            printf("big-endian\n");
            printf("ch[0] = %x\t ch[1] = %x\n", un.ch[0], un.ch[1]);
        }
        else if(un.ch[0] == 2 && un.ch[1] == 1){
            printf("little-endian\n");
            printf("ch[0] = %x\t ch[1] = %x\n", un.ch[0], un.ch[1]);
        }
    }
    else{
        printf("short's size is %d\n", sizeof(short));
    }   
    return 0 ;
}

3.5 字节操作函数

就是一些以字节为单位进行的操作。
第一组以b(byte)开头:

#include <string.h>
void bzero(void *dest, size_t nbytes);//清零
void bcopy(const void* src, void *des, size_t nbytes);//拷贝
int bcmp(const void* ptr1, const void* ptr2, size_t nbytes);//逐字节比较,返回值为0表示相同,返回值不为0表示不同

第二组是ANSI C函数:

#include <string.h>
void *memset(void *dest, int c, size_t nbytes);
void *memcpy(void *des, const void* src, size_t nbytes);//拷贝
int memcmp(const void* ptr1, const void* ptr2, size_t nbytes);//逐字节比较,返回值为0表示相同,返回值不为0表示不同

观察memxxx函数,可以发现,des放在src左侧,与赋值顺序相同,即:
des = src;
另外,memxxx函数都需要一个表示大小的参数。

3.6 inet_aton/inet_addr/inet_ntoa函数

这几个是IP地址与字符串的转化函数,有时候比较喜欢用点分十进制的字符串表示IP地址,但实际上IP地址是一个32位的数,可以通过这几个函数实现从字符串到32位IP地址的形式转换。

#include <arpa/inet.h>
int inet_aton(const char* strptr, struct in_addr* addrptr);//返回1:串有效, 0:串无效
char* inet_ntoa(struct in_addr inaddr);//返回指向点分十进制字符串的指针
in_addr_t inet_addr(const char* strptr);//成功则返回32比特IP地址,失败返回INADDR_NONE

上面这些函数只能处理IPv4,而且最好不要使用in_addr_t ,会出现很多问题,还不如不看。

3.7 inet_pton/inet_ntop函数

这两个是对IPv4和IPv6都能处理的函数,推荐使用这两个函数。

#include <arpa/inet.h>
int inet_pton(int family, const char* strptr, void* addrptr);//字符转IP地址,返回1:成功, 0:输入无效, -1:出错

const char* inet_ntop(int family, const void* addrptr, char* strptr, size_t len);//返回:指向结果的指针,出错返回null

注意函数inet_ntop的最后一个参数len,它表示存放转换后结果的数组的大小,以免函数调用的时候数组越界,数组的大小应该足够容得下以字符串形式表示的IP地址。

<netinet/in.h>中定义了两个宏,可以帮助我们规定数组的大小:

#define INET_ADDRSTRLEN 16 //IPv4
#define INET6_ADDRSTRLEN 46 //IPv6

IPv4地址最大255.255.255.255,算起来正好16个字符(加上尾部的\0)。

char str[INET_ADDRSTRLEN];
inet_ntop(AF_INET, (void*)(&foo.sin_addr), str, sizeof(str));
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值