以udp协议创建通信服务器

news/2024/5/18 12:36:11 标签: udp, 服务器, 网络协议

概念图

 创建服务器让A,B主机完成通信。

认识接口

socket

 返回值:套接字,你可以认为类似fd

参数:

  1. domain->:哪种套接字,常用AF_INET(网络套接字)、AF_LOCAL(本地套接字)
  2. type->:发送数据类型,常用 SOCK_DGRAM(以数据报式发送)
  3. protocol->:一般填0,自动推导类型或者IPPROTO_UDP、IPPROTO_TCP。

创建一个套接字,类似创建一个文件标识符fd。

先介绍些结构体类型

struct sockaddr
struct sockaddr_in
struct sockaddr_un

_in结构体中保存的是ip\port数据,而_un中保存的则是本地的数据

udp协议为了本地通信与网络通信同一套接口兼容,所以先将sockaddr_in/_un强转成sockaddr类型传入各个函数,在函数中判断前2个字节类型,来做本地通信或者网络通信。

bind

将套接字绑定,一般来说套接字绑定都是服务器才会绑定的,客户端一般给操作系统自动分配ip与端口的。

返回值:成功0,失败-1.设置错误码

参数:

  1. sockfd 需要绑定的套接字
  2. sockaddr包含了需要与套接字绑定的ip和端口号。
  3. addrlen该结构体长度。

recvfrom

用来接收数据的接收

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

参数

  1. sockfd:将从该套接字的端口和ip中取得数据
  2. buff:输出型参数,将数据存放到buff中。
  3. len:buff的长度
  4. flags:以状态等待数据,一般填0,阻塞等待数据
  5. src_addr:发送方ip+port结构体数据,输出型参数
  6. 结构体数据长度

sendto

发送数据给某个主机

返回值:实际发送数据的个数,-1失败

参数

  1. sockfd:将发送方的ip+port发送给对方
  2. buff:输入型参数,将buff中数据发送给对方。
  3. len:buff的长度;
  4. flags:默认发送方式发送
  5. 接收方ip+port结构体数据,根据该参数寻找对于主机
  6. 结构体数据长度

sockaddr_in结构体配套函数

机器大小端的转换函数。h本地to转

以太网规定,网络传输数据一定是大端方式传输,所以我们的机器无论是大端还是小端,在网络中都会成为大端序列。

 当是为了人能看的明白,我们的ip地址一般都是以字符串的方式呈现,这样的方式我们称为点分十进制的方式。而且传入为了的ip地址可能需要改序列,所以一批接口出现了。

这些接口将字符串转为uint32_t或者将uint32_t转为字符串

代码

一份2个主机通过服务器可以聊天的代码

服务器代码:

server.hpp

#pragma once
#include <cstdio>
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string>
#include <cstring>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "Log.hpp"
#include <unordered_map>
using std::cin;
using std::cout;
using std::endl;

class udp_server
{
    typedef int socket_t;
    void ip_type()
    {
        cout << "ip:" << _ip << endl;
        if (_ip == "127.0.0.1")
        {
            cout << "#####本地测试#####" << endl;
        }
        else if (_ip.empty())
        {
            cout << "#####开放全部ip地址#####" << endl;
        }
        else
        {
            cout << "#####指定ip地址#####" << endl;
        }
    }
    void init_server()
    {
        _socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
        if (_socket < 0)
        {
            Log(FATAL, "socket get fail!!![%d][%s]\n", __LINE__, __TIME__);
            exit(1);
        }
        struct sockaddr_in ip_port;
        bzero(&ip_port, sizeof(ip_port));
        ip_port.sin_family = AF_INET;
        ip_port.sin_addr.s_addr = _ip.empty() ? INADDR_ANY : inet_addr(_ip.c_str());
        ip_port.sin_port = htons(_port);
        if (bind(_socket, (struct sockaddr *)&ip_port, sizeof(ip_port)) < 0)
        {
            Log(FATAL, "bind  fail!!![%d][%s]\n", __LINE__, __TIME__);
            exit(2);
        }
        Log(NORMAL, "udp init success!!![%d][%s]\n", __LINE__, __TIME__);
    }

public:
    udp_server(uint16_t port, std::string ip = "") : _ip(ip), _port(port) {}
    udp_server() {}

