Linux网络编程 | 基于UDP协议实现客户端服务端通信

UDP(User Datagram Protocol 用户数据报协议)
它属于传输层协议,是无连接的 不可靠的 面向数据报的, 
应用:用于传输实时性高于安全性的场景--例如:视频传输

网络中通信的两端主机:客户端 服务端
        客户端:是通信中主动发起请求的一端
        服务端:是通信中针对请求提供服务的一端,也是被动接收请求的一端
   服务端通常需要提前告知客户端自己的地址信息,并且也不能随意改变 

udp通信编程流程

服务端

  1. 创建套接字:在内核中开辟一个socket结构体
  2. 绑定地址信息:给创建的socket结构体指定源端IP和源端端口                                                发送数据时指定源端地址信息;告诉系统收到的哪条数据应该交给这个socket
  3. 接收数据:从对应的socket结构体的接收缓冲区中取出数据
  4. 发送数据:将数据放到指定的socket发送缓冲区中
  5. 关闭套接字:释放socket结构体资源

          

客户端

  1.  创建套接字                            
  2. 为套接字绑定地址信息 : 客户端通常不主动绑定地址  ,操作系统会根据系统资源自动分配一个未使用的端口进行地址绑定          
  3. 接收数据:从对应的socket结构体的接收缓冲区中取出数据
  4. 发送数据:将数据放到指定的socket发送缓冲区中
  5. 关闭套接字:释放socket结构体资源

               

接口介绍


    1.创建套接字                                                   

int socket(int domain, int type, int protocol);

domain:地址域类型 :AF_INET -ipv4的地址类域型

                                   AF_INET6 -ipv6的地址域类型

                                  AF_UNIX--struct sockaddr_un;

                                 AF_INET--struct sockaddr_in    

                                 AF_INET6 --struct sockaddr_in6

 type:套接字类型   
                SOCK_STREAM:流式套接字  提供字节流传输 默认TCP协议
                SOCK_DGRAM:数据报套接字  提供数据报传输 默认UDP协议

protocol:协议所使用的协议类型;0-套接字类型默认协议
                IPPROTO_TCP:tcp协议
                IPPROTO_UDP:UDP协议
返回值:成功返回一个文件描述符--操作句柄;失败返回-1

    2.为套接字绑定地址信息
       

int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);    

sockfd:创建套接字返回值
addr:struct sockaddr通用地址协议  
len:指定地质结构长度
返回值:成功返回0 失败返回-1;
           

    3.发送数据

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

sockfd:套接字描述符  buf:发送的数据首地址
len:发送的数据长度  flag:标志位 默认为0-表示阻塞发送
dest_addr:指定对端的地址信息  addrlen:地址信息长度
返回值:成功返回实际发送的数据长度  失败返回-1 

  4.接收数据

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

sockfd:套接字描述符  buf 空间地址 用于存放接收的数据  
len:要获取的数据长度  flag:0-默认阻塞接收
src_addr:获取发送端的地址信息  
addrlen:输入输出参数--指定想要获取多长的地址  返回实际的地址长度     
返回值:返回实际接受到的数据长度  失败返回-1;

    5.关闭套接字
       

int close(int fd);

字节序转换接口

uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);     


字符串点分十进制ip地址转换为网络字节序整数IP地址

in_addr_t inet_addr(const char *cp);    //只能转换ipv4的地址
int inet_pton(int af, const char *src, void *dst);//ipv6和ipv4都可以


网络字节序整数转换为字符串点分十进制

char *inet_ntoa(struct in_addr in);//只能转换ipv4
const char *inet_ntop(int af,const void *src,char *dst,socklen_t size);ipv4和ipv6都可以转换


基于UDP协议实现客户端、服务端聊天程序

封装的公共头文件

#include <iostream>
#include <string>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <unistd.h>
using namespace std;
#define CHECK_RET(q) if((q)==false){return -1;}
class UdpSocket
{
public:
    UdpSocket() : _sockfd(-1) {}
    bool Socket()
    {
        _sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
        if (_sockfd < 0)
        {
            perror("socket error");
            return false;
        }
        return true;
    }
    void Addr(sockaddr_in *addr, const string &ip, const uint16_t port)
    {
        addr->sin_port = htons(port);
        addr->sin_family = AF_INET;
        addr->sin_addr.s_addr = inet_addr(ip.c_str());
    }
    bool Bind(const string &ip, const uint16_t port)
    {
        sockaddr_in addr;
        Addr(&addr, ip, port);

        int ret = bind(_sockfd, (sockaddr *)&addr, sizeof(sockaddr_in));
        if (ret < 0)
        {
            perror("bind error");
            return false;
        }
        return true;
    }
    bool Sendto(const string &str, const string &ip, uint16_t port)
    {
        sockaddr_in addr;
        Addr(&addr, ip, port);

        int ret = sendto(_sockfd, str.c_str(), str.size(), 0, (sockaddr *)&addr, sizeof(sockaddr_in));
        if (ret < 0)
        {
            perror("sendto error");
            return false;
        }
        return true;
    }
    bool Recvfrom(string *buf, string *ip = nullptr, uint16_t *port = nullptr)
    {
        sockaddr_in addr;

        socklen_t len = sizeof(sockaddr_in);
        char tmp[4096] = {0};
        int ret = recvfrom(_sockfd, tmp, 4096, 0, (sockaddr *)&addr, &len);
        if (ret < 0)
        {
            perror("recvfrom error");
            return false;
        }
        if (ip != nullptr)
            *ip = inet_ntoa(addr.sin_addr);
        if (port != nullptr)
            *port = ntohs(addr.sin_port);
        buf->assign(tmp,ret);
        return true;
    }
    bool Close()
    {
        if (_sockfd != -1)
            close(_sockfd);
        return true;
    }

private:
    int _sockfd;
};

服务端

#include "udpsocket.hpp"

int main()
{
    UdpSocket server;
    CHECK_RET(server.Socket());

    CHECK_RET(server.Bind("10.206.0.2", 9000));

    while (1)
    {
        string str;
        string ip;
        uint16_t port;

        CHECK_RET(server.Recvfrom(&str, &ip, &port));
        cout << "client-[" << ip << ":" << port << "] say:" << str << endl;
        cout << "server say:";
        str.clear();

        cin >> str;
        CHECK_RET(server.Sendto(str, ip, port));
    }

    server.Close();
    return 0;
}

客户端                                   

#include "udpsocket.hpp"
int main()
{
    UdpSocket client;
    CHECK_RET(client.Socket());

    while (1)
    {
        string str;
        cout << "client say:";
        cin >> str;
        CHECK_RET(client.Sendto(str, "10.206.0.2", 9000));
        str.clear();
        CHECK_RET(client.Recvfrom(&str));
        cout << "client say:" << str << endl;
    }

    client.Close();
    return 0;
}

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值