Linux C/C++ UDP Socket通信实例

环境:Linux

语言:C/C++

通信方式:UDP

概念层级

UDP中的服务器端和客户端没有连接

UDP 不像 TCP,无需在连接状态下交换数据,因此基于 UDP 的服务器端和客户端也无需经过连接过程。也就是说,不必调用 listen() 和 accept() 函数。UDP 中只有创建套接字的过程和数据交换的过程。

UDP服务器端和客户端均只需1个套接字

TCP 中,套接字是一对一的关系。如要向 10 个客户端提供服务,那么除了负责监听的套接字外,还需要创建 10 套接字。但在 UDP 中,不管是服务器端还是客户端都只需要 1 个套接字。之前解释 UDP 原理的时候举了邮寄包裹的例子,负责邮寄包裹的快递公司可以比喻为 UDP 套接字,只要有 1 个快递公司,就可以通过它向任意地址邮寄包裹。同样,只需 1 个 UDP 套接字就可以向任意主机传送数据。

基于UDP的接收和发送函数

创建好 TCP 套接字后,传输数据时无需再添加地址信息,因为 TCP 套接字将保持与对方套接字的连接。换言之,TCP 套接字知道目标地址信息。但 UDP 套接字不会保持连接状态,每次传输数据都要添加目标地址信息,这相当于在邮寄包裹前填写收件人地址。

代码层级

服务器端的步骤如下:

  1. socket: 建立一个socket

  2. bind: 将这个socket绑定在某个端口上(AF_INET)

  3. recvfrom: 如果没有客户端发起请求,则会阻塞在这个函数里

  4. close: 通信完成后关闭socket

客户端的步骤如下:
5. socket: 建立一个socket

  1. sendto: 向服务器的某个端口发起请求(AF_INET)

  2. close: 通信完成后关闭socket

基于UDP的接收和发送函数

创建好 TCP 套接字后,传输数据时无需再添加地址信息,因为 TCP 套接字将保持与对方套接字的连接。换言之,TCP 套接字知道目标地址信息。但 UDP 套接字不会保持连接状态,每次传输数据都要添加目标地址信息,这相当于在邮寄包裹前填写收件人地址。

int recvfrom(int sockfd, void * buf, size_t len, int flags, struct sockaddr * src_addr, socklen_t * addrlen);

int sendto(int sockfd, const void * buf, size_t len, int flags, const struct sockaddr * dest_addr, socklen_t addrlen);

UDP套接字不会保持连接状态,每次传输数据都要添加目标地址信息,这相当于在邮寄包裹前填写收件人地址。

recvfrom用于接收数据,sendto用于发送数据

recvfrom:

  • sockfd:用于接收UDP数据的套接字;
  • buf:保存接收数据的缓冲区地址;
  • len:可接收的最大字节数(不能超过buf缓冲区的大小);
  • flags:可选项参数,若没有可传递0;
  • src_addr:存有发送端地址信息的sockaddr结构体变量的地址;
  • addrlen:保存参数 src_addr的结构体变量长度的变量地址值。

sendto:

  • sockfd:用于传输UDP数据的套接字;
  • buf:保存待传输数据的缓冲区地址;
  • len:带传输数据的长度(以字节计);
  • flags:可选项参数,若没有可传递0;
  • dest_addr:存有目标地址信息的 sockaddr 结构体变量的地址;
  • addrlen:传递给参数 dest_addr的地址值结构体变量的长度。

UDP客户/服务器的套接字函数

在这里插入图片描述

Server.cpp

#include <stdio.h>   
#include <sys/types.h>   
#include <sys/socket.h>   
#include <netinet/in.h>   
#include <unistd.h>   
#include <errno.h>   
#include <string.h>   
#include <stdlib.h>   
  
