网络套接字补充——UDP网络编程

news/2024/5/18 13:38:49 标签: 网络, udp, 网络协议

五、UDP网络编程

​ 1.对于服务器使用智能指针维护生命周期;2.创建UDP套接字;3.绑定端口号,包括设置服务器端口号和IP地址,端口号一般是2字节使用uint16_t,而IP地址用户习惯使用点分十进制格式所以传入的是string类型,同时要保证网络字节序列;4.执行run;

5.1使用接口

5.1.1创建套接字
int socket(int domain, int type, int protocol);
//第一个参数为域/协议家族AF_UNIX, AF_LOCAL表示域间套接字,AF_INET ,AF_INET6表示网络套接字,AF_PACKET表示原始套接字,除了AF开头也可以PF开头;
//第二个参数表示socket的类型SOCK_STREAM表示流式套接字,SOCK_DGRAM表示数据包套接字;
//第三个参数如果只有一个协议则使用0;
//返回值是socket文件的文件描述符,打开的是网卡设备;

在这里插入图片描述

在这里插入图片描述

5.1.2进行绑定
#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr,
         socklen_t addrlen);
struct sockaddr {
    sa_family_t sa_family;
    char        sa_data[14];
}
//第一个参数是创建好的套接字文件描述符;
//第二个参数是输入型参数,一个特定的结构需要使用其他结构进行强转;
//第三个参数特定类型的长度
//返回值成功为0失败为-1,错误码被设置;
#include <strings.h>
void bzero(void *s, size_t n);
#include <string.h>
void *memset(void *s, int c, size_t n);
//使用上述函数进行清空
typedef uint16_t in_port_t;
typedef uint32_t in_addr_t;
struct in_addr
{
    in_addr_t s_addr;
};
typedef unsigned short int sa_family_t;
#define	__SOCKADDR_COMMON(sa_prefix) \
  sa_family_t sa_prefix##family
struct sockaddr_in
{
    __SOCKADDR_COMMON (sin_);
    in_port_t sin_port;			/* Port number.  */
    struct in_addr sin_addr;		/* Internet address.  */

    /* Pad to size of `struct sockaddr'.  */
    unsigned char sin_zero[sizeof (struct sockaddr) -
                           __SOCKADDR_COMMON_SIZE -
                           sizeof (in_port_t) -
                           sizeof (struct in_addr)];
};
sockaddr_in结构中,sin表示socket inet
sin_zero表示填充字段
sin_port表示端口号字段
sin_family表示使用的协议家族如AF_INET之类的
sin_addr表示的是IP地址结构
//local.sin_addr.s_addr = INADDR_ANY;//任意地址绑定
//字符串转整数接口
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int inet_aton(const char *cp, struct in_addr *inp);
in_addr_t inet_addr(const char *cp);//将字符串转32位并且是网络序列的;
//整数转字符串
char *inet_ntoa(struct in_addr in);//将整数转为字符串并且将网络字节序转为主机字节序
5.1.3数据报的读取
#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);
//第四个参数默认使用0以阻塞方式
ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
5.1.4数据报的发送
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
               const struct sockaddr *dest_addr, socklen_t addrlen);

5.2补充知识

1.查看网络状态

netstat -nlupa
#查看网络状态,n表示能显示成数字的就都显示成数字,l表示listen u表示udp,p表示进程,a表示所有;

2.IP问题

​ 云服务器禁止直接绑定公网IP;配置较高的机器IP地址不唯一;只是绑定一个IP地址就会导致另一个IP地址的发往另一个IP地址的数据无法被接收;一般使用的是0,这样凡是发送给本主机的数据都要根据端口号向上交付,而不是先确定IP;即IP地址使用0表示的是任意地址绑定;

3.端口号问题

​ 云服务器端口号默认0-1023是不允许绑定的,是系统内定的端口号,一般由固定的应用层协议使用如,http-80,https-443,MySQL-3306,普通用户直接使用的一般是1024以后的端口号,端口号的范围是0-65535,可以在这个范围内使用;

