tcp/ip
学习
TCP/IP的四层协议栈: 应用层,传输层,网络层,链路层
TCP and UDP 传输层的传输协议不同
其他差异可借鉴:传送门
TCP/IP协议栈中的各层的作用
- 物理层:解决的是传输0和1的问题
- 链路层:解决的是数据从源地址到目的地址传输的问题,通过MAC地址在自己的局域网内,以及通过L2交换机维护MAC地址和网口的映射表,来确保子网的数据有序的传输。
问题:既然局域网是用MAC地址就可以通信,为什么还需要IP地址? 局域网用MAC通讯为什么还要IP?
可以这么理解,只用MAC地址是完全可以在局域网通信,但一般的电脑设备没有提供只使用MAC地址来传输数据的“口子”,电脑最终还是要连接互联网的,要连外网必须使用IP地址,索性使用应用层一层一层向下调用,给用户留出统一的接口。 - 网络层:通过IP地址解决的是跨子网之间的数据传输,L3交换机维护路由表。
- 传输层:TCP和UDP协议,端口区别进程。
- 应用层:http ftp
windows 和linux 的不同
头文件:
win <winsock2.h> 需要插入进项目的头文件以codeblocks是插入bilwsock32.a
linux <unistd.h>.<arpa/inet.h>.<sys/socket.h>
套接字类型
- 面向连接 SOCK_STREAM
- 面向消息 SOCK_DGRAM
函数
int socket(int domain, int type, int protocol); 创建套接字
成功返回文件描述符,失败返回-1;
domain 协议族 一般用PF_INFT ipv4互联网协议族
type 传输类型
-
面向连接 SOCK_STREAM 特点 数据不会消失 按顺序 不存在边界
-
面向信息 SOCK_DGRAM 不按顺序 会丢失 有边界 限制大小
protocol 协议信息
- IPPROTO_TCP tcp套接字用于IPv4和面向连接
- IPPROTO_UDP UDP套接字用于IPv4和面向消息
网络地址分类与主机地址边界
首字节
A 类 0~127 开头 0
B 类 128~191开头 10
C 类 192~223 开头 110
字节序htons中
hton~s 表示把short型数据从主机转网络字节序
h:主机host n: 网络network s: short 型(l: long 型)
h和n可互相替换 表示谁转谁
初始化和分配地址
<arpa/inet.h>
int inet_aton(char *string, struct in_addr *addr) 与 in_addr_t inet_addr(char *string) 作用相同
1 成功 0 失败 成功返回32位 大端序整数型,失败返回INADDR_NONE
地址转码本机转网络是颠倒的 也就是小端序转大端序 颠倒方式如下按字节颠倒
0x1234 -> 0x3412
127.232.124.79 ->0x4f7ce87f
用二进制来看就是 0111 1111.1110 1000.0111 1100. 0100 1111 -> 0100 1111 0111 1100 1110 1000 0111 1111
TCP
默认函数调用顺序
服务端: 创建套接字 -> 分配地址 -> 等待连接请求 -> 允许连接 -> 数据交换 -> 关闭
客户端: 创建套接字 -> 请求连接 -> 数据交换 -> 断开连接
UDP
客户端与服务端相同不区分。
特点sendto在判断没有分配过地址时会自动分配地址
函数调用过程: 创建套接字->分配地址->数据交换->关闭
以上属于默认的未连接套接字。
每次注册目标套接字进行传输
同样还有已连接套接字
和TCP一样调用connect即可得到固定的目标套接字,发送消息的时候就也可以用write 和read了
connect 在这里不代表连接,只是在套接字中注册了目标的IP和端口信息
tcp/ip原理
I/O 缓冲
write() 调用时 ,会将数据带入输出缓冲,在适合的时候传入对方输入缓冲
read() 调用时, 会读取输入缓冲的数据。
特性:
- I/O缓冲在每个tcp套接字中单独存在
- 在创建套接字时自动生成
- 关闭套接字也会继续传递输出缓冲遗留的数据
- 关闭套接字会丢失输入缓冲的数据
不会发生超过输入缓冲大小的数据传输:
TCP中有滑动窗口协议-》相当与一次对话
套接字主要经过:
与对方建立连接
与对方数据交换
断开连接
建立连接:三次握手
也就是TCP在建立连接时的三次对话:
客户端先发请求,服务端接收并回复准备就绪,客户端再次回复收到受理答复。
收发数据前的准备:建立连接
客户端首次请求连接时用的消息 称SYN:SEQ:A数据包编号, ACK:-
接下来到服务端回复: SYN+ACK : SEQ:B数据包编号, ACK:A+1 也就是概述接收到并且提醒你下次要传递的编号,同时告诉自己的。
客户端再次回复:ACK:SEQ: A+1, ACK:B+1
SEQ 表示自己所发的数据包编号
ACK 表示告知你收到对方的时ACK-1 同时告诉对方下一个该发ACK编号的数据包了
数据交换:回复一下即可
发送者先发送 SEQ: 数据包编号
接收者回复 ACK:收到的编号+字节数+1 同时也表示告诉发送者下次发送的编号,和所收到消息的回应。
如果中途没收到超时会重发-------
断开连接:四次握手
本方要断开的先发送 FIN:SEQ A同上 ACK -
另一方回复:ACK : SEQ B同上 ACK:A+1
另一方准备后再次发送: FIN : SEQ B+1 ACK: A+1
最后本方 回复: ACK SEQ A+1 ACK:B+2
同样以编号大小来得知情况
优美的断开套接字连接
主要断开连接函数有:
shutdown(int sock, int howto) 关闭一个流
主要参数有:sock 套接字
howto 关闭的流类型
SHUT_RD 断开输入流 会使之没有数据能进来
SHUT_WR 输出流 会使之不能出去
SHUT_RDWR 断开两个 两个都不能
close(int sock) 关闭套接字
优美的方法就是在发送完自己的数据后
先关闭输出流,再执行读操作在关闭所有。。。