socket编程---UDP

目录

一、socket

二、socket接口

1.流程原理

2.代码


前言

提示:这里可以添加本文要记录的大概内容:

socket编程又称套接字编程,指进行网络通信程序的编写


提示:以下是本篇文章正文内容,下面案例可供参考

一、socket

网络通信就是将原始数据进行tcp/ip四层封装,通过网卡发送实现通信

网络通信分为:客户端、服务端。

客户端:在客户这一端的进程,是主动发送请求的一端。客户端必须提前知道服务端的

地址与端口才能给服务端发送请求。

服务端:提供服务的一端进程,是被动接收请求从而进行处理的一端。

二、socket接口

1.UDP通信流程原理

sockaddr 是一个通用的套接字地址结构体,包含了一个地址族和地址数据,主要用来存储和传递套接字地址信息的。

struct sockaddr { unsigned short sa_family; // 地址族 char sa_data[14]; // 地址信息 };

字节序转换接口---将主机字节序转化为网络字节序,字节序是存储多字节数据的

uint16_t htons(uint16_val); uint16_t ntons(uint16_val); --- 返回uint16_t数据

uint32_t htonl(uint32_t val) uint32_t ntonl(uint32_t val) --- 返回uint32_t数据

所以2字节只能使用s进行转换,4字节只能属于l,这是指定的不能使用其他的

in_addr_t inet_addr(const char* ip) //将字符串IP地址转换为网络字节序整形IP地址

const char* inet_ntoa(struct in_addr_t inet_addr;) //将网络字节序整形IP地址转换

为字符串IP地址

socket接口:

int socket(int domain, int type, int protocol); //创建socket,返回一个socket描述符

domain:指定了套接字使用的协议 AF_INET:用IPv4网络协议

AF_INET6:用IPv6网络协议 AF_UNIX或AF_LOCAL:本地进程间通信

type:指定套接字的类型

SOCK_STREAM:字节流传输通常用于TCP

SOCK_DGRAM:数据报传输通常用于UDP

protocol:协议类型:通常设置为0

IPPROTO_TCP:TCP协议

IPPROTO_UDP:UDP协议

返回值:失败返回-1

绑定地址接口

int bind(int sockfd, struct sockaddr *addr, socklen_t addrlen) //为创建的套接字绑定一个

地址

sockfd:创建套接字返回的描述符

addr:指向sockaddr结构体指针,包含要绑定的地址信息

addrlen:地址长度,取决于IP协议类型

返回值:成功返回0,失败返回-1

发送数据接口:

ssize_t sendto(

int sockfd, //返回的套接字描述符

void *buf, //要发送数据的缓冲区指针

size_t len,, //要发送的数据的长度--字节

int flags, //通常设置为0-阻塞发送, MSG_DONTWAIT--非阻塞

struct sockaddr *_addr, //一个指向sockaddr结构体指针,实际

使用中通常会使用 sockaddr_in(对于

IPv4)或 sockaddr_in6(对于IPv6)-

目的端地址信息

socklen_t addrlen 地址长度

)

返回值:成功返回实际发送的数据长度,失败返回-1

接收数据接口

ssize_t recvfrom(

int sockfd,

void *buf //存放接收数据的缓冲区指针--空间地址

size_t len //要接收数据的长度

int flags, //

struct sockaddr *dest_addr, //对端地址信息

socklen_t addrlen //用于设定想要获取的地址长度

)

返回值:成功返回实际接收到的数据长度,失败返回-1

关闭套接字

int close(int sockfd);
UDP协议:

无连接、不可靠、面向数据报

无连接:只要知道对方的地址就可以给对方发送数据

不可靠:数据没有丢包检测,丢了就没了,且不保证有序

面向数据包:传输以快为单位,有最大数据传输限制

其传输性能较高,多用于视频、音频等资源传输

2.代码

服务端

c 复制代码
// udp服务端程序
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>//htons字节序转换接口
#include <netinet/in.h>
#include <sys/socket.h>
#include <error.h>

int main(int argc, char *argv[]){
        //argc表示命令行参数个数。如果你在命令行中运行程序 ./myprogram arg1 arg2,那么 argc 的值将为3("./myprogram", arg1, arg2)
        //  ./udp_ 192.168.x.x 9000
    if(argc != 3){
        perror("./udp_ 172.23.62.176 9000");
    }
    char *ip_ = argv[1];        //argv[0]是程序的名称,argv[1]是第一个参数ip地址
    uint16_t port_ = atoi(argv[2]);     //将字符串转化整数
//创建套接字
    int socketfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if(socketfd < 0){
        perror("socket error");
        return -1;
    }
//绑定地址
    struct sockaddr_in addr;    //IPv4的结构体
        //配置结构体
    addr.sin_family = AF_INET;//地址族
    addr.sin_port  = htons(port_);      //导入ip地址
    addr.sin_addr.s_addr = inet_addr(ip_);
    socklen_t addrlen = sizeof(struct sockaddr_in); //计算接收的地址长度
    int bind_ = bind(socketfd, (struct sockaddr*) &addr, addrlen);
    if(bind_ < 0){
        perror("bind error");
        return -1;
    }
    while(1){
//接收数据
        char buf[1024] = {0};
        struct sockaddr_in cliaddr;
        socklen_t addrlen = sizeof(struct sockaddr_in);
        ssize_t recv = recvfrom(socketfd, buf, 1023, 0, (struct sockaddr*)&cliaddr, &addrlen);
        if(recv < 0){
           perror("recvfrom error");
           return -1;
        }
        uint16_t cliport = ntohs(cliaddr.sin_port);
        const char *cliip = inet_ntoa(cliaddr.sin_addr);
        printf("client[%s:%d] say: %s\n",cliip, cliport, buf);

//发送数据
        printf("server say:");
        fflush(stdout);
        fgets(buf, 1023, stdin);//从键盘获取数据
        int send = sendto(socketfd, buf, strlen(buf), 0, (struct sockaddr*)&cliaddr, addrlen);
        if(send < 0){
           perror("sendto error");
           return -1;
        }

    }
//关闭套接字
    close(socketfd);


return 0;
}

客户端

复制代码
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <error.h>

#define CHECK_RET(r) if((r) == false){return -1;}

class UdpSocket {
        private:
                int _socketfd;  //定义一个套接字
        public:
                UdpSocket() :_socketfd(-1) {}
                ~UdpSocket() { Close(); }
                bool Socket() {//创建套接字
                        _socketfd =  socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
                        if(_socketfd < 0){
                                perror("Socket error");
                                return false;
                        }
                        return true;
                }
                bool Bind(const 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());
                        socklen_t addrlen = sizeof(struct sockaddr_in);
                        int bend_ = bind(_socketfd, (struct sockaddr*)&addr, addrlen);
                        if(bend_ < 0){
                                perror("Bind error");
                                return false;
                        }
                        return true;
                }
                bool Send(const std::string &body, const 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());
                        socklen_t addrlen = sizeof(struct sockaddr_in);
                        ssize_t send = sendto(_socketfd, body.c_str(), body.size(), 0, (struct sockaddr*)&addr, addrlen);
                        if(send < 0){
                                perror("Send error");
                                return false;
                        }
                        return true;
                }
                bool Recv(std::string* body, std::string *ip = NULL, uint16_t *port = NULL){    //接收数据
                        struct sockaddr_in addr;
                        socklen_t addrlen = sizeof(struct sockaddr_in);
                        char tmp[4096] = {0};
                        ssize_t recv = recvfrom(_socketfd, tmp, 4096, 0, (struct sockaddr*)&addr, &addrlen);
                        if(recv < 0){
                                perror("Recv error");
                                return false;
                        }
                        if(ip != NULL) *ip = inet_ntoa(addr.sin_addr);
                        if(port != NULL) *port = ntohs(addr.sin_port);
                        body->assign(tmp, recv);
                        return true;
                }
                bool Close(){   //关闭套接字
                        if(_socketfd < 0){
                                return true;
                        }
                        close(_socketfd);
                        _socketfd = -1;
                        return true;
                }
};


