在物理层、数据链路层、网络层解决了主机和主机之间能够发送接收数据,但是在计算机网络中,主机的通信主体还是进程,而传输层则解决应用进程的通信,所谓传输层协议也是端对端协议。
传输层的协议主要有两种:TCP协议和UDP协议
本章这次主要是针对的UDP协议,下一章才是针对TCP协议的具体理解。
在传输层需要了解到一个新的概念:端口号port(可以理解为你要从那个地方传进去),设计了一个端口号来标识主机中的进行网络通信的进程。
port:16bit整形(两个字节)
端口号只在本机上有作用,在不同主句中同一个程序执行,也可以用不同的端口号。
UDP协议(用户数据报协议)(非面向连接)
1. 无连接,不需要提前把两个程序关联起来(想发给谁就发给谁)。
2. 面向报文的传输,应用程序要发送的数据,直接封装在udp数据报中(直接放在udp协议内容 后),既不做拆分也不做合并,直接交给网络层。
3. 不可靠,不保证数据一定能到达,尽最大努力交付。
UDP协议就是在MAC帧的外面在加上一层传输协议,既加上UDP头部,它的头部由以下四部分组成。
源端口:自己主机的端口号
目标端口:要传到那个主机的端口号
下面这张图是一个传输模型
可以看出这是TCP/IP网络模型,因为只有四层,从用户层、传输层、网络层、网络接口层构成。
在操作系统当中,其实网络协议是有写好了的函数库可以使用,只需要选择对应的协议就行。
1、首先我们需要了解udp怎样进行通信的,下图是udp通信流程
清楚了流程,但肯定不懂得后面函数得意义吧,下面来进行函数的细讲:
1、创建套接字(这里就是相当于创建一个包含网络通信协议的套接字文件)
#include
#include
int socket(int somain, int type, int protocol);
参数1:选择那种ip地址进行通信-----网络层 AF_INET IPV4
参数2:选择那种传输-----传输层 SOCK_STREAM TCP SOCK_DGRAM UDP SOCK_RAM 不使用
参数3:套接字协议 0 默认
返回值 成功:返回套接字 失败:返回-1
2、绑定套接字(也就是将自己的本地网络信息绑定到网络协议中)
#include /* See NOTES */
#include
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
参数1;利用函数int socket函数创建的套接字
参数2:自己的IPV4网络信息
struct sockaddr_in
{
sa_family_t sin_family; 地址族:AF_INET/*address family: AF_INET */
in_port_t sin_port;port端口号:当前进程的端口号
struct in_addr sin_addr;ip地址:本地网络地址 /* internet address */
};
struct in_addr
{
uint32_t s_addr;32位整数,ip地址 /* address in network byte order */
};
参数3:结构体大小,直接sizeof(struct addr)
3、这个函数就是发送数据与接收数据(发给谁,接收谁)
UDP发送数据
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);
参数1:要通过套接字发送给谁
参数2:要发送数据的首地址
参数3:发送数据的大小
参数4:标志 0:阻塞等待,发送完成才结束
参数5:const struct sockaddr *dest_addr:结构体地址,要包含目标,接收方的地址、端口
参数6::结构体大小,目标网络信息结构体的大小
返回值 成功:返回发送的字节数 失败:返回-1
UDP接收数据
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);
参数1:int sockfd:套接字,本地套接字携带了本地的网络信息,进行匹配
参数2:void *buf:接收了数据存储的地址
参数3:size_t len:最大接收多少个字节
参数4:int flags:标志 0:阻塞接收,函数一直等待接收了数据,完成接收才结束
参数5:struct sockaddr *src_addr:结构体地址,把发送方的ip、port存储到这个结构 体,得到发送方的信息 如果不需要 填NULL
参数6:socklen_t *addrlen:把结构体大小也存储到这个地址,得到发送方对应的信息大小 如果不需要 填NULL
返回值: 成功:返回接收到的字节数 失败:返回-1
在计算机存储数据中,有大小端之分(大端(高存低地址,低存高地址)小端(高存高地址,低存低地址)),但是在网络上传输时,统一成了大端模式。
在网络上传输的方式是网络字节序 ,而在机器上存储的方式是主机字节序。
则我们需要在传输时将主机字节序转网络字节序 ,网络字节序转主机字节序。
实例:
发送端程序:
#include
#include
#include
#include
#include
#include
//当前进程发送数据到指定主机的指定进程中,然后接收对方的数据
int main()
{
//1、创建套接字------在进程中要使用网络通信协议,得到套接字文件描述符
int sockfd = socket(AF_INET,SOCK_DGRAM,0); //IPV4,UDP协议
//2、绑定套接字,绑定套接字 中 协议中本地的网络信息
//把套接字,绑定本地网络信息
struct sockaddr_in srcaddr;//本地网络信息---源信息
srcaddr.sin_family = AF_INET;//ipv4 地址
srcaddr.sin_port = htons(9999);//当前进程的端口号
srcaddr.sin_addr.s_addr = inet_addr("xxx.xxx.xxx.xxx");//把主机地址转为32位整数
bind(sockfd,(struct sockaddr *)&srcaddr,sizeof(struct sockaddr_in));
//3、发送数据
//目标结构体
struct sockaddr_in destaddr;//目标信息
destaddr.sin_family = AF_INET;
destaddr.sin_port = htons(8888);
destaddr.sin_addr.s_addr = inet_addr("xxx.xxx.xxx.xxx");
while(1)
{
char buf[20],buf2[20];
scanf("%s",buf);//输入了数据
sendto(sockfd,buf,20,0,(struct sockaddr *)&destaddr,sizeof(destaddr));发送
recvfrom(sockfd,buf2,20,0,NULL,NULL);接收
printf("%s\n",buf2);
}
close(sockfd);
}
接收数据程序、
#include
#include
#include
#include
#include
#include
//当前进程接收数据,然后发送给对方
int main()
{
//1、创建套接字------在进程中要使用网络通信协议,得到套接字文件描述符
int sockfd = socket(AF_INET,SOCK_DGRAM,0);
//2、绑定套接字,绑定套接字 中 协议中本地的网络信息
//把套接字,绑定本地网络信息
struct sockaddr_in srcaddr;//本地网络信息---源信息
srcaddr.sin_family = AF_INET;//ipv4 地址
srcaddr.sin_port = htons(8888);//当前进程的端口号
srcaddr.sin_addr.s_addr = inet_addr("xxx.xxx.xxx.xxx");//把主机地址转为32位整数
bind(sockfd,(struct sockaddr *)&srcaddr,sizeof(struct sockaddr_in));
//3、接收数据
//目标结构体
struct sockaddr_in destaddr;//目标信息
destaddr.sin_family = AF_INET;
destaddr.sin_port = htons(9999);
destaddr.sin_addr.s_addr = inet_addr("xxx.xxx.xxx.xxx");
char buf2[20];
while(1)
{
recvfrom(sockfd,buf2,20,0,NULL,NULL); //接收
printf("%s\n",buf2);
sendto(sockfd,buf2,20,0,(struct sockaddr *)&destaddr,sizeof(destaddr)); //发送获取
}
close(sockfd);
}
UDP协议采用的非面向连接(没有连接),所以还是有很多问题的,无连接、不可靠、面向数据报的传输 。而且发送了也不知道发送到了没,缺点之一。