开篇语

前两年, 就买了《TCP/IP网络编程》这本书, 由于自身基础薄弱, 只是走马观花翻阅了几张。

后来工作了这些年, 越来越感到瓶颈期已经来临, 再花式的 curd 也俘获不了领导的芳心了。

于是, 打算仔细学习下 《TCP/IP网络编程》, 为了让自己更深刻记忆, 特做笔记。

创建套接字(socket)

#include <sys/scoket.h>int socket(int domain, int type, int protocol)domain : 套接字中实用的协议族信息
type : 套接字数据传输类型信息
protocol : 计算机通信中实用的协议信息
复制代码

1. domain参数 协议族

名称 协议族
PF_INET IPv4互联网协议族
PF_INET6 IPv6互联网协议族
PF_LOCAL 本地通信unix协议族
... ...

2. type参数 套接字类型

2.1 面向链接的套接字类型 (SOCK_STREAM)

传输方式特征:

1.1 传输过程数据不会丢失
1.2 按序传输数据
1.3 不存在数据边界
复制代码

这几个特性其实就是我们常说的 TCP协议。

缓冲区概念:

收发数据的套接字内部有缓冲(buffer), 简言之就是字节数组. 通过套接字传输的数据将保存到该数组。 因此, 我们 read、write其实读取缓冲区的内容。

那么当缓冲区满, 会发生什么情况呢。 在ICP/IP网络编程书中介绍, 如果read函数读取的速度比接收数据的速度慢, 则缓冲区有可能填满。 此时套接字将无法再接收数据, 传输端套接字将停止传输。

2.2 面向消息的套接字类型 (SOCK_STREAM)

传输方式特征:

1. 强调快速传输而非传输顺序
2. 传输数据可能丢失也可能毁损
3. 传输的数据存在数据边界
复制代码

其实就是我们常说的UDP协议

3. protocol参数 协议最终选择

这里我们不做选择, 为0即可。

4. 最终我们使用TCP链接模式写法

//创建套接字(IPv4协议族, TCP套接字, TCP协议)
int sock = socket(PF_INET, SOCK_STREAM, 0);
复制代码

返回的为 文件描述符, 失败返回-1

向套接字分配网络地址(bind)

#include <sys/socket.h>int bind(int socketfd, struct sockaddr *myaddr, socklen_t addrlen);socketfd 要分配的套接字文件描述符
myaddr  存储地址信息的结构体变量地址值
addrlen 第二个结构体变量的长度
复制代码

1. socketfd 参数

socketfd 不用多说, 即是我们的socket函数返回的文件描述符

2. myaddr 参数

我们看到他是一个 sockaddr 结构体的指针类型。

sockaddr结构体:

struct sockaddr {__uint8_t       sa_len; sa_family_t     sa_family; //地址组char            sa_data[14]; //地址信息
};
复制代码

在sa_data一个成员里,包含了ip、port的地址信息, 这样写起来很麻烦, 所以有了新的结构体 sockaddr_in (IP和端口进行了拆分)

sockaddr_in结构体

struct sockaddr_in {__uint8_t       sin_len;sa_family_t     sin_family; //地址族in_port_t       sin_port; // TCP/UDP端口号struct  in_addr sin_addr; //IP地址char            sin_zero[8];
};
复制代码

在上面的结构体中, 又嵌套了 in_addr 结构体,记录 IP 地址

struct in_addr {in_addr_t s_addr; //32位IPv4地址
};
复制代码

结构体 sockaddr_in 的成员分析

成员 sin_family
地址族 含义
AF_INET IPv4互联网使用的地址族
AF_INET6 IPv6互联网使用的地址族
AF_LOCAL 本地通信unix使用的地址族
... ...
成员 sin_port

16位端口号

成员 sin_addr

32位 ip 地址信息, 以网络字节序保存

成员 sin_zero

无特殊含义, 为与sockaddr 大小保持一致, 写入0 即可。

3. addrlen参数

传递地址信息的长度

4. 最终我们使用bind绑定地址方式

//分配内存-构造服务端地址端口
memset(&serv_addr, 0, sizeof(serv_addr));
//IPv4中的地址族
serv_addr.sin_family = AF_INET;
//32位的IPv4地址, INADDR_ANY表示当前ip
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
//16位tcp/udp端口号
serv_addr.sin_port = htons(atoi(argv[1]));  //分配地址
if (bind(serv_sock, (struct sockaddr*) &serv_addr,sizeof(serv_addr) )==-1){printf("bind() error");exit(0);
}
复制代码

bind函数之前, 构造了 sockaddr_in 结构体的数据, 其中介绍几个点.

  1. INADDR_ANY 会自动获取当前服务器的IP
  2. 我们看到使用到了 htonl、htons 函数,构造IP地址和端口

为什么构造结构体地址时候使用了 htonl、htons对IP、端口进行了转换

