网络编程基础——UDP编程(2)

1 UDP的概念

UDP 是User Datagram Protocol的简称, 中文名是用户数据报协议,是OSI(Open System Interconnection,开放式系统互联) 参考模型中一种无连接的传输层协议。
UDP协议与TCP协议一样用于处理数据包,在OSI模型中,两者都位于传输层,处于IP协议的上一层。UDP有不提供数据包分组、组装和不能对数据包进行排序的缺点,也就是说,当报文发送之后,是无法得知其是否安全完整到达的。UDP用来支持那些需要在计算机之间传输数据的网络应用。包括网络视频会议系统在内的众多的客户/服务器模式的网络应用都需要使用UDP协议。UDP协议从问世至今已经被使用了很多年,虽然其最初的光彩已经被一些类似协议所掩盖,但即使在今天UDP仍然不失为一项非常实用和可行的网络传输层协议。
许多应用只支持UDP,如:多媒体数据流,不产生任何额外的数据,即使知道有破坏的包也不进行重发。当强调传输性能而不是传输的完整性时,如:音频和多媒体应用,UDP是最好的选择。在数据传输时间很短,以至于此前的连接过程成为整个流量主体的情况下,UDP也是一个好的选择。

2 UDP编程主要函数与步骤

1 socket

int socket(int domain, int type, int protocol);
功能:创建一个用来进程通信的套接字,返回文件描述符
参数:
	domain:AF_INET   	IPv4协议族
	type:SOCK_STREAM	流式套接字			tcp传输协议
		 SOCK_DGRAM		数据报套接字		udp传输协议
		 SOCK_RAW		原始套接字			
	protocol:默认为0 
返回值:
	成功返回套接字新文件描述符
	失败返回-1 

2 bind

int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
功能:将套接字与IP地址端口绑定在一起
参数:
	sockfd:文件描述符 
	addr:结构体空间首地址 
	addrlen:信息的长度
返回值:
	成功返回0 
	失败返回-1 

3 sendto

ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);
功能:给另一个套接字发送消息
参数:
	sockfd:套接字文件描述符
	buf:要发送数据存放空间的首地址
	len:要发送数据的长度
	flags:发送属性  默认为0 
	dest_addr:目的地址
	addrlen:目的地址信息长度
返回值:
	成功返回发送字节个数
	失败返回-1 

4 recvfrom

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);
 功能:接收数据
参数:
	sockfd:套接字文件描述符
	buf:存放接收到数据空间的首地址
	len:最多允许接收的字节数
	flags:属性 默认为0 
	src_addr:存放发送端地址信息空间首地址
	addrlen:想要接收发送端地址大小的变量空间首地址
返回值:
	成功返回实际接收字节数
	失败返回-1 
**注意:该函数具有阻塞功能**

5 htons与ntohs

 uint16_t htons(uint16_t hostshort);
功能:将本地字节序转换为网络字节序
参数:hostshort:本地端口号
返回值:返回网络字节序端口号
uint16_t ntohs(uint16_t netshort);
功能:将网络字节序转换为本地字节序
参数:netshort:网络端口号
返回值:返回本地字节序端口号

6 套接字结构体

struct sockaddr_in {
    
    
	   sa_family_t    sin_family; /* address family: AF_INET */
	   in_port_t      sin_port;   /* port in network byte order */
	   struct in_addr sin_addr;   /* internet address */
   };

/* Internet address. */
struct in_addr {
    
    
	   uint32_t       s_addr;     /* address in network byte order */
   };

3 用UDP编程实现客户端与服务端轮询单次交互

在这里插入图片描述
这里我的ip地址为图中的192.168.1.117,所以在已下代码都用的这个地址
在这里插入图片描述

头文件

#ifndef __HEAD_H__
#define __HEAD_H__

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <semaphore.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/socket.h>
#include <netinet/ip.h> 
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <sys/epoll.h>

#endif

用户端代码

#include "head.h"

int main(int argc, char const *argv[])
{
    
    
     // 初始化结构体套接字
     struct sockaddr_in cli;
     struct sockaddr_in ser = {
    
    
         .sin_family = AF_INET,
         .sin_port = htons(50000),
         .sin_addr.s_addr = inet_addr("192.168.1.117"),//这里你需要改为自己的ip
     };
     //==1==
     
     int sock_fd_a = socket(AF_INET, SOCK_DGRAM, 0);
     if (-1 == sock_fd_a)
     {
    
    
          perror("fail to socket");
          return -1;
     }
     while (1)
     {
    
    
          //==2==
          char sendbuf[1024] = {
    
    0};
          fgets(sendbuf, sizeof(sendbuf),stdin);
          ssize_t nret1 = sendto(sock_fd_a, sendbuf, strlen(sendbuf) + 1, 0, (struct sockaddr *)&ser, sizeof(ser));
          //==3==
          char recbuf[1024] = {
    
    0};
          ssize_t nret2 = recvfrom(sock_fd_a, recbuf, sizeof(recbuf), 0, NULL, NULL);
          printf("ser->cli:%s", recbuf);
     }
     //==4==
     close(sock_fd_a);

     return 0;
}

客户端代码

#include "head.h"

