【unix网络编程第三版】阅读笔记(二):套接字编程简介

2023-05-16

unp第二章主要将了TCP和UDP的简介,这些在《TCP/IP详解》和《计算机网络》等书中有很多细致的讲解,可以参考本人的这篇博客【计算机网络 第五版】阅读笔记之五:运输层,这篇博客就不再赘述。

本篇博客主要记录套接字编程API,从一些基本的API来一步一步了解套接字网络编程。

1.套接字地址结构

大多数的套接字函数都以一个指向套接字地址结构的指针作为参数。每个协议簇都定义了自己的套接字地址结构。
套接字地址结构均以sockaddr_开头,并以对应每个协议簇的唯一后缀结尾。

1.1 ipv4套接字地址结构

//ipv4的套接字地址结构
typedef uint32_t in_addr_t;
struct in_addr
{
    in_addr_t s_addr;//32位的ipv4地址
};
//网络套接字地址结构
struct sockaddr_in
{
    //查看in.h源码发现此处为__SOCKADDR_COMMON (sin_)
    uint8_t sin_len  //套接字地址结构的长度
    sa_family_t sin_family //   AF_INET
    in_port_t sin_port; //端口号
    struct in_addr sin_addr;    //网络地址
    unsigned char sin_zero[8];//不使用,该字段必须为0
};

通常我们在使用套接字的时候,只会用到三个字段:sin_family,sin_addr和sin_port,如下:

struct sockaddr_in  servaddr;//声明一个套接字地址
bzero(&servaddr, sizeof(servaddr));//套接字结构体清0
servaddr.sin_family = AF_INET;//使用ipv4的协议簇
servaddr.sin_port   = htons(13);//端口号,13为获取日期和时间的端口

1.2 通用套接字结构

当作为一个参数传递进任何套接字函数时,套接字地址总是以引用形式来传递。
类似于void*代表通用指针类型,通用套接字地址结构可以便于参数传递,使得套接字函数能够处理来自所支持的任何协议簇的套接字地址结构。

struct sockaddr
{
    __SOCKADDR_COMMON (sa_);    /* Common data: address family and length.  */
    char sa_data[14];       /* Address data.  */
};

以bind函数为例,说明它的用法:

struct sockaddr_in  servaddr;
//SA为struct scokaddr的简写
Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));//必须对servaddr进行强制转换
//附:Bind函数的原型
int bind(int , struct sockaddr* , socklen_t);

1.3 IPv6的套接字地址结构

struct in6_addr
{
    uint8_t __u6_addr8[16];//128位IPv6地址,网络字节序
}
struct sockaddr_in6
{
    uint8_t sin_len  //套接字地址结构的长度,128
    sa_family_t sin_family //   AF_INET6
    in_port_t sin6_port;    //传输端口
    uint32_t sin6_flowinfo; //IPv6 流标字段
    struct in6_addr sin6_addr;  //IPv6 地址
    uint32_t sin6_scope_id; //对于具备范围的地址,此字段标志其范围
};

1.4 新的通用套接字地址结构

为IPV6套接字API定义的新的通用套接字地址结构,其足以容纳所支持的任何套接字地址结构

书中说到在in.h文件中,可是我怎么也找不到。后来在usr/include/X86_64-linux-gnu/bits的socket.h中找到

struct sockaddr_storage
{
    uint8_t ss_len;//length of this struct
    sa_family_t ss_family; // address family:AF_XXXX value
    __ss_aligntype __ss_align;  /* Force desired alignment.  */
    char __ss_padding[_SS_PADSIZE];//enough storage to hold any type of socket address that the system supports
};

2. 值-结果参数

当一个套接字函数传递一个套接字地址结构时,往往是采用引用的方式传递。为了读取正确的字节数和写入时不会越界,该套接字的长度也需要作为参数传递给套接字函数。

不过,其传递方式取决于该结构的传递方向:是从进程到内核,或者内核到进程

2.1 从进程到内核

这类函数有三个:bind,connect和sendto,其传递的时该结构的整数大小,这样内核就知道从进程中复制多少数据,例如:

struct sockaddr_in serv; 
connect( sockfd, ( SA * )&serv, sizeof( serv ) );//第三个参数为传递的该结构的整数大小

2.2 从内核到进程

这类函数有4个,accept,recvfrom,getsockname和getpeername,其传递的是指向某个套接字结构的指针和指向表示该结构大小的整数变量和指针:

struct sockaddr_un cli;
socklen_t len;
len = sizeof(cli);//len is value
getpeername(unixfd,(SA*)&cli,&len);//len既作为值被传递进去,又作为结果返回出来

3. 字节排序函数

3.1 大端和小端字节序

字节序分为大端字节序和小端字节序,以下面的代码来判断系统到底时何种字节序:

#include "unp.h"
int main(int argc, char const *argv[])
{
    union {
        short s;
        char c[sizeof(short)];
    }un;
    un.s = 0x0102;
    printf("%s: ", CPU_VENDOR_OS);
    if (sizeof(short) == 2)
    {
        if (un.c[0]==0x01 && un.c[1]==0x02)
        {
            printf("big-endian\n");
        }
        else if(un.c[0]==0x02 && un.c[1]==0x01)
        {
            printf("little-endian\n");
        }
        else 
            printf("unknown\n");
    }
    else 
        printf("sizeof(short)=%d\n", (int)sizeof(short));
    exit(0);
    return 0;
}

运行此代码后输出:

$ gcc byteorder.c -o byteorder -lunp
$ ./byteorder 
x86_64-unknown-linux-gnu: little-endian

代表本机时小端字节序

3.2 转换函数

网络字节序使用大端字节序来传送,那么本机和网络之间要正确传递数据,就需要一个转换函数。

这两种字节序之间的转换使用以下4种函数:

uint16_t htons( uint16_t host16bitvalue );
uint32_t htonl( uint32_t host32bitvalue );----均返回:网络字节序的值
uint16_t ntohs( uint16_t net16bitvalue );
uint32_t ntohl( uint32_t net32bitvalue );----均返回:主机字节序的值

h代表host主机,n代表net网络,s代表short,l代表long

测试用例:将0x1234(4660)从主机字节序转换成网络字节序,转换后应该为0x3412(13330)

#include "unp.h"
int main(int argc, char const *argv[])
{
    uint16_t portAddr;
    portAddr = htons(0x1234);//十进制值为4660
    printf("the port 0x1234 netword port is:%d\n", portAddr);
    printf("the port is:%d\n", ntohs(portAddr));
    return 0;
}
//输出结果
the port 0x1234 netword port is:13330
the port is:4660//答案正确

4. 字节操纵函数

操作多字节字段的函数有两组,他们既不对数据作解释,也不假设数据是以空字符结束的C字符串。

#include <string.h>
//b开头(表示字节)的一组函数
void bzero(void* dest,size_t nbytes);//将指针dest以后的nbytes位置0
void bcopy(const void* src,void *dest , size_t nbytes);//将指针src后的nbytes位复制到指针dest
int bcmp(const void *ptr1, const void* ptr2, size_t nbytes);//比较ptr1和ptr2后的nbytes位的大小
//mem开头(表示内存)的一组函数
void *memset(void *dest , int c , size_t len);//将dest开始的一段长度为len的内存的值设为c
void *memcpy(void *dest , const void* src , size_t nbytes);//同bcopy
int memcmp(const void *ptr1 , const void *ptr2, size_t nbytes);//同bcmp

5.地址转换函数

5.1 inet_aton,inet_addr和inet_ntoa函数

在点分十进制数串和与它长度为32位的网络字节序二进制值间转换ipv4地址


//将__cp所指的字符串转换成一个32位的网络字节序,保存在__inp结构体中
int inet_aton (const char *__cp, struct in_addr *__inp) ;//返回:若字符串有效则为1,否则为0
//同上,只不过转换后的值作为返回值直接返回
in_addr_t inet_addr (const char *__cp);//如字符串有效则返回32位字节序,否则返回INADDR_NONE
//将一个32位的网络字节序二进制IPv4地址转换成相应的点分十进制数串
char *inet_ntoa (struct in_addr __in);//返回一个点分十进制数串的指针