    void activate()
    {
        init_server();
        ip_type();
        while (1)
        {
            char buff[1024] = {0};
            // cout<<"buff size: "<<strlen(buff)<<endl;
            struct sockaddr_in client_ip_port;
            socklen_t len = sizeof(client_ip_port);
            //开始等待客户端

            ssize_t end = recvfrom(_socket, buff, sizeof(buff) - 1, 0, (struct sockaddr *)&client_ip_port, &len);
            char retbuff[1024]={0};
            // cout<<"buff size: "<<strlen(buff)<<endl;
            if (end >= 0)
            {
                buff[end] = '\0';
                printf("[%s:%d]clint say# %s\n", inet_ntoa(client_ip_port.sin_addr), ntohs(client_ip_port.sin_port), buff);
                sprintf(retbuff,"[%s:%d]clint say# %s", inet_ntoa(client_ip_port.sin_addr), ntohs(client_ip_port.sin_port), buff);
            }
            else
            {
                Log(WARINNG, "recvfrom Message have fail [%d][%s]\n", __LINE__, __TIME__);
            }
            //处理数据。
            char key[64];
            snprintf(key,sizeof(key),"%s-%u",inet_ntoa(client_ip_port.sin_addr),client_ip_port.sin_port);
            auto it = _users.find(key);
            if(it==_users.end())
            {
                cout<<key<<endl;
                cout<<key<<" :放入客户端"<<endl;
                _users.insert({key,client_ip_port});
            }

            // cout<<"end : "<<end<<endl;
            //反馈
            for(auto&it:_users)
            {
                if(client_ip_port.sin_addr.s_addr==it.second.sin_addr.s_addr)
                {
                    continue;
                }
                sendto(_socket, retbuff, sizeof retbuff, 0, (struct sockaddr *)&(it.second), sizeof(it.second));
            }
            Log(Debug, "sendto Message have fail [%d][%s]\n", __LINE__, __TIME__);
        }
    }

private:
    socket_t _socket;
    std::string _ip;
    uint16_t _port;
    std::unordered_map<std::string, struct sockaddr_in> _users;
};

server.cpp

#include "server.hpp"
#include <memory>
using std::shared_ptr;
void SERVERUER()
{
    std::cout<<"./server + ip + port"<<std::endl;
}


int main(int argc,char*argv[])
{
    if(argc==2||argc==3)
    {
        cout<<"argc:"<<argc<<endl;
        if(argc==2)
        {
            in_port_t port=atoi(argv[1]);
            shared_ptr<udp_server> server_ptr(new udp_server(port));
            server_ptr->activate();
        }
        else
        {
            
            in_port_t port=atoi(argv[2]);
            std::string ip=argv[1];
            shared_ptr<udp_server> server_ptr(new udp_server(port,ip));
            server_ptr->activate();
        }
    }
    else
    {
        SERVERUER();
    }
    return 0;
}

客户端代码

client.cpp

#include <cstdio>
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string>
#include <cstring>
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "Log.hpp"
#include <pthread.h>
using std::cin;
using std::cout;
using std::endl;
typedef int socket_t;

void CLIENTUER()
{
    std::cout << "./client + ip + port" << std::endl;
}

void *thread_func(void *ages)
{
    socket_t *_socket = (socket_t*)ages;
    char get_messages[102];
    struct sockaddr_in temp;
    bzero((void *)&temp, sizeof(temp));
    socklen_t len(sizeof temp);
    while (1)
    {
        ssize_t end = recvfrom(*_socket, get_messages, sizeof(get_messages) - 1, 0, (struct sockaddr *)&temp, &len);
        if (end >= 0)
        {
            get_messages[end] = 0;
            std::cout<< get_messages << std::endl;
        }
    }
    return nullptr;
}