​ 服务器需要绑定显式端口号但是客户端不需要显式绑定,操作系统会随机绑定;因为一个端口号只能被一个进程绑定,所以重复绑定同一个端口号会出错;防止出现这种冲突,就不允许用户显示绑定而是交给了操作系统,只要保证端口号的唯一性就可以,这样就不会导致不同软件的客户端出现绑定端口号冲突;一般服务器需要显示绑定端口号是因为一般是客户端先发送的请求,所以需要知道服务端的IP地址和端口号,且端口号不能发生变化必须是固定的,这样才能让客户端知道要发送的另一端进程是谁;

​ 系统会在客户端首次发送数据的时候进行端口号的绑定;客户端可以获取多个服务器的信息所以获取信息时要包含服务端的信息;

4.本地环回地址

​ 127.0.0.1是本地环回地址,通常来进行cs的测试;

5.3echo服务器实现

服务端

#include <sys/types.h>
#include <sys/socket.h>
#include "log.hpp"
#include <string>
#include <arpa/inet.h>
#include <cstring>
#include <cstdlib>
#include <netinet/in.h>
#include <functional>

using func_t = std::function<std::string(const std::string &)>;
extern Log lg;

const uint16_t defaultport = 8080;
const std::string defaultip = "0.0.0.0";
const int size = 1024;

enum
{
    SOCKETERR = 1,
    BINDERR,
};

class udpserver
{
    public:
    udpserver(const uint16_t &port = defaultport, const std::string &ip = defaultip) : ip_(ip), port_(port), isrunning_(false) {}
    ~udpserver()
    {
        if (sockfd_ > 0)
        {
            close(sockfd_);
        }
    }

    public:
    void init()
    {
        // 1.创建socket
        sockfd_ = socket(AF_INET, SOCK_DGRAM, 0);
        if (sockfd_ < 0)
        {
            lg(Fatal, "socket create error, sockfd: %d", sockfd_);
            exit(SOCKETERR);
        }
        lg(Info, "socket create success, sockfd: %d", sockfd_);
        // 2.bind
        struct sockaddr_in local;
        bzero(&local, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(port_);
        // local.sin_addr.s_addr = inet_addr(ip_.c_str());
        local.sin_addr.s_addr = INADDR_ANY;

        int n = bind(sockfd_, (sockaddr *)&local, sizeof(local));
        if (n < 0)
        {
            lg(Fatal, "bind error, errno: %d, strerror: %s", errno, strerror(errno));
            exit(BINDERR);
        }
        lg(Info, "bind success, errno: %d, strerror: %s", errno, strerror(errno));
    }
    void run(func_t func)
    {
        isrunning_ = true;
        char buff[size];
        sockaddr_in client;
        socklen_t sz = sizeof(client);
        while (isrunning_)
        {
            ssize_t n = recvfrom(sockfd_, buff, sizeof(buff) - 1, 0, (sockaddr *)&client, &sz);
            if (n < 0)
            {
                lg(Warning, "recvfrom error, errno: %d, strerror: %s", errno, strerror(errno));
                continue;
            }
            buff[n] = 0;
            // 处理数据
            std::string info = buff;
            std::string echo_string = func(info);
            sendto(sockfd_, echo_string.c_str(), echo_string.size(), 0, (sockaddr *)&client, sz);
        }
    }

    private:
    int sockfd_;     // 网络文件描述符
    std::string ip_; // IP地址,1.将字符串风格转换为uint_t类型,2.转换成网络字节序列
    uint16_t port_;  // 端口号转换成网络字节序列
    bool isrunning_;
};--

void Usage(string proc)
{
    cout << "\n\rUsage: " << proc << " port[1024+]" << endl;
}

string handler(const string &str)
{
    cout << str << endl;
    string ret = "server get a message: ";
    ret += str;
    return ret;
}

int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        Usage(argv[0]);
        exit(0);
    }
    uint16_t port = stoi(argv[1]);
    unique_ptr<udpserver> svr(new udpserver(port, "127.0.0.1"));
    svr->init();
    svr->run(handler);
    return 0;
}

