28UDP

Posted gd-luojialin

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了28UDP相关的知识,希望对你有一定的参考价值。

UDP通信流程步骤:

服务端: 等待(被动)接收发送

1: 创建 socket:  socket()

2: 绑定端口:      bind()

3: 读取消息:      read()

4: 发送消息:      write()

5: 关闭套接字:  close()

 

客户端:主动发送接收

1: 创建 socket:   socket()

2: 发送数据:        write()

3: 接受结果:         read()

4: 关闭套接字:     close()

 

UDP通信流程图:

技术分享图片

 

 

UDP通信

1.没有固定连接

2.客户端发完包,就不管了,也不知道服务端是不是收到了

 

UDP创建套接字、绑定套接字的方式与TCP一样,可参考TCP。

 

发送消息

sendto(int sockfd, void* buf,  size_t len,  int flags,

          struct sockaddr *to,  socklen_t tolen);

 

sockaddr由sockaddr_in转换。

UDP 没有accept创建新的通信fd,需要指定目标地址

函数可以用于TCP通信,后面两个参数会忽略

 

接收消息

recvfrom(int sockfd, void *buf,  size_t len ,  int flags,

             sturct sockaddr *from,  socklen_t *fromlen)

 

UDP 没有 accept 函数来获取对端地址,这里增加了2个参数

函数可以用于TCP通信

 

 

例子:

#include<stdio.h>

#include<stdlib.h>

#include<string.h>

#include<sys/socket.h>

#include<netinet/in.h>

#include<arpa/inet.h>

#include<unistd.h>

#define SRV_PORT  8888

#define CLT_PORT  6666

 

void Udp_server()

{

      int fd;

      int iRet;

      struct sockaddr_in addr;

      socklen_t addrlen = sizeof(addr);

      //创建套接字

      fd = socket(PF_INET, SOCK_DGRAM, 0);

      if (fd < 0)

      {

           perror("Fail to socket!");

           return;

      }

 

      addr.sin_family = AF_INET;

      addr.sin_port = htons(SRV_PORT);

      addr.sin_addr.s_addr = htonl(INADDR_ANY);

      //绑定,可以让客户端知道通过什么IP地址和端口号来连接

      iRet = bind(fd, (struct sockaddr*)&addr, addrlen);

      if (iRet)

      {

           perror("Fail to bind!");

           close(fd);

           return;

      }

     

      struct sockaddr_in srcaddr;

      char szBuf[1000];

      char szMsg[] = "[UDP]I Received!";

      while(1)

      {

           //接收,并获取客户端的IP和端口号(struct sockaddr*)&srcaddr

           memset(szBuf, 0, 1000);

           iRet = recvfrom(fd, szBuf, 1000, 0, (struct sockaddr*)&srcaddr, &addrlen);

           if (iRet < 0)

           {

                 perror("Fail to recvfrom!");

                 break;

           }

          

           printf("Recv:%s ", szBuf);  

     

           //发送

           fprintf(stderr,"Echo:");

           scanf("%s",szMsg);

           sendto(fd, szMsg, strlen(szMsg), 0, (struct sockaddr*)&srcaddr, addrlen);

 

      }

      close(fd);

 

      return;

}

 

void Udp_client()

{

      int fd;

      int iRet;

      struct sockaddr_in addr;

      socklen_t  addrlen = sizeof(addr);

 

      fd = socket(PF_INET, SOCK_DGRAM, 0);

      if (fd < 0)

      {

           perror("Fail to socket!");

           return;

      }

      /*

      addr.sin_family = AF_INET;

      addr.sin_port = htons(CLT_PORT);

      addr.sin_addr.s_addr = htonl(INADDR_ANY);

 

      iRet = bind(fd, (struct sockaddr*)&addr, addrlen);

      if (iRet)

      {

           perror("Fail to bind!");

           close(fd);

           return;

      }

      */

      struct sockaddr_in srvaddr;

      char szIp[16] = ;

      int  port;

 

      fprintf(stderr, "Input server IP and port:");

      scanf("%s%d", szIp, &port);

     

      srvaddr.sin_family = AF_INET;

      srvaddr.sin_port = htons((short)port);

      srvaddr.sin_addr.s_addr = inet_addr(szIp);

 

      char szBuf[100];

      char szRcv[1000];

      while(1)

      {

           memset(szBuf, 0, 100);

           fprintf(stderr, "->");

           read(STDIN_FILENO, szBuf, 100);

          

           sendto(fd, szBuf, strlen(szBuf), 0, (struct sockaddr*)&srvaddr, addrlen);

          

           memset(szRcv, 0, 1000);

           iRet = recvfrom(fd, szRcv, 1000, 0, (struct sockaddr*)&srvaddr, &addrlen);

           if (iRet < 0)

           {

                 perror("Fail to recvfrom!");

                 break;

           }

 

           printf("Recv:%s ", szRcv);

 

      }

      close(fd);

 

      return;

          

}

 

int main(int argc, char** argv)

{

      if (argc!=2

         || (strcmp(argv[1], "s") && strcmp(argv[1], "c"))

           )

      {

           printf("Usage: %s [ c | s ] ", argv[0]);  

           printf(" s: For start udp server ");

           printf(" c: For start udp client ");

           return 0;

      }

      if (argv[1][0] == ‘s‘)

      {

           Udp_server();

      }

      else if (argv[1][0] == ‘c‘)

      {

           Udp_client();

      }

      return 0;

}

 

 

UDP打洞:

技术分享图片

打洞就是让对方不需要通过服务器的转换,直接使用公网IP地址进行通信。

解析:

一般的主机都是使用私有IP地址(在不同的局域网中私有IP地址可以重复的),对方主机通过路由路转换成公有IP地址,再进行通信。

打洞,当主机A向某一服务器发送数据时,此时在服务器上,显示的是主机A的公网IP地址和端口号,要做的就是将该IP地址和端口号再一次返回给主机A。那么主机A就知道了自己的公网IP和端口号。此时主机B就可以直接对主机A发送数据,进而可以相互通信。

 

以上是关于28UDP的主要内容,如果未能解决你的问题,请参考以下文章

2021.2.28软考网络工程师-TCP,UDP协议

网络1-28

如果我在 C# 中发送 0 有效载荷数据,udp 数据包的大小是多少?

UDP的最大报文长度

洪流 DHT udp

通过 UDP 实现 ack?