int main(int argc, char* argv[])
{
        if(argc != 3){
                std::cout <<"./udp 172.23.62.176 9000\n";
                return -1;
        }
        UdpSocket us;   //实例化对象
//创建套接字
        CHECK_RET(us.Socket());
        while(1){
                std::string buf;
                std::cout <<"client say:";
                fflush(stdout); //刷新输出缓冲区
                std::cin >> buf;//向缓冲区写入数据
//发送数据
                CHECK_RET(us.Send(buf, argv[1], std::stoi(argv[2])));
//接收数据
                buf.clear();    //清空缓冲区
                CHECK_RET(us.Recv(&buf));
                std::cout << "server say:" << buf << std::endl;
        }
//关闭套接字
        us.Close();
        return 0;
}
相关推荐
chlk1231 天前
Linux文件权限完全图解:读懂 ls -l 和 chmod 755 背后的秘密
linux·操作系统
舒一笑1 天前
Ubuntu系统安装CodeX出现问题
linux·后端
改一下配置文件1 天前
Ubuntu24.04安装NVIDIA驱动完整指南(含Secure Boot解决方案)
linux
BingoGo1 天前
当你的 PHP 应用的 API 没有限流时会发生什么?
后端·php
JaguarJack1 天前
当你的 PHP 应用的 API 没有限流时会发生什么?
后端·php·服务端
深紫色的三北六号2 天前
Linux 服务器磁盘扩容与目录迁移:rsync + bind mount 实现服务无感迁移(无需修改配置)
linux·扩容·服务迁移
SudosuBash2 天前
[CS:APP 3e] 关于对 第 12 章 读/写者的一点思考和题解 (作业 12.19,12.20,12.21)
linux·并发·操作系统(os)
哈基咪怎么可能是AI2 天前
为什么我就想要「线性历史 + Signed Commits」GitHub 却把我当猴耍 🤬🎙️
linux·github
BingoGo2 天前
OpenSwoole 26.2.0 发布:支持 PHP 8.5、io_uring 后端及协程调试改进
后端·php
JaguarJack2 天前
OpenSwoole 26.2.0 发布:支持 PHP 8.5、io_uring 后端及协程调试改进
后端·php·服务端