UDP 理解
创始人
2024-05-24 17:06:58
0

这里需要指出的一点是,伪首部完全是虚拟的,它并不会和用户数据报一起被发送出去,只是在校验和的计算过程中会被使用到,伪首部主要来自于运载UDP报文的IP数据报首部,将源IP地址和目的IP地址加入到校验和的计算中可以验证用户数据报是否已经到达正确的终点。

所以udp头部大小为8字节。

tcp和udp可以同用一个端口。使用地址复用SO_REUSEADDR即可。

udp通用函数

// 接收缓冲区,udp有接收缓冲区,无发送缓冲区

int nRecvBuf=32*1024;//设置为32K

setsockopt(s,SOL_SOCKET,SO_RCVBUF,(const char*)&nRecvBuf,sizeof(int));

//发送缓冲区

int nSendBuf=32*1024;//设置为32K

setsockopt(s,SOL_SOCKET,SO_SNDBUF,(const char*)&nSendBuf,sizeof(int));

//地址复用

BOOL bReuseaddr = TRUE;

setsockopt( s, SOL_SOCKET, SO_REUSEADDR, ( const char* )&bReuseaddr, sizeof( BOOL ) );

接收端

1.创建,socket()函数

2.绑定, bind()函数

3.接收 recvfrom

不用实际读取就能检测到数据的到来,可以调用MSG_PEEK标志的recv活recvfrom,或者调用ioctlsocket或select.

ssize_t recvfrom(int sockfd,void *buf,size_t len,unsigned int flags, struct sockaddr *from,socket_t *fromlen);

sockfd:标识一个已连接套接字的描述字。

buf:接收数据缓冲区。

len:缓冲区长度。

flags:调用操作方式。一般为0, 是一个或者多个标志的组合体,可通过“ | ”操作符连在一起:

from:指针,指向装有源地址的缓冲区。可选,如不关心,则为null

fromlen:指针,指向from缓冲区长度值。可选,如不关心,则为null

返回值

如果正确接收返回接收到的字节数,失败返回-1.返回0也是可行的。tcp的read返回0表示对端已关闭,而udp不是。

udp和tcp都有接收缓冲区,在没有收到数据时,默认是阻塞的,可通过设置超时返回(如select机制,默认最大描述符为1024)

延伸connect

1.没有3路握手,只是检查是否存在立即可知的错误(如网络不可达,但如果是网络可达,但应用程序没启动检查不到的,只有调sendto才返回)

2.如果udp调用connect后,不能使用sendto(或者不能在sendto制定目的地址)

3.如果udp调用connect后,只能接受connect所指定地址的数据报,不能接受别的套接字的数据。

相当于不能进行广播和组播了

4.好处:就是只显性连接一次,可以多次发送数据。传输效率更高。

不然调用sendto就是连接一次,发送一次,断开连接一次。再连接,发送,断开连接

5.多次调用connect,可以指定新的目的地址和端口和断开套接字

关闭closesocket() :

只是将socket的资源归还给协议栈。

发送端:

1.创建,socket()函数

2.发送 sendto ()函数:

适合在从同一个socket向不同的远程主机发送数据。

或还有一种办法是,调用connect()函数,再调用send()函数。适用于向同一个远程地址发送数据。

int sendto ( socket s , const void * msg, int len, unsigned int flags, const struct sockaddr * to , int tolen ) ;

send 和 sendto 函数在 UDP 层没有输出缓冲区,因此sendto不会阻塞。而是直接返回。

s 套接字

buff 待发送数据的缓冲区

size 缓冲区长度

Flags 调用方式标志位, 一般为0, 改变Flags,将会改变Sendto发送的形式

addr (可选)指针,指向目的套接字的地址

len addr所指地址的长度

成功则返回实际传送出去的字符数,失败返回-1,返回是0也是可以的。

udp并没有真正的发送缓冲区,和tcp还是有区别的。

对客户端的udp而言,进程首次调用sendto时,绑定一个临时端口,不能在修改了。而ip地址可以随客户发送的udp数据报而变动。