int main(int argc, char const *argv[])
{
    
    
     // 初始化结构体套接字
     struct sockaddr_in cli;
     struct sockaddr_in ser = {
    
    
         .sin_family = AF_INET,
         .sin_port = htons(50000),
         .sin_addr.s_addr = inet_addr("192.168.1.117"),
     };
     //==1==
     int sock_fd_b = socket(AF_INET, SOCK_DGRAM, 0);
     if (-1 == sock_fd_b)
     {
    
    
          perror("fail to socket");
          return -1;
     }
     //==2==
     int bret = bind(sock_fd_b, (struct sockaddr *)&ser, sizeof(ser));
     if (-1 == bret)
     {
    
    
          perror("fail to bind");
          return -1;
     }
     while (1)
     {
    
    
          //==3==
          char recbuf[1024] = {
    
    0};
          socklen_t serlen = sizeof(ser);
          ssize_t nret2 = recvfrom(sock_fd_b, recbuf, sizeof(recbuf), 0, (struct sockaddr *)&ser, &serlen);
          printf("cli->ser:%s", recbuf);
          //==4==
          char sendbuf[1024] = {
    
    0};
          fgets(sendbuf, sizeof(sendbuf),stdin);
          ssize_t nret1 = sendto(sock_fd_b, sendbuf, strlen(sendbuf) + 1, 0, (struct sockaddr *)&ser, sizeof(ser));
     }
     close(sock_fd_b);

     return 0;
}

4 用UDP编程实现客户端与服务端文件的传输(这里用的是图片文件,头文件与上面的头文件相同、UDP的丢包现象属于正常,可加些许延迟改善)

客户端(用到一些文件io知识,之前的博客有提到)

#include "head.h"

int main(int argc, char const *argv[])
{
    
    
     struct stat file_stat;
     struct sockaddr_in cli;
     struct sockaddr_in ser = {
    
    
          .sin_family = AF_INET,
          .sin_port = htons(50000),
          .sin_addr.s_addr = inet_addr("192.168.1.117"),
     };
     

     int fd_src = open("./src.jpg",O_RDONLY);//打开文件
     if(-1 == fd_src)
     {
    
    
          perror("fail to open");
          return -1;
     }
     int stat_ret = fstat(fd_src,&file_stat);//获取文件状态
     if(-1 == stat_ret)
     {
    
    
          perror("fail to fstat");
          close(fd_src);
          return -1;
     }

     int fd_cli = socket(AF_INET,SOCK_DGRAM,0);
     if(-1 == fd_cli)
     {
    
    
          perror("fail to socket");
          return -1;
     }
     //发送文件大小
     sendto(fd_cli,&(file_stat.st_size),sizeof(&file_stat.st_size),0,(struct sockaddr *)&ser,sizeof(ser));
     printf("%ld\n",file_stat.st_size);
     char R_file_buf[1024] = {
    
    0};
     ssize_t nret = 0;
     while (1)
     {
    
    
          nret = read(fd_src,R_file_buf,sizeof(R_file_buf));
          printf("%ld\n",nret);
          if(nret <= 0)
          {
    
    
               break;
          }
          sendto(fd_cli,R_file_buf,nret,0,(struct sockaddr *)&ser,sizeof(ser));  
     }
     close(fd_src);
     return 0;
}

服务端

#include "head.h"

int main(int argc, char const *argv[])
{
    
    
     struct sockaddr_in cli;
     struct sockaddr_in ser = {
    
    
          .sin_family = AF_INET,
          .sin_port = htons(50000),
          .sin_addr.s_addr = inet_addr("192.168.1.117"),
     };
     

     int fd_dst = open("./dst.jpg",O_RDWR | O_CREAT | O_TRUNC,0664);//打开文件
     if(-1 == fd_dst)
     {
    
    
          perror("fail to open");
          return -1;
     }
     int fd_ser = socket(AF_INET,SOCK_DGRAM,0);
     if(-1 == fd_ser)
     {
    
    
          perror("fail to socket");
          close(fd_dst);
          return -1;
     }
     int ret_bint = bind(fd_ser,(struct sockaddr *)&ser,sizeof(ser));
     if(-1 == ret_bint)
     {
    
    
          perror("fail to bind");
          close(fd_dst);
          return -1;
     }
     //接收文件大小
     char file_size[64] = {
    
    0};
     socklen_t clilen = sizeof(cli);
     ssize_t ret_re =  recvfrom(fd_ser,file_size,sizeof(file_size),0,(struct sockaddr *)&cli,&clilen);
     ssize_t size = 0;
     char tmpbuff[1024] = {
    
    0};
     while (1)
     {
    
    
         ssize_t nret =  recvfrom(fd_ser,tmpbuff,sizeof(tmpbuff),0,(struct sockaddr *)&cli,&clilen); 
         write(fd_dst,tmpbuff,nret);
         if(nret != 1024)
         {
    
    
          break;
         }
     }
     close(fd_ser);
     
}

结果图

在这里插入图片描述

5 UDP编程的大概框架

客户端 服务端
1 socket 1 socket
2 sendto 2 bind
3 recvfrom 3 recvfrom
4 close 4 sendto
5close

猜你喜欢

转载自blog.csdn.net/m0_58193842/article/details/128708279
今日推荐