客户端

#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <cstring>
#include <cstdlib>
#include <arpa/inet.h>
#include <netinet/in.h>
#include "log.hpp"
extern Log lg;

void Usage(std::string proc)
{
    std::cout << "\n\rUsage: " << proc << " serverip serverport\n"
              << std::endl;
}
int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        Usage(argv[0]);
        return 0;
    }
    std::string serverip = argv[1];
    uint16_t serverport = std::stoi(argv[2]);
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0)
    {
        std::cerr << "socket error" << std::endl;
        exit(1);
    }

    sockaddr_in server;
    bzero(&server, sizeof(server));
    server.sin_family = AF_INET;
    server.sin_port = htons(serverport);
    server.sin_addr.s_addr = inet_addr(serverip.c_str());

    std::string message;
    char buff[1024];
    while (true)
    {
        std::cout << "client say@ ";
        std::getline(std::cin, message);
        sendto(sockfd, message.c_str(), message.size(), 0, (sockaddr *)&server, sizeof(server));
        sockaddr_in temp;
        socklen_t len = sizeof(temp);
        ssize_t n = recvfrom(sockfd, buff, sizeof(buff) - 1, 0, (sockaddr *)&temp, &len);
        if (n > 0)
        {
            buff[n] = 0;
            std::cout << buff << std::endl;
        }
    }

    close(sockfd);
    return 0;
}

5.4服务器实现执行指令

#include <stdio.h>
FILE *popen(const char *command, const char *type);
//是一个封装起来的管道和子进程执行命令的接口,会自动建立管道,让子进程执行命令并将子进程的执行结果通过管道返回给父进程;
//第一个参数是子进程要执行的命令,第二个参数是对打开文件的方式,读写;
//返回值是用来进行获取执行结果的;
int pclose(FILE *stream);
char *fgets(char *s, int size, FILE *stream);
//读取一行,当读到EOF/空时返回nullptr;
bool safecheck(const string &cmd)
{
    int issafe = false;
    vector<string> keyword;
    keyword.push_back("rm");
    keyword.push_back("mv");
    keyword.push_back("kill");
    for (auto &e : keyword)
    {
        auto pos = cmd.find(e);
        if (pos != string::npos)
        {
            return issafe;
        }
    }
    issafe = true;
    return issafe;
}

string execute(const string &cmd)
{
    if (!safecheck(cmd))
    {
        return "unsafe";
    }
    FILE *fp = popen(cmd.c_str(), "r");
    if (fp == nullptr)
    {
        perror("popen error: ");
        return "error";
    }
    char buff[4096];
    string ret;
    while (true)
    {
        char *res = fgets(buff, sizeof(buff), fp);
        if (res == nullptr)
        {
            break;
        }
        ret += buff;
    }
    cout << "get a cmd: " << cmd << endl;
    pclose(fp);
    return ret;
}

​ Xshell的原理就是,远端执行了一个22服务,即ssh,客户端发送指令,服务端进行执行后返回结果;

在这里插入图片描述

5.5Windows和Linux通信

#include<iostream>
#include<WinSock2.h>//网络套接字接口

#pragma comment(lib,"ws2_32.lib")//链接静态库

using namespace std;

int main()
{
	cout << "hello client" << endl;
	WSADATA wsd;//创建变量
	int n = WSAStartup(MAKEWORD(2, 2), &wsd);//初始化
    //通信过程
	WSACleanup();//释放变量
	return 0;
}
#define _WINSOCK_DEPRECATED_NO_WARNINGS 1
#include<iostream>
#include<WinSock2.h>//网络套接字接口
#include<string>

#pragma comment(lib,"ws2_32.lib")//链接静态库