udp sendto输出操作成功返回仅仅表示在接口输出队列中具有存放所形成ip数据报的空间。

说明了udp套接字:由它引发的异步错误并不返回给它,除非它也连接(即如调用了connect函数)。这也是udp使用connect的初衷。

主要区别在于:udp获取目标ip地址的方法是recvmsg函数。

在客户端udp中,调用connect并没有和tcp的三路握手,只能是内核检查存放立即可知的错误,记录对端的ip地址和端口,然后立即返回到进程。如果使用了connect函数,就只能使用read(recv)替代recvfrom ,write(send)替代sendto.

udp可以多次调用connect,其目的是指定新的ip地址和端口,或断开套接字。tcp只能一次调用connect。

调用connect并不给服务器发送任何信息,只是保存对端的ip地址和端口号。

tcp和udp可以同用一个端口。

对于已连接的udp套接字可以调用sendto,但不能指定目的地址。

对于已连接的udp套接字只能通过connect断开连接,而不能通过shutdown.

3.关闭closesocket

主要在udp中,可以扩展,从单播到组播,或广播。可以在同一应用程序同时接收和发送。

udp套接字显性地绑定一个本地ip接口,并发送数据。出现什么情况

udp套接字不会真正和网络接口绑定在一起,而是建立一种关联,即被绑定的ip接口地址成为发出去的udp数据报的源ip地址。

一个数据报即udp只要一打开就处于可写的状态,一经命名就处于可读的状态。

所以,应用程序下socket()调用后,马上可以发送数据。

在调用bind()显式命名或调用sendto函数隐式命名后,马上就可以接收数据。

对于udp多播,发送应用进程的套接字可以不必加入到多播组。

udp广播

一直都UDP层(通过端口号),才能确定是否要丢弃广播数据。

发送端:

1.创建udp套接字,无须绑定端口和地址。

2.设置udp套接字的广播标志。

int flag = 1;

setsockopt(server_sockfd , SOL_SOCKET , SO_BROADCAST , &flag , sizeof(flag) );

3.调用sendto函数,参数中需要明确广播地址和端口号。

#define BROADCAST_IP "192.168.1.255"
#define CLIENT_PORT 9000bzero(&clientaddr , sizeof(clientaddr));
clientaddr.sin_family = AF_INET;
inet_pton(AF_INET , BROADCAST_IP , &clientaddr.sin_addr.s_addr);clientaddr.sin_port = htons(CLIENT_PORT); 
sendto(server_sockfd , buf , strlen(buf) , 0 , (struct sockaddr *)&clientaddr , sizeof(clientaddr));

接收端:

  1. 创建udp套接字,必须绑定端口(该端口是发送端发送的端口)。绑定的IP不可以使用“127.0.0.1”,可以使用真实IP地址或者INADDR_ANY。否则接收失败。

 bzero(&localaddr , sizeof(localaddr));localaddr.sin_family = AF_INET;
inet_pton(AF_INET , "0.0.0.0" , &localaddr.sin_addr.s_addr);localaddr.sin_port = htons(CLIENT_PORT);int ret = bind(confd , (struct sockaddr *)&localaddr , sizeof(localaddr));

2.接收方的Socket不需要设置成广播属性。

3.调用recvfrom函数进行接收.

len = recvfrom(confd , buf , sizeof(buf),0 ,(struct sockaddr*)&from,(socklen_t*)&len);

//from为发送端的本地地址。

相关的广播知识点:

BOOL bBroadcast=TRUE;

setsockopt(s,SOL_SOCKET,SO_BROADCAST,(const char*)&bBroadcast,sizeof(BOOL));

特点:

1.广播的数据在子网的所有主机都接收。直到传输层才决定是否丢弃。

2.不能够跨越不同的网络,被路由器所隔离开,即只能在局域网,不能应用到广域网

3.接收端的端口号要与广播端绑定的端口号一样。

udp组播

组播在网卡处就对接收地址进行判断,从而丢弃数据包。

一个ip地址可以加入到多个组播组。