测试用例:输入一个点分十进制网络地址,测试输出

#include "unp.h"
int main(int argc, char const *argv[])
{
    struct in_addr addr;
    char *pAddr;
    inet_aton(argv[1],&addr);
    printf("%d\n",addr.s_addr);
    pAddr = inet_ntoa(addr);
    printf("%s\n",pAddr);
    return 0;
}
//输出结果:
$ ./Test_inet_aton 127.0.0.1  //0x7F000001
16777343//小端字节序,0100007F
127.0.0.1//转换为点分十进制数串

5.2 inet_pton和inet_ntop函数

随着IPv6出现的新函数,p代表表达式,即ASCII字符串;n代表数值,即存放在套接字地址结构中的二进制值。
这两个函数对于ipv4和ipv6都适用。

//int __af为AF_INET或者AF_INET6
//将__cp存储的字符串转换成二进制,结果存放在__buf
int inet_pton (int __af, const char *__cp, void * __buf)
//将__cp所指的二进制转换成字符串,__len指定二进制大小,
const char *inet_ntop (int __af, const void *__cp, char *__buf, socklen_t __len)

5.3 sock_ntop和相关函数(作者自定义的函数)

inet_ntop函数的基本问题时要求调用者传递一个指向某个二进制地址的指针,而该地址通常包含在一个套接字地址中
因此调用者事先需要知道这个结构的格式。

struct sockaddr_in addr;//需要事先定义,如果为ipv6则为sockaddr_in6
inet_ntop(AF_INET,&addr.sin_addr,str,sizeof(str));

为了解决这个问题,作者自己写了一个函数:


#include "unp.h"

char *sock_ntop(const struct sockaddr *sa, socklen_t salen);
//具体实现如下:
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);
    }
/* end sock_ntop */
#ifdef  IPV6
    case AF_INET6: {
        struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) sa;
        str[0] = '[';
        if (inet_ntop(AF_INET6, &sin6->sin6_addr, str + 1, sizeof(str) - 1) == NULL)
            return(NULL);
        if (ntohs(sin6->sin6_port) != 0) {
            snprintf(portstr, sizeof(portstr), "]:%d", ntohs(sin6->sin6_port));
            strcat(str, portstr);
            return(str);
        }
        return (str + 1);
    }
#endif
//后续省略了一些case,如case AF_UNIX:
}

6. readn,writen和readline

字节流套接字上调用read和write输入和输出的字节数可能比请求的数量要少,所以作者自己写了readn,writen和readline三个函数。

readn的实现如下:

#include    "unp.h"
ssize_t                     /* Read "n" bytes from a descriptor. */
readn(int fd, void *vptr, size_t n)
{
    size_t  nleft;
    ssize_t nread;
    char    *ptr;
    ptr = vptr;
    nleft = n;
    while (nleft > 0) {
        if ( (nread = read(fd, ptr, nleft)) < 0) {//循环读取
            if (errno == EINTR)
                nread = 0;      /* and call read() again */
            else
                return(-1);
        } else if (nread == 0)
            break;              /* EOF */
        nleft -= nread;
        ptr   += nread;
    }
    return(n - nleft);      /* return >= 0 */
}
/* end readn */

writen的实现如下:

#include    "unp.h"
ssize_t                     /* Write "n" bytes to a descriptor. */
writen(int fd, const void *vptr, size_t n)
{
    size_t      nleft;
    ssize_t     nwritten;
    const char  *ptr;
    ptr = vptr;
    nleft = n;
    while (nleft > 0) {
        if ( (nwritten = write(fd, ptr, nleft)) <= 0) {
            if (nwritten < 0 && errno == EINTR)
                nwritten = 0;       /* and call write() again */
            else
                return(-1);         /* error */
        }
        nleft -= nwritten;
        ptr   += nwritten;
    }
    return(n);
}

readline的实现如下:

