OSI七层模型(传输层)
UDP协议端格式
UDP特点
无连接:知道对端的IP和端⼝号就直接进⾏传输, 不需要建⽴连接
不可靠: 没有确认机制, 没有重传机制; 如果因为网络故障该段⽆法发到对⽅, UDP协议层也不会给应⽤层返回任何错误信息
面向数据报:不能够灵活的控制读写数据的次数和数量
面向数据报:应⽤层交给UDP多⻓的报⽂, UDP原样发送, 既不会拆分, 也不会合并
例:如果发送端调⽤⼀次sendto, 发送100个字节, 那么接收端也必须调⽤对应的⼀次recvfrom, 接收100个字节; ⽽不能循环调⽤10次recvfrom, 每次接收10个字节;
UDP传输的过程
无连接,全双工(UDP的socket既能读,也能写)
关于UDP的缓冲区
UDP没有真正意义上的 发送缓冲区. 调⽤sendto会直接交给内核, 由内核将数据传给网络层协议进⾏后续的传输动作;
UDP具有接收缓冲区. 但是这个接收缓冲区不能保证收到的UDP报的顺序和发送UDP报的顺序⼀致; 如果缓冲区满了, 再到达的UDP数据就会被丢弃;
注意事项:
UDP协议⾸部中有⼀个16位的最⼤⻓度. 也就是说⼀个UDP能传输的数据最⼤⻓度是64K(包含UDP⾸部). 然⽽64K在当今的互联网环境下, 是⼀个⾮常⼩的数字.
如果我们需要传输的数据超过64K, 就需要在应⽤层⼿动的分包, 多次发送, 并在接收端⼿动拼装;
基于UDP的应用层协议
NFS: 网络⽂件系统
TFTP: 简单⽂件传输协议
DHCP: 动态主机配置协议
BOOTP: 启动协议(⽤于⽆盘设备启动)
DNS: 域名解析协议
以下是一个UDP回显服务器编写过程
//linux下运行
/******************** server.c *********************/
#include
#include
#include
#include
#include
#include //socket_in
#include //inet_addr
typedef struct sockaddr sockaddr;
typedef struct sockaddr_in sockaddr_in;
int main(int argc, char* argv[])
{
if(argc != 3)
{
printf("Usage ./server [ip] [port]\n");
return 1;
}
int sock = socket(AF_INET,SOCK_DGRAM,0);//创建文件描述符
//socket 第一个参数 指定协议类型 IPV4
//SCOK_DGRAM 表示创建的socket要使用面向数据报的方式传输数据
//第三个参数 一般填0
if(sock < 0)
{
perror("socket error\n");
}
struct sockaddr_in addr;
addr.sin_family = AF_INET; //版本号
addr.sin_addr.s_addr = inet_addr(argv[1]);//IP地址
//inet_addr 把一个点分十进制的字符串IP地址转换为数字IP地址 刚好是一个网络字节序
addr.sin_port = htons(atoi(argv[2]));
//atoi argv[2](字符串)转化为数字 然后转化为网络序
//先发出的数据是低地址,后发出的数据是高地址 tcp/ip 大端.
//htons表示将16位的短整数从主机字节序转换为网络字节序
int ret = bind(sock,(sockaddr*)&addr,sizeof(addr));//绑定端口号
//第一个参数 名字 第二个参数 地址
//第三个参数 长度
//第二个参数和第三个参数描述addr这个结构体
if(ret < 0)
{
perror("bind\n");
return 1;
}
while(1)
{
sockaddr_in peer;
socklen_t len = sizeof(peer);
char buf[1024] = {};
ssize_t read_size = recvfrom(sock,buf,sizeof(buf) - 1,0,
(sockaddr*)&peer,&len);//读数据
//UDP 面向数据报 故只能用recvfrom
//第一个参数 文件描述符 sock (关联网卡)
//第二个参数 读到的内容放入到缓冲区
//第三个参数 缓冲区大小
//第四个参数 flag 默认0
//第五个参数 对端的IP地址和端口号 存放地方
//第六个参数 缓冲区的最大长度 (保存对端IP地址和端口号)
if(read_size < 0)
{
perror("recvfrom\n");
continue;
}
buf[read_size] = '\0';//不写没有任何问题 初始化为0 前面我们预留了一位'0'
printf("[%s:%d] %s\n",inet_ntoa(peer.sin_addr),
ntohs(peer.sin_port),buf);
//回显服务器
//inet_ntoa 32位的数字IP 转化为点分十进制的字符串IP
//ntons 网络序转主机序
sendto(sock,buf,strlen(buf),0,
(sockaddr*)&peer,sizeof(peer));// UDP 面向数据报
//第一个参数 文件描述符
//第二个参数 缓冲区位置
//第三个参数 发送的大小
//4 flag 0
//5 对端IP地址的起始位置
//6 大小
}
close(sock); //执行不到 保证
return 0;
}
/******************** client.c **********************/
#include
#include
#include
#include
#include
#include //socket_in
#include
typedef struct sockaddr sockaddr;
typedef struct sockaddr_in sockaddr_in;
int main(int argc,char* argv[])
{
//通过命令行参数,指定客户端向哪个服务器发送请求
// ./client [ip] [port]
if(argc != 3)
{
printf("Usage ./cilent [ip] [port]\n");
return 1;
}
int sock =socket(AF_INET,SOCK_DGRAM,0);
if(sock < 0)
{
perror("socket error\n");
return 1;
}
sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr(argv[1]);
server_addr.sin_port = htons(atoi(argv[2]));
while(1)
{
printf(">begin>:");
fflush(stdout);
char buf[1024] = {0};
ssize_t read_size = read(0,buf,sizeof(buf)-1);
//0 stdin 标准输入读数据
//1 stdout
//2 stderr
if(read_size < 0)
{
perror("read error\n");
return 1;
}
if(read_size == 0)//读完啦 读到EOF
{
printf("read done!\n");
return 0;
}
buf[read_size] = '\0';
//2.把数据发给服务器
sendto(sock,buf,strlen(buf),0,(sockaddr*)&server_addr,
sizeof(server_addr));
//3.尝试从服务器读取数据,此时recvfrom不需要知道对端的IP和端口号
char buf_output[1024] = {0};
read_size = recvfrom(sock,buf_output,
sizeof(buf_output)-1,0,NULL,NULL);
//4.写到标准输入上
printf("server resp: %s",buf_output);
}//while
close(sock);
return 0;
}
/******************** makefile ***********************/
.PHONY:all
all:server client
server:server.c
gcc $^ -o $@
client:client.c
gcc $^ -o $@
.PHONY:clean
clean:
rm -f server client
OSI七层模型:
https://blog.csdn.net/Romantic_C/article/details/81665591
网络套接字:
https://blog.csdn.net/Romantic_C/article/details/81704425
UDP服务器:
https://blog.csdn.net/Romantic_C/article/details/81705468
TCP服务器:
https://blog.csdn.net/Romantic_C/article/details/81707907
应用层:
https://blog.csdn.net/Romantic_C/article/details/81714161
传输层:
https://blog.csdn.net/Romantic_C/article/details/81747317
网络层:
https://blog.csdn.net/Romantic_C/article/details/81751255
数据链路层:
https://blog.csdn.net/Romantic_C/article/details/81777844