1.地址范围:D类IP地址。范围:224.0.0.0~239.255.255.255

2.组播组:永久/临时。永久组播组一般由官方分配。

3.224.0.0.0~224.0.0.255为预留的组播地址,即永久组地址。地址224.0.0.0保留不做分配,其它地址供路由协议使用。

4.224.0.1.0~224.0.1.255是公用组播地址,可以用于Internet。

5.224.0.2.0~238.255.255.255为用户可用的组播地址(临时组地址),全网范围内有效。

6.239.0.0.0~239.255.255.255为本地管理组播地址,仅在特定的本地范围内有效。

发送端:

1.创建udp套接字,无须绑定端口和地址。

2.调用sendto函数,参数中需要明确组播地址和端口号。

#include 
#include 
#include 
#include 
#include 
#include 
#include int main()
{int server = 0;struct sockaddr_in saddr = {0};int client = 0;struct sockaddr_in remote = {0};socklen_t asize = 0;int len = 0;char buf[32] = "Software";int r = 0;//int brd = 1;server = socket(PF_INET, SOCK_DGRAM, 0);if( server == -1 ){printf("server socket error\n");return -1;}saddr.sin_family = AF_INET;saddr.sin_addr.s_addr = htonl(INADDR_ANY); // 本机地址saddr.sin_port = htons(8888);if( bind(server, (struct sockaddr*)&saddr, sizeof(saddr)) == -1 ){printf("udp server bind error\n");return -1;}printf("udp server start success\n");remote.sin_family = AF_INET;remote.sin_addr.s_addr = inet_addr("224.1.1.168"); //设置一个多播地址remote.sin_port = htons(9000);while( 1 ){len = sizeof(remote);r = strlen(buf);sendto(server, buf, r, 0, (struct sockaddr*)&remote, len);sleep(1);}close(server);return 0;
}

接收端:

1.创建套接字,必须绑定端口(该端口是发送端发送的端口)。绑定的IP不可以使用“127.0.0.1”,可以使用真实IP地址或者INADDR_ANY。否则接收失败。

2.把当前本地的ip地址加人到组播地址。

3.调用recvfrom获取数据,参数为发送端的本地地址。

#include 
#include 
#include 
#include 
#include 
#include 
#include int main()
{int sock = 0;struct sockaddr_in addr = {0};struct sockaddr_in remote = {0};int len = 0;char buf[128] = {0};char input[32] = {0};int r = 0;//多播struct ip_mreq group={0};sock = socket(PF_INET, SOCK_DGRAM, 0);if( sock == -1 ){printf("socket error\n");return -1;}addr.sin_family = AF_INET;addr.sin_addr.s_addr = htonl(INADDR_ANY);addr.sin_port = htons(9000);if( bind(sock, (struct sockaddr*)&addr, sizeof(addr)) == -1 ){printf("udp bind error\n");return -1;}//remote.sin_family = AF_INET;//remote.sin_addr.s_addr = inet_addr("127.0.0.1");//remote.sin_port = htons(8888);group.imr_multiaddr.s_addr=inet_addr("224.1.1.168");group.imr_interface.s_addr=htonl(INADDR_ANY); //local host//这里INADDR_ANY 为0.0.0.0 通过看ipconfig/ifconfig 可以看到有多个//网络ip地址,这个时候让操作系统选择哪一个端口进行多播数据收发。//在实际的工程中需要明确指定需要哪一个网络地址进行多播数据收发,//不能完全依赖操作系统,否者有时候能够收到数据,有时候收不到数据。setsockopt(sock,IPPROTO_IP,IP_ADD_MEMBERSHIP,&group,sizeof(group));while( 1 ){len=sizeof(remote);r = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr*)&remote, &len);if( r > 0 ){buf[r] = 0;printf("Receive: %s\n", buf);}else{break;}}close(sock);return 0;
}

多播(组播)相关知识点:

ip多播:采用的是“无根”的通讯方式,组内的成员可以相互之间发送和接收数据。IPv4 多播是一个D类的IP地址,范围:224.0.0.0 ----239.255.255.255 之间。