#include    "unp.h"
//较慢的一个版本,需要每次都调用系统的read函数
//作者还写了一个较快的版本,这里就不贴出代码了,主要思想是定义一个my_read一次读取进来,但每次都只返回给readline一个字符
ssize_t
readline(int fd, void *vptr, size_t maxlen)
{
    ssize_t n, rc;
    char    c, *ptr;
    ptr = vptr;
    for (n = 1; n < maxlen; n++) {
        if ( (rc = read(fd, &c, 1)) == 1) {
            *ptr++ = c;
            if (c == '\n')
                break;
        } else if (rc == 0) {
            if (n == 1)
                return(0);  /* EOF, no data read */
            else
                break;      /* EOF, some data was read */
        } else
            return(-1); /* error */
    }
    *ptr = 0;
    return(n);
}
/* end readline */
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【unix网络编程第三版】阅读笔记(二):套接字编程简介 的相关文章

  • STM32基于FreeRTOS的多任务程序案例

    STM32基于FreeRTOS的多任务程序案例 一 初步了解FreeRTOS二 实验要求三 基于FreeRTOS的多任务程序案例四 参考链接 使用工具 野火stm32mini开发板 Keil uVision5 野火多功能调试助手 一 初步了
  • 【TcaplusDB知识库】TcaplusDB刷新tbus通道介绍

    命令 xff1a RefreshBusCfg 其中 xff0c 指进程的进程id xff0c 比如1 2 2 2 1 2 1 2等 xff0c 也支持简单的正则表达式 xff0c 比如RefreshBusCfg 1 2 2 那么只要匹配到1
  • Python中下划线的5种含义

    作者 xff1a 地球的外星人君 链接 xff1a https zhuanlan zhihu com p 36173202 来源 xff1a 知乎 著作权归作者所有 商业转载请联系作者获得授权 xff0c 非商业转载请注明出处 分享一篇文章
  • ubuntu下 安装PX4编译环境

    最近博主的ubuntu虚拟机再次崩溃 xff0c 狠下决心将4G内存升级为12G 这样就可以给虚拟机多分配些内存了 鉴于前两次安装PX4环境出现了很多错误 xff0c 走了很多弯路 xff0c 没有一一记录下来 xff0c 所获甚少 故而借
  • PX4原生固件,position_estimator_inav解读

    INAV integrated navigation组合导航 对于多旋翼的位置姿态估计系统 xff1a PX4原生固件如今已经默认使用EKF2了 xff0c 另一种情况是 使用local position estimator attitud
  • FusionCharts Free (FCF) 版本 v3.0 更新细节

    版本 v3 0 更新细节 1 新的图表类型 滚动图 柱二维 xff0c 二维和区系的二维 xff0c 堆栈柱二维 xff0c 二维结合 xff0c 结合二维 xff08 双 Y 轴 xff09 样图 样条区域图 对数坐标图 二维多图单 组合
  • dwm-1000 测距总是出现 #define SYS_STATUS_RXPTO 0x00200000UL /* Preamble detection timeout */

    ex 05b ds twr resp 程序 总是出现 致使官方的代码 无法实现通讯 define SYS STATUS RXPTO 0x00200000UL Preamble detection timeout 需要着重修改参数
  • VNC远程桌面到linux,提示connection refused(10061)解决办法

    确认server端的VNC服务开启 xff0c service vncserver start xff0c 检测状态时ok的 ps ef grep vnc xff0c 来查看不是已经开启多个vnc连接 如果有多个vnc连接 xff0c 使用
  • uio驱动框架

    核心 xff0c 利用mmap进行映射 参考资料 uio 编写实例 1 https blog csdn net wujiangguizhen article details 12453253 uio编写实例 2 https blog csd
  • enum类型被intent所携带时需要注意的地方

    一般我们在Activity之间传递对象时多用Parcelable 比如写一个class xff0c 在这个class上标明implements Parcelable并实现接口就可以用Intent putExtra String Parcel
  • dump文件,windbg

    dump文件 xff0c 在VC中的调试还是非常非常非常有用的 xff0c 因为我们也不会经每一行代码都加上日志 xff0c 当然如果你愿意 xff0c 也可以每一行都加上日志 xff1b 在Windows上 xff0c 添加dump文件有
  • 使用PyQt4制作一个音乐播放器(1)

    1 前言 最近用Python给老妈写了一个处理excel表格的小软件 xff0c 因为自己平时用Python一般都是用在数值计算领域 xff0c 所以一般使用命令行的方式交互即可 但是给老妈用发现用命令行交互方式使用并不是很方便 xff0c
  • AI 到底是怎么「想」的?

    本文作者 xff1a kurffzhou xff0c 腾讯 TEG 安全工程师 最近 xff0c Nature发表了一篇关于深度学习系统被欺骗的新闻文章 xff0c 该文指出了对抗样本存在的广泛性和深度学习的脆弱性 xff0c 以及几种可能
  • 效能优化实践:C/C++单元测试万能插桩工具

    作者 xff1a mannywang xff0c 腾讯安全平台后台开发 研发效能是一个涉及面很广的话题 xff0c 它涵盖了软件交付的整个生命周期 xff0c 涉及产品 架构 开发 测试 运维 xff0c 每个环节都可能影响顺畅 高质量地持
  • tensowflow报错tensorflow.python.framework.errors_impl.InvalidArgumentError<exception str

    tensorflow用于自己的数据集时 xff0c 在用saver restore导入模型到Session中 xff0c 导入语句报错 xff0c 异常链终止时提示 xff1a tensorflow python framework err
  • 详解HTTP中的摘要认证机制

    在上一期http blog csdn net tenfyguo article details 6167190中笔者较为详细的介绍了HTTPBasic认证在apache下的配置 xff0c 通过简单的实验演示了HTTP Basic认证的基本
  • 【ubuntu(Linux)安装Vs code并配置c++编译及cmake多文件编译】

    目录标题 VS code配置c 43 43 编译环境1 Linux系统安装2 在Ubuntu中安装VS code2 1 首先下载对应系统的VS code安装包2 2 安装VS code 3 在ubuntu系统下的vscode中配置g 43
  • yolo v4安装与使用

    yolo v4安装与使用 GPU版本 系统环境 Ubuntu18 04 cuda11 0 cudnn8 0 4 opencv3 4 4 相关链接 官网链接 xff1a https github com AlexeyAB darknet yo
  • Jetson NX克隆与烧录镜像

    前言 本文主要说明如何在Ubuntu系统上备份Jetson NX镜像 xff0c 并烧录镜像到多台NX开发板上 我的NX的系统是在SD卡上的 xff0c 所以这个方法只适用于系统装在SD卡上的开发板 参考链接 https blog csdn
  • ros2 CMakeLists.txt与packages.xml写法

    注 xff1a 尽量使用ament cmake auto来编写cmakelists txt文件 xff0c 依赖的程序包在packages xml文件中用 lt depend gt nav msgs lt depend gt 的形式声明 x