#define SERV_PORT   8000   
int main()  
{  
   /* sock_fd --- socket文件描述符 创建udp套接字*/  
  int sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
  if(sock_fd < 0)  
  {  
    perror("socket");  
    exit(1);  
  }  
   
  /* 将套接字和IP、端口绑定 */  
  struct sockaddr_in addr_serv;  
  int len;  
  memset(&addr_serv, 0, sizeof(struct sockaddr_in));  //每个字节都用0填充
  addr_serv.sin_family = AF_INET;              //使用IPV4地址
  addr_serv.sin_port = htons(SERV_PORT);          //端口
  /* INADDR_ANY表示不管是哪个网卡接收到数据,只要目的端口是SERV_PORT,就会被该应用程序接收到 */  
  addr_serv.sin_addr.s_addr = htonl(INADDR_ANY);  //自动获取IP地址
  len = sizeof(addr_serv);  
   /* 绑定socket */  
  if(bind(sock_fd, (struct sockaddr *)&addr_serv, sizeof(addr_serv)) < 0)  
  {  
     perror("bind error:");  
     exit(1);  
  }  
  
  int recv_num;  
  int send_num;  
  char send_buf[20] = "i am server!";  
  char recv_buf[20];  
  struct sockaddr_in addr_client;  
  
  while(1)  
  {  
    printf("server wait:\n");  
      
    recv_num = recvfrom(sock_fd, recv_buf, sizeof(recv_buf), 0, (struct sockaddr *)&addr_client, (socklen_t *)&len);  
      
    if(recv_num < 0)  
    {  
      perror("recvfrom error:");  
      exit(1);  
    }  
  
    recv_buf[recv_num] = '\0';  
    printf("server receive %d bytes: %s\n", recv_num, recv_buf);  
  
    send_num = sendto(sock_fd, send_buf, recv_num, 0, (struct sockaddr *)&addr_client, len);  
      
    if(send_num < 0)  
    {  
       perror("sendto error:");  
       exit(1);  
     }  
  }  
     
  close(sock_fd);  
     
   return 0;  
}

Client.cpp

#include <stdio.h>   
#include <string.h>   
#include <errno.h>   
#include <stdlib.h>   
#include <unistd.h>   
#include <sys/types.h>   
#include <sys/socket.h>   
#include <netinet/in.h>   
#include <arpa/inet.h>   
  
#define DEST_PORT 8000   
#define DSET_IP_ADDRESS  "127.0.0.1"   
  
  
int main()  
{  
  /* socket文件描述符 */  
  int sock_fd;  
  
  /* 建立udp socket */  
  sock_fd = socket(AF_INET, SOCK_DGRAM, 0);  
  if(sock_fd < 0)  
  {  
    perror("socket");  
    exit(1);  
  }  
    
  /* 设置address */  
  struct sockaddr_in addr_serv;  
  int len;  
  memset(&addr_serv, 0, sizeof(addr_serv));  
  addr_serv.sin_family = AF_INET;  
  addr_serv.sin_addr.s_addr = inet_addr(DSET_IP_ADDRESS);  
  addr_serv.sin_port = htons(DEST_PORT);  
  len = sizeof(addr_serv);  
  
    
  int send_num;  
  int recv_num;  
  char send_buf[20] = "hey, who are you?";  
  char recv_buf[20];  
      
  printf("client send: %s\n", send_buf);  
    
  send_num = sendto(sock_fd, send_buf, strlen(send_buf), 0, (struct sockaddr *)&addr_serv, len);  
    
  if(send_num < 0)  
  {  
    perror("sendto error:");  
    exit(1);  
  }  
    
  recv_num = recvfrom(sock_fd, recv_buf, sizeof(recv_buf), 0, (struct sockaddr *)&addr_serv, (socklen_t *)&len);  
    
  if(recv_num < 0)  
  {  
    perror("recvfrom error:");  
    exit(1);  
  }  
    
  recv_buf[recv_num] = '\0';  
  printf("client receive %d bytes: %s\n", recv_num, recv_buf);  
    
  close(sock_fd);  
    
  return 0;  
}

程序运行截图:
在这里插入图片描述
client向服务器发送了“hey, who are you?”的字符串,server返回"i am server!"的字符串。

参考链接:
https://blog.csdn.net/weixin_30530939/article/details/95822533?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.control

  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值