如果多播需要在多个网络断传输,需要考虑使用IGMP协议,该协议运行在主机和组播路由器之间。此外一个套接字注意TTL的值,其作用主要显示数据能传输多远。

1.多播服务端针对特定多播地址只发送一次数据,但是组内的所有客户端都能收到数据

也就是说如果自身不想接受数据,就不要把自己加入到组播地址去。

2.加入特定的多播组即可接收发往该多播组的数据。如果自己想接收数据,就把自己加入组播地址

3.与单播一样,多播是允许在广域网即Internet上进行传输的,而广播仅仅在同一局域网上才能进行;即可以跨网段,跨路由器.所以多播时,路由器能够复制数据并进行转发

常用函数

IPPROTO_IP

当接收者加入到一个多播组以后,再向这个多播组发送数据,这个字段的设置是否允许再返回到本身。

int loop=1; //1:on 0:off

setsockopt(sock,IPPROTO_IP,IP_MULTICAST_LOOP,&loop,sizeof(loop));

IP_MULTICAST_TTL

默认情况下,多播报文的TTL被设置成了1,也就是说到这个报文在网络传送的时候,它只能在自己所在的网络传送,当要向外发送的时候,路由器把TTL减1以后变成了0,这个报文就已经被Discard了。

IP_MULTICAST_IF设置多播接口地址

struct in_addr addr;

setsockopt(s,IPPROTO_IP,IP_MULTICAST_IF,&addr,sizeof(addr))

IP_ADD_MEMBERSHIP 加入一个组播组

struct ip_mreq ipmr;

ipmr.imr_interface.s_addr = inet_addr("192.168.101.1");

ipmr.imr_multiaddr.s_addr = inet_addr("234.5.6.7");

setsockopt(s, IPPROTO_IP, IP_ADDR_MEMBERSHIP, (char*)&ipmr, sizeof(ipmr));

IP_DROP_MEMBERSHIP 离开一个多播组

相关内容

热门资讯

喜欢穿一身黑的男生性格(喜欢穿... 今天百科达人给各位分享喜欢穿一身黑的男生性格的知识,其中也会对喜欢穿一身黑衣服的男人人好相处吗进行解...
发春是什么意思(思春和发春是什... 本篇文章极速百科给大家谈谈发春是什么意思,以及思春和发春是什么意思对应的知识点,希望对各位有所帮助,...
网络用语zl是什么意思(zl是... 今天给各位分享网络用语zl是什么意思的知识,其中也会对zl是啥意思是什么网络用语进行解释,如果能碰巧...
为什么酷狗音乐自己唱的歌不能下... 本篇文章极速百科小编给大家谈谈为什么酷狗音乐自己唱的歌不能下载到本地?,以及为什么酷狗下载的歌曲不是...
华为下载未安装的文件去哪找(华... 今天百科达人给各位分享华为下载未安装的文件去哪找的知识,其中也会对华为下载未安装的文件去哪找到进行解...
怎么往应用助手里添加应用(应用... 今天百科达人给各位分享怎么往应用助手里添加应用的知识,其中也会对应用助手怎么添加微信进行解释,如果能...
家里可以做假山养金鱼吗(假山能... 今天百科达人给各位分享家里可以做假山养金鱼吗的知识,其中也会对假山能放鱼缸里吗进行解释,如果能碰巧解...
四分五裂是什么生肖什么动物(四... 本篇文章极速百科小编给大家谈谈四分五裂是什么生肖什么动物,以及四分五裂打一生肖是什么对应的知识点,希...
一帆风顺二龙腾飞三阳开泰祝福语... 本篇文章极速百科给大家谈谈一帆风顺二龙腾飞三阳开泰祝福语,以及一帆风顺二龙腾飞三阳开泰祝福语结婚对应...
美团联名卡审核成功待激活(美团... 今天百科达人给各位分享美团联名卡审核成功待激活的知识,其中也会对美团联名卡审核未通过进行解释,如果能...