随机推荐

  • TX2 ubuntu18.04 改固定IP地址 eth0无本机IP

    注意事项 我是在TX2 ubuntu18 04上改的固定IP地址 其他主机上ubuntu18 04好像需要另一种方法 xff0c 需要你自己去百度上搜 参考博客 https blog csdn net a13662080711 articl
  • MFC与.NET混合编程

    1 xff1a VS2008 建立 MFC程序 xff1b 2 xff1a Alt 43 F7 设置 xff1a 公共语言运行库支持 xff08 选择 xff09 公共语言运行库支持 clr xff1b 3 xff1a 添加 Net相应库
  • 在TX2(Jetpack4.2+kernel4.9.140+librealsense v2.22)上安装D435i驱动

    在TX2 xff08 Jetpack4 2 43 kernel4 9 140 43 librealsense v2 22 xff09 上安装D435i驱动 前言准备工作系统版本参考资料 操作步骤 前言 用了很长时间 xff0c 参考了很多博
  • ROS发布静态tf变换

    方法一 xff1a include lt ros ros h gt include lt tf transform broadcaster h gt int main int argc char argv ros init argc arg
  • ROS常见问题及解决方法

    1 undefined reference to 96 tf TransformBroadcaster TransformBroadcaster 问题描述 xff1a CMakeFiles imu data dir src imu data
  • TX2制作镜像并烧写镜像

    前言 本文简要介绍TX2如何制作镜像并烧写镜像 xff0c 如果读者一开始是通过sdkmanager安装的TX2系统可直接进入笔记本中 nvidia nvidia sdk JetPack 4 2 Linux P3310 Linux for
  • php写守护进程(Daemon)

    守护进程 xff08 Daemon xff09 是运行在后台的一种特殊进程 它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件 守护进程是一种很有用的进程 php也可以实现守护进程的功能 1 基本概念 进程 每个进程都有一个父
  • clang-format 说明

    转载自 xff1a https www cnblogs com lepeCoder p 8032178 html BasedOnStyle string 这个样式用于所有没有特殊指定配置的选项 这个选项仅在clang format配置中支持
  • 路径规划

    转载 xff1a 链接 xff1a https www jianshu com p f3bab4e119cf D算法 xff0c Dijkstra算法 两个表 xff1a closed表与open表 closed表用于记录已访问过的节点 x
  • awk 筛选并计算时间

    grep 39 filter cost time 39 log file awk F 39 filtered route count 39 39 print 2 39 awk F 39 39 39 print 1 39 awk 39 BEG
  • 忽略批处理中的pause命令

    当存在一个批处理文件 run bat xff0c 其中 xff1a 64 echo 开始暂停 pause 64 echo 暂停结束 当我们执行这个run bat文件时 xff0c 运行到pause会提示需要按任意键继续 xff0c 并且命令
  • BullseyeCoverage 代码覆盖度检查工具

    昨日又有同事问我 BullseyeCoverage 的使用方法 xff0c 由于时间久远 xff0c 有些遗忘 xff0c 只后悔当初为什么没有整理记录下来 xff0c 只好重新查阅了很多文档 今日整理下比较重要的知识 xff0c 方便以后
  • 用于词义消岐的Lesk算法

    该算法由Michael E Lesk于1986年提出 xff0c 是一个基于词典的词义消岐方法 该算法认为 xff1a 一个词在词典中的词义解释与该词所在句子具有相似性 这种相似性可以由相同单词的个数来表示 xff0c 比如 cone 和
  • 通过cmake打印CMakeLists.txt中的宏

    转 cmake中宏的使用 原文路径 https blog csdn net qq 29573053 article details 80392441 首先贴一个例子进行分析 set var 34 ABC 34 macro Moo arg m
  • undefined reference to `vtable for XXXXX`

    vtable 表示的是虚表 这个错误出现时 请检查你的父类所有虚函数是否实现 或者子类是否把父类的虚函数都处理完 注意 析构函数也算
  • CMakeLists.txt ----find_package

    在linux平台下编译程序的时候通常都会使用到CMakeLists txt来制定编译规则 在查找需要链接的lib时候 通常会使用到find package 记录一下我之前用到的地方 find package 之后 最好到FindXXX cm
  • opencv学习笔记1 opencv安装及配置 一劳永逸不需要每次都重新配置

    opencv2 4 9地址 xff1a https sourceforge net projects opencvlibrary files opencv win 2 4 9 opencv 2 4 9 exe download vs2013
  • 【Linux学习笔记】关于ubuntu开机菜单栏和任务栏不见了的有效解决方法

    一 问题描述 ubuntu开机只有桌面 xff0c 没有菜单栏和任务栏 xff0c 如下图 xff1a 二 问题解决 刚学习ubuntu xff0c 总有些像我这样不折腾就不舒服的人 xff0c 今天改了一下主题 xff0c 图标什么的 x
  • 【数据结构与算法】深入浅出递归和迭代的通用转换思想

    深入浅出递归和迭代的通用转换思想 一般来说 xff0c 能用迭代的地方就不要用递归 xff01 理论上讲 xff0c 所有的递归和迭代之间都能相互转换 xff01 刷题碰到 一天一道LeetCode 130 Surrounded Regio
  • 【unix网络编程第三版】阅读笔记(二):套接字编程简介

    unp第二章主要将了TCP和UDP的简介 xff0c 这些在 TCP IP详解 和 计算机网络 等书中有很多细致的讲解 xff0c 可以参考本人的这篇博客 计算机网络 第五版 阅读笔记之五 xff1a 运输层 xff0c 这篇博客就不再赘述