【Linux】一篇文章搞定 CPP模拟实现UDP协议下socket通信

1. UDP的编程流程图

在这里插入图片描述

2. 数据收发阶段使用的API

#include <sys/types.h>
#include <sys/socket.h>
ssize_t sendto(int sockfd, void *buf, size_t len, int flags, struct sockaddr *dest_addr, socklen_t addrlen);
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);

2.1 sendto接口

ssize_t sendto(int sockfd, void *buf, size_t len, int flags, struct sockaddr *dest_addr, socklen_t addrlen);
  • sockfd:套接字描述符

  • buf:待发送数据

  • len:待发送数据长度

  • flags:标志位,填入0表示阻塞发送

  • dest_addr:消息接收方的地址信息结构体

  • addrlen:消息接收方的地址信息结构体大小

2.2 recvfrom接口

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
  • sockfd:套接字描述符
  • buf:数据接受存放的缓冲区
  • len:最大接受数据的能力(strlen(buf)-1)
  • flag:标志位,填入0表示阻塞接收
  • src_addr:输出型参数,消息发送方的地址信息结构,recvfrom函数会填写该结构体字段信息
  • addrlen:输出型参数,消息发送方的地址信息长度,recvfrom函数会填写该信息长度的地址

3. CPP语言 模拟实现 UDP协议 下 客户端 与 服务端 的 Socke通信

3.1 服务端代码

#include <stdio.h>                                                                                      
#include <iostream>
#include <string.h>
using namespace std;

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

//创建
int get_socket()
{
    int sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if(sockfd < 0)
    {
        perror("socket");
        return -1;
    }
    return sockfd;
}

bool bind_addr(int sockfd, char* ip_str, uint16_t port)
{
    uint32_t ip = inet_addr(ip_str);
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = port;
    addr.sin_addr.s_addr = ip;
    int ret = bind(sockfd,(sockaddr*)&addr,sizeof(addr));
    if(ret < 0)
    {
        perror("bind");
        return false;
    }
    return true;
}
void interact_client(int sockfd)
{
    // 1.等待客户端发送数据
    char buf[1024] = { 0 };//用于存放接收的信息的缓冲区
    struct sockaddr_in src_addr;  //出参,地址信息结构体
    socklen_t addr_len = sizeof(src_addr);//地址信息长度
    ssize_t recv_size = recvfrom(sockfd, buf, strlen(buf) - 1, 0, (struct sockaddr*)&src_addr, &addr_len);
    if(recv_size < 0)
    {
        perror("recvfrom");
        return ;
    }
    cout << "cli say:" << buf << endl;

    // 2.给客户端发送数据
    cout << "I wan to say:";
    std::string s;
    cin >> s;
    //执行到这里,就代表已经获得了客户端的 地址信息结构体
    ssize_t send_size = sendto(sockfd, s.c_str(), s.size(), 0, (struct sockaddr*)&src_addr, addr_len);
    if(send_size < 0)
    {
        perror("sendto");
        return ;
    }
}

int main()
{
    int sockfd = get_socket();
    uint16_t port = 19999;
    char *ip_str = "192.168.153.128";

    if(!bind_addr(sockfd, ip_str, port))  
    {
        return -1;
    }

    while(1)
    {
        interact_client(sockfd);
    }

    return 0;
}                                                                                                         

3.2 客户端代码

#include <stdio.h>                                                                                       
#include <iostream>
#include <string.h>
#include <string>
using namespace std;

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int get_socket()
{
    int sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if(sockfd < 0)
    {
        perror("socket");
        return -1;
    }
    return sockfd;
}

void interact_svr(int sockfd, struct sockaddr_in* svr_addr )
{
    // 1.给服务端发送数据
    cout << "I want to say:";
    string s;
    cin >> s;
    ssize_t send_size = sendto(sockfd, s.c_str(), s.size(), 0, (struct sockaddr*)svr_addr, sizeof(struct sockaddr_in));
    if(send_size < 0)
    {
        perror("sendto");
        return ;
    }
    
    // 2.从服务端接收数据
    char buf[1024] = { 0 };//用于存放接收的信息的缓冲区
    //已知对端信息所以可以不接收
    ssize_t recv_size = recvfrom(sockfd, buf, strlen(buf) - 1, 0, NULL, NULL);
    if(recv_size < 0)
    {
        perror("recvfrom");
        return ;
    }
    cout << "svr say:" << buf << endl;
}

int main()
{
    int sockfd = get_socket();
    uint16_t port = 19999;
    char *ip_str = "192.168.153.128";

    struct sockaddr_in svr_addr;
    svr_addr.sin_family = AF_INET;
    svr_addr.sin_port = port;
    svr_addr.sin_addr.s_addr = inet_addr(ip_str);

    while(1)
    {
        interact_svr(sockfd,&svr_addr);
    }
	
    return 0;
}                                                  