首先我们来看下这几个函数的含义

地址族 含义
htons 把short型数据从主机字节序转化为网络字节序
htonl 把long型数据从主机字节序转化为网络字节序
ntohs 把short型数据从网络字节序转化为主机字节序
ntohl 把long型数据从网络字节序转化为主机字节序
... ...

数据传输采用的网络字节序, 那在传输前应直接把数据转换成网络字节序, 接收的数据也需要转换城主机字节序再保存 上面这句话是有问题的, 原因是数据收发过程中是有自动转换机制的.

除了 socketaddr_in 结构体变量手动填充数据转换外, 其他情况不需要考虑字节序问题。

说了这么多字节序, 那到底什么是网络字节序,什么是主机字节序

1.主机字节序:主机内部内存中数据的处理方式。

2.网络字节序:网络字节顺序是TCP/IP中规定好的一种数据表示格式,它与具体的CPU类型、操作系统等无关,从而可以保证数据在不同主机之间传输时能够被正确解释。网络字节顺序采用big endian(大端)排序方式。

天啦撸, 大端又是啥, 我们从两种网络字节顺序说起

字节序:是指整数在内存中保存的顺序。

cpu向内存保存数据字节序有两种实现方式:

  • 小端字节序(little endian):低字节数据存放在内存低地址处,高字节数据存放在内存高地址处。

  • 大端字节序(bigendian):高字节数据存放在低地址处,低字节数据存放在高地址处。

图例:

大字节序更符合我们的阅读习惯。但是我们的主机使用的是哪种字节序取决于CPU,不同的CPU型号有不同的选择。

当我们两台计算机是需要网络通信时, 规范统一约定为大端序进行通讯处理.

###客户端代码分析 我们在服务端设置ip时候, 使用了 INADDR_ANY 会自动获取当前服务器的IP, 我们看下客户端的连接代码

struct sockaddr_in serv_addr;
struct sockaddr_in clnt_addr;char message[30];
//创建套接字(IPv4协议族, TCP套接字, TCP协议)
int sock = socket(PF_INET, SOCK_STREAM, 0);
if (sock == -1 ){printf("socket() error");exit(1);
}//分配内存-构造服务端地址端口
memset(&serv_addr, 0, sizeof(serv_addr));
//IPv4中的地址族
serv_addr.sin_family = AF_INET;
//32位的IPv4地址
serv_addr.sin_addr.s_addr = inet_addr(argv[1]);
//16位tcp/udp端口号
serv_addr.sin_port = htons(atoi(argv[2]));  if (connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr))) {printf("connect() error");exit(1);
}int length = read(sock, message, sizeof(message)-1);
if (length==-1){printf("read() error");exit(1);
}
复制代码

知识点1

设置服务端 serv_addr.sin_addr.s_addr 地址, 使用了函数 inet_addr

int_addr_t inet_addr(const char * string);
//成功时32位大端序整数值, 失败时返回 INADDR_NONE.
复制代码

例:

printf("%d",inet_addr("192.168.2.1"));
//output: 16951488
printf("%d",inet_addr("192.168.2.256"));
//output: -1
复制代码

相同功能函数, 只是简化了向 serv_addr.sin_addr.s_addr 赋值操作

int inet_aton(const char *string, struct in_addr * addr);
//成功时返回1(true) 失败时返回0(false)
inet_aton(addr, &addr_inet.sin_addr)
复制代码

其他函数:

char * inet_ntao(struct in_addr adr);
//成功时返回转换的字符串地址值, 失败时返回-1.
复制代码

知识点2

● atoi():将字符串转换为整型值。

● atol():将字符串转换为长整型值。

printf("%d",atoi("123"));
//output : 123
复制代码

对比服务端、客户端构造地址代码

服务端

serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
//16位tcp/udp端口号
serv_addr.sin_port = htons(atoi(argv[1]));
复制代码

客户端

//32位的IPv4地址
serv_addr.sin_addr.s_addr = inet_addr(argv[1]);
//16位tcp/udp端口号
serv_addr.sin_port = htons(atoi(argv[2]));
复制代码

这里面包含上面讲到的一些知识点.

  1. 服务端因为使用INADDR_ANY实际等于 inet_addr("0.0.0.0"), 获取本机的IP地址
  2. 因为客户端接收了字符串IP地址, 所以使用了显示 inet_addr, 返回32位大端序整型数值
  3. htons 将短整型转换为网络字节序, 对于端口来说是比较合适的, 而对于IP类转换的整型数值, 一般需要 htonl 进行转换

参考资料:

《TCP/IP 网络编程》

blog.csdn.net/stalin_/art…

转载于:https://juejin.im/post/5cc5b6596fb9a031fb2cdc2f