using namespace std;
const uint16_t serverport = 8080;
const string serverip = "60.205.138.126";
int main()
{
    cout << "hello client" << endl;
    WSADATA wsd;
    int n = WSAStartup(MAKEWORD(2, 2), &wsd);
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0)
    {
        cout << "socket error" << endl;
    }
    sockaddr_in server;
    memset(&server, 0, sizeof(server));
    server.sin_family = AF_INET;
    server.sin_port = htons(serverport);
    server.sin_addr.s_addr = inet_addr(serverip.c_str());

    std::string message;
    char buff[1024];
    while (true)
    {
        std::cout << "client say@ ";
        std::getline(std::cin, message);
        sendto(sockfd, message.c_str(), message.size(), 0, (sockaddr*)&server, sizeof(server));
        sockaddr_in temp;
        int len = sizeof(temp);
        int n = recvfrom(sockfd, buff, sizeof(buff) - 1, 0, (sockaddr*)&temp, &len);
        if (n > 0)
        {
            buff[n] = 0;
            std::cout << buff << std::endl;
        }
    }
    closesocket(sockfd);
    WSACleanup();
    return 0;
}


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

相关文章

Visual studio 下载扩展太慢, 怎么办?

Visual studio 下载扩展太慢&#xff0c; 怎么办&#xff1f; 下载慢的原因&#xff1a; 下载地址在国外服务器上&#xff0c;就像github一样&#xff0c;连接很慢 解决 采用vpn代理服务 1、安装浏览器插件 无忧行 2、在该插件的控制面板代理国外服务 3、代理完成后可以在如…

实时数仓之实时数仓架构(Hudi)

目前比较流行的实时数仓架构有两类&#xff0c;其中一类是以FlinkDoris为核心的实时数仓架构方案&#xff1b;另一类是以湖仓一体架构为核心的实时数仓架构方案。本文针对FlinkHudi湖仓一体架构进行介绍&#xff0c;这套架构的特点是可以基于一套数据完全实现Lambda架构。实时数…

C# 高级文件操作与异步编程探索(初步)

文章目录 文本文件的读写探秘StreamReader 类深度剖析StreamWriter 类细节解读编码和中文乱码的解决方案 二进制文件的读写BinaryReader 类全面解析BinaryWriter 类深度探讨 异步编程与C#的未来方向同步与异步&#xff1a;本质解读Task 的神奇所在async/await 的魔法 在现代编程…

Rust编程(五)终章:查漏补缺

闭包 & 迭代器 闭包&#xff08;Closure&#xff09;通常是指词法闭包&#xff0c;是一个持有外部环境变量的函数。外部环境是指闭包定义时所在的词法作用域。外部环境变量&#xff0c;在函数式编程范式中也被称为自由变量&#xff0c;是指并不是在闭包内定义的变量。将自…

【IP 组播】PIM-SM

目录 原理概述 实验目的 实验内容 实验拓扑 1.基本配置 2.配置IGP 3.配置PIM-SM 4.用户端DR与组播源端DR 5.从RPT切换到SPT 6.配置PIM-Silent接口 原理概述 PIM-SM 是一种基于Group-Shared Tree 的组播路由协议&#xff0c;与 PIM-DM 不同&#xff0c;它适合于组播组成…

Win10 搭建FTP存储服务器站点【超详细教程】

目录 第一步&#xff1a;打开控制面板>程序 第二步&#xff1a;win10左下角搜索IIS并打开 第三步&#xff1a;右键网站&#xff0c;选择添加FTP站点 第四步&#xff1a;添加FTP站点名称 第五步&#xff1a;添加IP地址和端口 第六步&#xff1a;身份验证与授权信息 第…

Mac添加和关闭开机应用

文章目录 mac添加和关闭开机应用添加开机应用删除/查看 mac添加和关闭开机应用 添加开机应用 删除/查看 打开&#xff1a;系统设置–》通用–》登录项–》查看登录时打开列表 选中打开项目&#xff0c;点击“-”符号

项目四-图书管理系统

1.创建项目 流程与之前的项目一致&#xff0c;不再进行赘述。 2.需求定义 需求: 1. 登录: ⽤⼾输⼊账号,密码完成登录功能 2. 列表展⽰: 展⽰图书 3.前端界面测试 无法启动&#xff01;&#xff01;&#xff01;--->记得加入mysql相关操作记得在yml进行配置 配置后启动…