3.3 交互演示

[gongruiyang@localhost client]$ ./cli 
I want to say:XXXX
svr say:AAAA
I want to say:66666
svr say:7777
I want to say:^C
    
[gongruiyang@localhost server]$ ./svr 
cli say:XXXX
I wan to say:AAAA
cli say:66666
I wan to say:7777
^C

4. 寻找 UDP下 客户端与服务端通信 共性

在这里插入图片描述

  • 由图可以看出,这两个端口中有许许多多的接口都是重复的
  • 为了降低代码重复率,我们可以将这些方法封装在一起

5. 封装后的UDP协议下socket编程接口

udp.cpp

#pragma once

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <iostream>
#include <string>

class UdpSvr
{
private:
    int sockfd_;

public:
    UdpSvr()
    {
        sockfd_ = -1;
    }
    ~UdpSvr()
    {

    }

    int CreatSocket()
    {
        sockfd_ = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
        if(sockfd_ < 0)
        {
            perror("socket");
            return -1;
        }
        return 0;
    }

    int Bind(std::string ip, uint16_t port)
    {
        struct sockaddr_in addr;
        addr.sin_family = AF_INET;
        addr.sin_port = htons(port);
        addr.sin_addr.s_addr = inet_addr(ip.c_str());
        int ret = bind(sockfd_, (struct sockaddr*)&addr, sizeof(addr));
        if(ret < 0)
        {
            perror("bind");
            return -1;
        }

        return 0;
    }

    int Sendto(std::string data, struct sockaddr_in* dest_addr)
    {
        ssize_t send_size = sendto(sockfd_, data.c_str(), data.size(), 0 ,(struct sockaddr*)dest_addr, sizeof(struct sockaddr_in));
        if(send_size < 0)
        {
            perror("sendto");
            return -1;
        }
        return send_size;
    }

    int Recvfrom(std::string* data, struct sockaddr_in* peer_addr)
    {
        char buf[1024] = {0};
        socklen_t peer_addr_len = sizeof(struct sockaddr_in);
        ssize_t recv_size = recvfrom(sockfd_, buf, sizeof(buf) - 1, 0, (struct sockaddr*)peer_addr, &peer_addr_len);
        if(recv_size < 0)
        {
            perror("recvfrom");
            return -1;
        }

        data->assign(buf, strlen(buf));
        return recv_size;
    }

    void Close()
    {
        close(sockfd_);
    }
};

client端口代码

#include "../udp.hpp"

#define CHECK_RET(p) if(p < 0){return -1;}

int main(int argc, char* argv[])
{
    // ./cli [ip] [port]
    if(argc != 3)
    {
        printf("using ./cli [ip] [port]\n");
        return -1;
    }

    UdpSvr us;
    CHECK_RET(us.CreatSocket());

    std::string ip = argv[1]; 
    uint16_t port = atoi(argv[2]);

    struct sockaddr_in dest_addr;
    dest_addr.sin_family = AF_INET;
    dest_addr.sin_port = htons(port);
    dest_addr.sin_addr.s_addr = inet_addr(ip.c_str());

    while(1)
    {
        std::string data;
        std::cout << "please enter msg to svr:";
        fflush(stdout);
        std::cin >> data;

        int ret = us.Sendto(data, &dest_addr);
        if(ret < 0)
        {
            continue;
        }

        data.clear();

        struct sockaddr_in peer_addr;
        ret = us.Recvfrom(&data, &peer_addr);
        if(ret < 0)
        {
            continue;
        }

        std::cout << "svr say: " << data << std::endl;
    }

    us.Close();

    return 0;
}

server端口代码

#include "../udp.hpp"

#define CHECK_RET(p) if(p < 0){return -1;}

int main(int argc, char* argv[])
{
    // ./svr [ip] [port]
    if(argc != 3)
    {
        printf("using ./svr [ip] [port]\n");
        return -1;
    }

    UdpSvr us;
    CHECK_RET(us.CreatSocket());

    std::string ip = argv[1]; 
    uint16_t port = atoi(argv[2]);

    CHECK_RET(us.Bind(ip, port));

    while(1)
    {
        std::string data;
        struct sockaddr_in peer_addr;
        int ret = us.Recvfrom(&data, &peer_addr);
        if(ret < 0)
        {
            continue;
        }

        std::cout << "cli say: " << data << std::endl;

        data.clear();
        std::cout << "please enter msg to client: ";
        fflush(stdout);
        std::cin >> data;

        ret = us.Sendto(data, &peer_addr);
        if(ret < 0)
        {
            continue;
        }
    }

    us.Close();

    return 0;
}
  • 7
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值