linux--实现udp协议的通信小程序

函数接口

发送数据接口

 #include <sys/types.h>
#include <sys/socket.h>

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

  • sockdf:套接字的描述符
  • buf:待发送的数据
  • len:发送数据的长度
  • flags:传入0表示阻塞发送
  • dest_addr:目标主机的地址信息,封装在struct sockaddr结构体中
  • addrlen:dest_addr表示的地址信息的长度

在这里插入图片描述
注意:

  1. UDP也是有发送缓冲区的,只是UDP协议的特点是面向数据报,他是整条信息直接发送的。
    所以说当数据从应用层到达传输层的过程,也就是到发送缓冲区中,数据会被直接打上UDP协议的头部,然后直接提交到网络层,中间的阻塞时间可以说很短暂
  2. 阻塞发送,是指当发送缓冲区中有数据的时候,这个阶段如果再次调用sendto接口,会让数据发送的过程陷入阻塞等待。
    直到上一条数据发送完毕,sendto接口才可以将数据发送到缓冲区中

接收数据接口

#include <sys/types.h>
#include <sys/socket.h>

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

  • sockfd:套接字描述符
  • buf :把从接收缓冲区中拿到数据放到buf数组中
  • len:buf数组的最大长度,也就是最大可以结束多少数据,在末尾需要预留一个’\0'的位置
  • flags:0表示阻塞接收数据
  • src_addr:源主机的地址信息,标识着这条信息从哪一个主机的哪一个进程来
  • addrlen:src_addr地址信息的长度,是一个输入输出型参数,传的是长度的地址。

关闭套接字接口

#include <unistd.h>

int close(int fd);

因为socket函数的返回值也是一个文件描述符,所以就可以使用close接口来关闭。

UDP通信小程序

实现的功能

服务端 (server):

  • 创建套接字
  • 绑定地址信息
  • 接收数据
  • 发送数据
  • 关闭套接字

客户端(client):

  • 创建套接字
  • 发送数据
  • 接收数据
  • 关闭套接字

流程

在这里插入图片描述

程序代码

udpClass.hpp,将重复的部分集中到一个类中

#pragma once 
#include <unistd.h>                                                                                                                                                                        
#include <cstdio>
#include <cstdlib>
#include <string>
#include <iostream>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>

using std::cin;
using std::cout;
using std::endl;
using std::string;

class udpClass
{
public:
    //构造函数、
    udpClass()
    {
        _sock = -1;
    }

    //socket 创建套接字
    bool CreateSocket()
    {
        //int socket(int domain, int type, int protocol);
        //选择ipv4-->AF_INET  面向数据报--> SOCK_DGRAM UDP协议-->IPPROTO_UDP 
        _sock = socket(AF_INET, SOCK_DGRAM,IPPROTO_UDP);
        if(_sock < 0)
        {
            //创建套接字失败
            perror("socket error ");
            return false;
        }
        return true;
    }
    
	// bind 绑定Ip地址 端口信息
    bool Bind(string& ip,uint16_t port)
    {
        //int bind(int sockfd, const struct sockaddr *addr,                                                                                                                                
        //       socklen_t addrlen);
        struct sockaddr_in addr;
        addr.sin_family = AF_INET;//按照ipv4的格式进行解析
        addr.sin_port = htons(port);//小端字节序转为大端字节序,16位
        //inet_addr--> 点分十进制的ip转为uint32格式, 将主机字节序转为网络字节序
        //ip.c_str --> string 类型转为 char* 类型
        addr.sin_addr.s_addr = inet_addr(ip.c_str());

        int ret =  bind(_sock,(struct sockaddr*)&addr,sizeof(struct sockaddr_in));
        if(ret < 0)
        {
            perror("bind error ");
            return false;
        }
        return true;
    }

    //send 发送数据
    bool Send(string& data, struct sockaddr_in* addr)
    {
        //ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
        //       const struct sockaddr *dest_addr, socklen_t addrlen);
        int sendsize = sendto(_sock,data.c_str(),data.size(),0,
                (struct sockaddr*)addr,sizeof(struct sockaddr_in));
        if(sendsize < 0)
        {
            perror("send error ");
            return false;
        }
        return true;
    }
    
	//recvfrom 接收数据
    bool Recvfrom(string& buf,struct sockaddr_in* addr)
    {
        //ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
        //      struct sockaddr *src_addr, socklen_t *addrlen);
        char data[1024] = {'\0'};
        socklen_t addrlen = sizeof(struct sockaddr_in);
        int recvsize = recvfrom(_sock,data,sizeof(data),0,
                (struct sockaddr*)addr, &addrlen);
        if(recvsize < 0)
        {
            perror("recvfrom error ");
            return false;
        }
        //拷贝接收的数据i
        //从data数组中截取前recvsize个字符
        buf.assign(data,recvsize);
        
        return true;
    }

    //close
    void Close()
    {
        close(_sock);
        _sock = -1;
    }

private:
    int _sock;//套接字描述符
}; 

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

这种情况下,我们在用sizeof计算sockaddr_in结构体的大小的时候,不能直接对变量名使用sizeof,因为原变量的类型是指针类型,他的大小在32位机器下一直是4个字节,在64位机器下是8个字节。所以需要直接对原变量的结构体类型进行sizeof,这样才能求出准确的大小。

server.cpp ,服务端的程序

#include "udpClass.hpp"
int main(int argc, char* argv[])    
{    
    //所需要的三个参数  ./server ip port    
    if(argc != 3)    
    {    
        cout<<"所输入的参数不正确,应该为:[./server] [ip] [port]"<<endl;    
        return 0;    
    }    
    string ip = argv[1];    
    uint16_t port = atoi(argv[2]);    
    
    udpClass udp;    
    //创建套接字    
    if(!udp.CreateSocket())    
        return 0;    
    
    //绑定ip和端口信息    
    if(!udp.Bind(ip,port))    
        return 0;    
    
    while(1)    
    {    
        //接收数据    
        string buf;    
        struct sockaddr_in cliaddr;//对端地址信息    
        udp.Recvfrom(buf,&cliaddr);    
    
        cout<<"client say : "<<buf<<endl;    
    
        //发送数据    
        cout<<"server respond : ";    
        fflush(stdout);//刷新缓冲区    
        cin>>buf;    
        udp.Send(buf,&cliaddr);    
    }    
    
    udp.Close();    
    return 0;                                                                                                                                                                              
}

client.cpp ,客户端程序

#include "udpClass.hpp"    
    
int main(int argc, char* argv[])    
{    
    if(argc != 3)    
    {    
        //ip port 是服务端的ip port    
        cout<<"参数错误 [./client] [ip] [port]"<<endl;    
        return 0;    
    }    
    
    string ser_ip = argv[1];    
    uint16_t ser_port = atoi(argv[2]);    
    
    udpClass udp;    
    //创建套接字    
    if(!udp.CreateSocket())    
        return 0;    
    
    //服务端的地址信息    
    struct sockaddr_in ser_addr;    
    ser_addr.sin_family = AF_INET;    
    ser_addr.sin_port = htons(ser_port);    
    ser_addr.sin_addr.s_addr = inet_addr(ser_ip.c_str());    
    
    while(1)    
    {    
        //发送数据    
        cout<<"client say : ";                                                                                                                                                             
        fflush(stdout);    
    
        string buf;    
        cin>>buf;    
        udp.Send(buf,&ser_addr);    
    
        //接收数据    
        struct sockaddr_in dataaddr;    
        udp.Recvfrom(buf,&dataaddr);
        cout<<"server say : "<<buf<<endl;
    }

    udp.Close();

    return 0;
}

运行结果

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值