int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        CLIENTUER();
        exit(1);
    }
    socket_t _socket = socket(AF_INET, SOCK_DGRAM, 0);
    pthread_t tid;
    pthread_create(&tid,nullptr,thread_func,(void*)&_socket);
    std::string messages;
    struct sockaddr_in server_ip_port;
    bzero(&server_ip_port, sizeof(server_ip_port));
    server_ip_port.sin_family = AF_INET;
    server_ip_port.sin_addr.s_addr = inet_addr(argv[1]);
    server_ip_port.sin_port = htons(atoi(argv[2]));
    socklen_t len = sizeof(server_ip_port);

    while (1)
    {
        fflush(stdout);
        std::getline(std::cin, messages);
        sendto(_socket, messages.c_str(), messages.size(), 0, (struct sockaddr *)&server_ip_port, len);
    }
    return 0;
}


http://www.niftyadmin.cn/n/4980000.html

相关文章

Nginx详解 二:配置文件部分

文章目录 1. Nginx 配置文件1.1 主配置文件1.2 子配置文件1.3 全局配置1.3.1 修改启动的进程数1.3.2 cpu和work进程绑定&#xff08;nginx调优&#xff09;1.3.3 修改PID路径1.3.4 nginx进程的优先级&#xff08;work进程的优先级&#xff09;1.3.5 调试work进程打开的文件的个…

.netcore grpc日志记录配置

一、日志记录配置概述 通过配置文件appsettings.json进行配置通过Program.cs进行配置通过环境变量进行配置客户端通过日志通道进行配置 二、实战案例 配置环境变量:Logging__LogLevel__GrpcDebug配置Appsettings.json配置Program.cs配置客户端工厂以上截图是目前为止已知的可…

第十二章 ObjectScript - 命令

文章目录 第十二章 ObjectScript - 命令命令熟悉的命令用于多维数组的命令 第十二章 ObjectScript - 命令 命令 本节概述了在 ObjectScript 常用命令。其中包括与其他语言中的命令相似的命令&#xff0c;以及其他语言中没有等效项的其他命令。 命令名称不区分大小写&#xf…

浅析三维模型OBJ格式轻量化压缩集群处理方法

浅析三维模型OBJ格式轻量化压缩集群处理方法 三维模型的OBJ格式轻量化压缩是指通过一系列技术和方法将三维模型的文件大小进一步减小&#xff0c;以提高模型在计算机中的加载、传输和存储效率。集群处理技术是指利用多台计算机构成的集群来并行处理任务&#xff0c;以加速计算过…

macOS上开源免费的新闻阅读器SABnzbd

SABnzbd Mac版是一款运行在Mac平台上的开源新闻阅读器&#xff0c;这款阅读器界面简约、功效简单强大&#xff0c;使用SABnzbd时可以帮助使用Python语言编写&#xff0c;让用户使用usenet新闻组更便利&#xff0c;是你阅读新闻的好帮手&#xff01; SABnzbd具有以下主要特点&a…

leetcode分类刷题:哈希表(Hash Table)(二、循环存在问题)

1、当需要快速判断某元素是否出现在序列中时&#xff0c;就要用到哈希表了。 2、本文针对的总结题型为给定的序列或需要构造的序列中是否存在循环&#xff0c;与 160. 相交链表、 141. 环形链表、142. 环形链表 II的题型一样。 202. 快乐数 这道题还考察如何对正整数求解各个位…

【PLSQL】PLSQL基础

文章目录 一&#xff1a;记录类型1.语法2.代码实例 二&#xff1a;字符转换三&#xff1a;%TYPE和%ROWTYPE1.%TYPE2.%ROWTYPE 四&#xff1a;循环1.LOOP2.WHILE&#xff08;推荐&#xff09;3.数字式循环 五&#xff1a;游标1.游标定义及读取2.游标属性3.NO_DATA_FOUND和%NOTFO…

XML—DTD、 Schema

目录 DTD是什么&#xff1f; DTD有什么用途&#xff1f; DTD与XML有什么联系&#xff1f; DTD原理图 外部DTD DTD文件book.dtd: 使用外部DTD文件的XML文件 PCDATA XML 文档构建模块 一、元素 1、元素声明 ①、有元素&#xff1a; ②、空元素&#xff1a; ③、ANY…