TCP/IP网络编程-前三章学习笔记相关推荐

  1. TCP/IP网络编程 第三章 地址族与数据序列

    地址族与数据序列 目录 分配给套接字的IP地址和端口号 地址信息的表示 网络字节序与地址变换 网络地址的初始化与分配 基于Windows的实现 1 分配给套接字的IP地址和端口号 1.1 网络地址 1 ...

  2. TCP/IP 网络编程 (三)

    server端未处理高并发请求通常採用例如以下方式: 多进程:通过创建多个进程提供服务 多路复用:通过捆绑并统一管理 I/O 对象提供服务 多线程:通过生成和客户端等量的线程提供服务 多进程serve ...

  3. 《TCP/IP网络编程》第20章

    <TCP/IP网络编程>第20章 同步方法分类及CRITICAL_SECTION同步 用户模式(User mode)和内核模式(Kernal mode) 用户模式同步 内核模式同步 基于C ...

  4. TCP/IP网络编程之基于TCP的服务端/客户端(一)

    TCP/IP网络编程之基于TCP的服务端/客户端(一) 理解TCP和UDP 根据数据传输方式的不同,基于网络协议的套接字一般分为TCP套接字和UDP套接字.因为TCP套接字是面向连接的,因此又称为基于 ...

  5. TCP/IP网络编程(一)

    TCP/IP网络编程读书笔记 第1章 理解网络编程和套接字 1.1 理解网络编程和套接字 1.1.1 构建打电话套接字 1.1.2 编写 Hello World 套接字程序 1.2 基于Linux的文 ...

  6. 计算机tcpip网络原理与应用,清华大学出版社-图书详情-《TCP/IP网络编程原理与技术》...

    前言 随着Internet的发展,网络技术已经渗透到人们的生活和工作中.TCP/IP已经成为最流行的网络协议,且还在演变以满足未来的需要.在速度越来越快的计算机硬件和不断更新的软件发展的背后,TCP/ ...

  7. TCP/IP网络编程---Linux系统下的TCP套接字编程

    目录 第一章 理解网络编程和套接字 1.1 网络编程和套接字概要 1.2 基于Linux的文件操作 1.2.1 底层文件访问和文件描述符 1.2.2 打开文件 1.2.3 关闭文件 1.2.4 将数据 ...

  8. TCP/IP网络编程:P3->地址族与数据序列

    本系列文章为<TCP/IP网络编程----尹圣雨>学习笔记,前面的系列文章链接如下 TCP/IP网络编程:P1->理解网络编程和套接字 TCP/IP网络编程:P2->套接字类型 ...

  9. TCP/IP网络编程之基于TCP的服务端/客户端(二)

    回声客户端问题 上一章TCP/IP网络编程之基于TCP的服务端/客户端(一)中,我们解释了回声客户端所存在的问题,那么单单是客户端的问题,服务端没有任何问题?是的,服务端没有问题,现在先让我们回顾下服 ...

  10. TCP/IP网络编程之四书五经

    TCP/IP网络编程之四书五经 孟岩 TCP/IP协议是目前广域网和局域网通用的网络协议,因此,基于TCP/IP的编程就格外重要.从 应用上来说,现在直接利用C层次Socket API进行TCP/IP ...

最新文章

  1. 虽然这些代码很少,就几行,但却很牛逼!
  2. 我的XGBoost学习经历及动手实践
  3. leveldb demo
  4. WEB开发中的会话控制
  5. Open WebRTC Toolkit实时视频分析系统
  6. ambari hdfs 启动报错_HDFS 运维常见问题处理
  7. 平衡二叉树中需要旋转的情况
  8. 敏捷BI与数据驱动机制
  9. DOS7.1安装与学习
  10. 基于Java的Office 系列文档处理五种工具简单介绍
  11. 为保证云应用及虚拟串口功能正常使用,请务必下载安装Windows系统环境补丁
  12. UE5 C++ Rider 编程指南 1.编辑器基础
  13. 明天去不了第三届网志大会
  14. 用户 用户组 切换用户
  15. RMAN Encrypted Backups
  16. Ubuntu 16.04 系统 gflags glog 安装
  17. oracle数据库 参考文献,数据库参考文献格式
  18. 【Niagara 03】Tridium N4使用——时间表生成
  19. rock带你读CornerNet-lite系列源码(二)
  20. 笨方法学Python(1-5)

热门文章

  1. uni-app 开发微信,支付宝小程序
  2. react-native升级到0.63ios图片不展示
  3. Python的继承与多继承
  4. php接受fromdata,php接收form-data形式的多文件的问题
  5. PHP自定义状态码数组
  6. Js查找数组中元素的位置
  7. Vue.js 中取得后台原生HTML字符串 原样显示问题的解决方法
  8. PHP将图片转换成base64编码,hash函数
  9. 评微软裁员测试:自动化测试并不能代替人工
  10. 上下五千年,人口十几亿。在这遇见你,缘份真神奇