【网络编程】网络编程套接字(二)简单的UDP网络程序

news2024/12/23 3:49:34

文章目录

  • 服务器编程
    • 1.创建服务端套接字
    • 2.绑定服务端套接字
    • 3.服务端启动
  • 客户端编程
    • 1.创建客户端套接字
    • 2.绑定客户端套接字
  • 服务器和客户端测试

服务器编程

1.创建服务端套接字

使用socket函数调用可以创建套接字的文件描述符,与前边的文件类似,socket函数的返回值是文件描述符,其实套接字是被进程创建的,例如在服务器端的某一个进程需要与客户端的一个进程进行网络通信就要创建套接字,进程的管理是通过进程控制块PCB来实现的,创建套接字就是创建了一个struct_files结构体,而在PCB:task_struct中有一个struct files_struct* files的指针,指针指向一个表,这个表中有一部分是进程的文件描述符表:fd_array,在该表的某一位置写入该结构体的指针,就是创建了套接字。
在这里插入图片描述
当我们创建一个套接字时,其实就是进程打开了一个网络文件,而打开文件就会创建文件结构体,并将地址填入PCB的文件描述符表中,所以socket函数的返回值就是文件描述符,只打开一个文件,文件描述符就是3,前边分别打开了标准输入,标准输出,标准错误,并且这些文件结构体是通过双向链表的结构保存的。

在这里插入图片描述
每一个文件结构体struct_file中都包含该文件的属性,该文件的缓冲区,该文件的操作方法,例如如何打开和关闭文件,因为在系统中,我们可以把一切硬件看作文件,只是给予不同的操作方法就可以操作文件,而对于普通文件来说,缓冲区被刷新后对应的地方就是硬盘,而对于网络编程套接字来说,缓冲区刷新后,把数据发送到网卡。

socket函数

// 创建 socket 文件描述符 (TCP/UDP, 客户端 + 服务器)
int socket(int domain, int type, int protocol);

在这里插入图片描述

参数
domain:套接字的域,也就是套接字的类型,例如常用的两个域:
AF_INET IPv4 Internet protocols ip(7)
AF_INET6 IPv6 Internet protocols ipv6(7)
type:套接字创建时的服务类型,例如:
SOCK_STREAM 表示流协议
SOCK_DGRAM 表示数据报协议
protocol:通常传入0,可以根据前边的参数,推出使用UDP协议还是TCP协议

返回值:返回值就是创建套接字成功后返回的文件描述符,错误返回-1.

2.绑定服务端套接字

在创建好套接字之后,本质上就是在系统层面打开了一个文件结构体,但是此时系统并不清楚打开的文件是网络文件还是普通文件,所以就要将套接字与IP和端口号进行绑定,在绑定套接字时,必须先传入一个sockaddr_in类型的结构体,并且必须使用IP,端口号,协议家族进行填充,此时就可以将套接字与网络进行绑定。

bind函数

// 绑定端口号 (TCP/UDP, 服务器) 
int bind(int socket, const struct sockaddr *address,
 socklen_t address_len);

参数
socket:前边创建套接字时生成的对应文件描述符
address:sockaddr_in或者sockaddr_un的地址,选择对应网络通信还是域间通信
len:传入sockaddr结构体的长度

返回值
绑定成功返回0,失败返回-1

但是还有一些要注意的地方

  • bind函数的第二个参数是struct sockaddr* ,所以在传参时,必须进行强转。
  • 在对sockaddr_in结构体进行填充时,端口号从主机发送到网络,必须使用htons或者htonl函数将端口号转为大端模式。
  • 在填充时,IP地址为了让用户观察,所以设置为点分十进制,但是发送到网络之后,确是一个四字节的整数,所以必须要进行转换,使用inet_addr接口可以将点分十进制转换为四字节整数,而使用inet_ntoa可以将四字节整数转换为点分十进制,并且这两个接口也有前边处理大小端问题的作用。
    在这里插入图片描述
    sockaddr_in的结构
    在这里插入图片描述
    在这里插入图片描述
    在sockaddr_in结构体中主要有IP地址,端口号,已经协议家族,在绑定时,套接字就是根据sockaddr_in 中填充的这些数据来绑定。

server.hpp

class udp_server
{
public:
    udp_server(const uint16_t& port, const std::string& ip = "0.0.0.0")
        : _sockfd(-1), _ip(ip), _port(port)
    {}
    void server_init()
    {
        // 创建套接字
        _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
        if (_sockfd < 0)
        {
            logMessage(FATAL, "%d:%s", errno, strerror(errno));
            exit(2);
        }
         std::cout << "socket success: " << _sockfd << std::endl;
        // 将套接字与网络绑定
        struct sockaddr_in local;
        bzero(&local, sizeof(local));
        local.sin_addr.s_addr = inet_addr(_ip.c_str());
        local.sin_family = AF_INET;
        local.sin_port = htons(_port);
        if (bind(_sockfd, (struct sockaddr *)&local, sizeof(local)) < 0)
        {
            logMessage(FATAL, "%d:%s", errno, strerror(errno));
            exit(2);
        }
    }
 private:
    int _sockfd;
    std::string _ip;
    uint16_t _port;
};

server.cc

 #include<iostream>
#include"udp_server.hpp"
#include<memory>
#include <cstdlib>

static void usage(std::string proc)
{
    std::cout << "\nUsage: " << proc << " port\n" << std::endl;
}
int main(int argc, char *argv[])
{
    if(argc != 2)
    {
        usage(argv[0]);
        exit(1);
    }
    uint16_t port = atoi(argv[1]);
    std::unique_ptr<udp_server> svr(new udp_server(port));
    svr->server_init();
    svr->start();
    return 0;
}

3.服务端启动

由于服务器必须不间断的提供服务,所以必须不断循环等待客户端发送消息,首先要对sockaddr_in结构体进行填充,通过recvfrom接口接收数据时,同时也可以接收到客户端的IP地址和端口号,因为处理完毕要发送的时候就可以知道要给哪一个IP的哪个进程。

recvfrom接口
在这里插入图片描述

参数
sockfd:创建套接字生成的文件描述符
buf:要接收内容的缓冲区
len:接收内容的字节数
flags:读取方式,一般设置为0,阻塞读取
src_addr:对端网络的相关属性,例如协议家族,IP地址,端口号
addrlen:期望读取对端网络结构体的长度,这是一个输入输出型参数

返回值
读取成功返回0,失败返回-1

sendto接口
在这里插入图片描述

参数
sockfd:套接字的文件描述符
buf:要发送的数据
len:期望发送数据的字节数
flags:写入的方式,通常为0,表示阻塞写入
dest_addr:目标网络的属性,包含通信协议,IP地址,端口地址
addrlen:传入sockaddr结构体的大小\

返回值
成功发送返回0,发送错误返回-1

    void start()
    {
        char buffer[SIZE];
        for (;;)
        {
            struct sockaddr_in peer;
            socklen_t size = sizeof(peer);  
             bzero(&peer, sizeof(peer));
            ssize_t s = recvfrom(_sockfd, buffer, sizeof(buffer), 0, (struct sockaddr *)&peer, &size);
            if (s > 0)
            {
                buffer[s] = 0;
                uint16_t client_port = ntohs(peer.sin_port);
                std::string client_ip = inet_ntoa(peer.sin_addr);

                printf("[%s:%d]# %s\n", client_ip.c_str(), client_port, buffer);
            }

            sendto(_sockfd, buffer, strlen(buffer), 0, (struct sockaddr *)&peer, size);
        }
    }

客户端编程

1.创建客户端套接字

客户端套接字的创建与服务端的创建类似,只是在创建套接字之后,不需要进行绑定端口号和IP地址,因为服务器是给为了给别人提供服务,所以必须让别人知道IP地址和端口号,而且不能轻易更改,但是客户端的端口号只要是唯一的就可以了,不需要进行绑定,如何客户端只是绑定唯一的一个端口号,那么当一个进程没有启动,那么这个端口也不能给别人使用,如果这个歌这个端口被别人使用了,此时这个进程也就不能启动了,所以不进行绑定,而是在进程启动时分配端口号,可以提高利用率。

client.cc

#include<iostream>
#include"udp_client.hpp"

static void usage(std::string proc)
{
    std::cout << "\nUsage: " << proc << " port\n" << std::endl;
}
int main(int argc, char* argv[])
{
    if(argc != 3)
    {
        usage(argv[0]);
        exit(1);
    }
	std::string server_ip = argv[1];
	int server_port = atoi(argv[2]);
	udp_client* clt = new udp_client(server_port,server_ip);
	clt->client_init();
	clt->start();
	return 0;
}

client.hpp

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

#define SIZE 1024
class udp_client
{
public:
    udp_client(uint16_t port, std::string ip)
        : _sockfd(-1), _port(port), _ip(ip)
    {
    }
    bool client_init()
    {
        _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
        if (_sockfd < 0)
        {
            logMessage(FATAL, "%d:%s", errno, strerror(errno));
            exit(2);
        }
        return true;
    }
    ~udp_client()
    {
        if (_sockfd > 0)
            close(_sockfd);
    }

private:
    int _sockfd;
    uint16_t _port;
    std::string _ip;
};

2.绑定客户端套接字

首先要对sockaddr_in结构体进行填充,填充时要注意前边的几点,但是在将字符串风格的IP地址转换为整形风格的IP时,首先要将string类型转为C语言风格的字符串。

    void start()
    {
        struct sockaddr_in peer;
        memset(&peer, '\0', sizeof(peer));
        peer.sin_family = AF_INET;
        peer.sin_port = htons(_port);
        peer.sin_addr.s_addr = inet_addr(_ip.c_str());
        std::string msg;
        for (;;)
        {
            char buffer[SIZE];
            std::cout << "please input#";
            std::getline(std::cin, msg);
            
            sendto(_sockfd, msg.c_str(), msg.size(), 0, (struct sockaddr *)&peer, sizeof(peer));

            struct sockaddr_in temp;
            socklen_t size = sizeof(temp);
            ssize_t s = recvfrom(_sockfd, buffer, sizeof(buffer)-1, 0, (struct sockaddr *)&temp, &size);
            if (s > 0)
            {
                buffer[s] = 0;
                std::cout << "server echo# " << buffer << std::endl;
            }
        }
    }

服务器和客户端测试

直接运行,说明书提示,服务器必须提供端口号,客户端必须提供服务器的端口号和IP
在这里插入图片描述
在这里插入图片描述
当我们正确的写上命令行参数时,就可以进行通信。我们可以先进行本地测试,此时服务器没有绑定外网,绑定的是本地环回。现在我们运行服务器时指明端口号为8081,再运行客户端,此时客户端要访问的服务器的IP地址就是本地环回127.0.0.1,服务端的端口号就是8081。
在这里插入图片描述
同时,我们可以使用netstat指令来获得网络信息。
在这里插入图片描述
在进行本地测试之后,也可以将自己的公网IP和端口号告诉朋友,可以让朋友试一下是否可以进行网络通信:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/715015.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【基础算法】递归算法

递归算法是一种直接或间接调用原算法的算法&#xff0c;一个使用函数自身给出定义的函数被称为递归函数。利用递归算法可以将规模庞大的问题拆分成规模较小的问题&#xff0c;从而使问题简化。无论是递归算法还是递归函数&#xff0c;最大的特点都是“自己调用自己”。 斐波那…

nRF52832蓝牙概述

基本概念 RSSI&#xff08;Received Signal Strength Indicator&#xff09;是接收信号的强度指示。 接收包RSSI是指无线模块发送信息后&#xff0c;接收端的无线模块接收到数据后&#xff0c;当前接收数据的信号强度的寄存器值&#xff0c;也就是接收模块获取到发送模块当前发…

Vector - CAPL - 数据库和CAPL_01

目录 获取CAN总线报文信息 静态访问报文信息 动态访问报文信息 静态访问数据库信息 DBLookup&#xff08;Access Message & Signal&#xff09; 1、报文类型信息 2、类型信息 3、节点信息 获取CAN总线报文信息 我们在做CAN网络管理或者通信的测试的过程中&#xf…

LLM prompt提示构造案例

参考&#xff1a; https://github.com/PlexPt/awesome-chatgpt-prompts-zh 吴恩达 prompt工程应用&#xff1a; https://www.bilibili.com/video/BV1No4y1t7Zn prompt构造案例代码 prompt """文本分类任务&#xff1a;将一段用户给外卖服务的评论进行分类…

LSTM已死,Transformer永生(面试问答RNN/LSTM/Transformer)

计算机视觉面试题-Transformer相关问题总结&#xff1a;https://zhuanlan.zhihu.com/p/554814230 计算机视觉面试31题 CV面试考点&#xff0c;精准详尽解析&#xff1a;https://zhuanlan.zhihu.com/p/257883797 1. 循环神经网络&#xff08;Recurrent Neural Networks, RNN&am…

渲染颜色透明度异常分析 Unity RenderTexture

解决 Unity Canvas 相机 RenderTarget 渲染颜色透明度异常的问题 问题 重现场景 新建一个 Canvas-RT , Canvas Render Mode 改为 Screen Space - Camera 。 为 Canvas-RT 添加一个 RawImage-Origin 对象&#xff0c;修改颜色透明度为 120 。 修改 Camera-RT 中的 Target Textur…

如何实现Nginx+Tomcat反向代理与负载均衡

目录 一、正向代理与反向代理 正向代理 反向代理 二、负载均衡 什么是负载均衡 代码仓库定义 分流策略 权重 least_conn&#xff08;最少连接&#xff09; ip_hash&#xff08;负载均衡模式&#xff09; fair&#xff08;第三方负载均衡模式&#xff09; url_hash&…

算法设计与分析 课程期末复习简记

目录 网络流 线性规划 回溯算法 分支限界 贪心算法 动态规划 分治算法 算法复杂度分析 相关概念 网络流 下面是本章需要掌握的知识 • 流量⽹络的相关概念 • 最⼤流的概念 • 最⼩割集合的概念 • Dinic有效算法的步骤 • 会⼿推⼀个流量⽹络的最⼤流 下面对此依次进行复…

基于AUTOSAR的ECU启动阶段哪些事儿

AUTOSAR实战篇:EcuM启动时序大总结 前言 小T出品,必是精品! EcuM启动时序大总结,你值得拥有! 正文 正如小T前文中《AUTOSAR基础篇之EcuM》中讲到的那样,AUTOSAR架构中将ECU的上下电过程统一由单一的模块来进行统一管理,即EcuM模块。 虽然不同硬件的初始化过程不一样,但…

Redis是什么

Redis是什么 https://blog.csdn.net/Little_Oranges/article/details/121870705 1.简介 基于字典格式的。基于内存&#xff0c;高性能的。键值对的方式进行存储。可以存储多种数据结构类型的数据。 2.应用场景 缓存排行榜计数器分布式会话分布式锁社交网络最新列表消息系统 …

华为OD机试真题 JavaScript 实现【查找充电设备组合】【2023Q1 100分】

目录 一、题目描述二、输入描述三、输出描述四、补充说明五、JavaScript算法源码六、效果展示1、输入2、输出3、说明一、题目描述 某个充电站,可提供n个充电设备,每个充电设备均有对应的输出功率。任意个充电设备组合的输出功率总和,均构成功率集合P的1个元素。功率集合P的…

Qt Quick系列(8)—Model-View—视图信号

&#x1f680;作者&#xff1a;CAccept &#x1f382;专栏&#xff1a;Qt Quick 文章目录 前言代码示例源码关键知识点 总结 前言 在Qt Quick的Model-View中内置视图有很多&#xff0c;有Repeater、ListView、GridView…&#xff0c;而他们也有着自己的信号&#xff0c;比如…

WebDAV之派盘本地个人云+Documents

Documents是一款由Readdle开发的文档管理和编辑工具&#xff0c;支持PDF阅读、音频播放、图像浏览和标注、以及多种常见文档格式的编辑操作等。派盘是一款本地私有云产品&#xff0c;基于WebDAV、FTP、SMB等多种协议&#xff0c;提供文件存储、分享、同步、备份等服务&#xff…

【Squid一】Squid代理服务器应用

Squid代理服务器应用 1.Squid代理服务器1.1 正向代理的工作机制1.2 代理服务器的概念及其作用1.3 代理服务器主要作用1.4 Squid代理的类型 2.CDN2.1 CDN概述2.2 CDN优势2.3 CDN对网络的优化作用2.4 CDN访问过程2.5 CDN网络的组成要素 3.安装Squid服务3.1 使用脚本启动和关闭squ…

guest内核不响应导致磁盘卸载问题排查

用户问题 客户报障磁盘卸载不了&#xff0c;而且是经常出现卸载不了的情况&#xff0c;客户比较着急&#xff0c;同时PDD也是大客户。 排查过程 查看宿主机上虚拟机信息 1、用户虚拟机有14块磁盘&#xff0c;而且这14块都是以legacypci的方式插入虚拟机&#xff0c;我印象中…

MATLAB | 拉普拉斯分布/拉普拉斯噪声的生成

一、实验目标 生成拉普拉斯分布的噪声&#xff0c;并分析它的概率密度函数 二、解决思路 &#xff08;1&#xff09;拉普拉斯分布可以由指数分布生成 拉普拉斯的概率密度函数为 f ( x ; μ , λ ) 1 2 λ e − ∣ x − μ ∣ λ f(x;\mu,\lambda)\frac{1}{2 \lambda} e^{…

创建启动前端vue与后端python/flask,前后端分离,相互传递参数

创建启动vue 确保你已经安装了Node.js和npm 安装vue npm install -g vue/cli创建vue项目&#xff1a; vue create my-project cd my-project启动vue npm run serve如果安装vue报错&#xff1a;管理员权限模式打开powershell Windows PowerShell 版权所有&#xff08;C&#…

windows怎么查看目标文件.o and windows - 如何使用/安装 GNU binutils (objdump)

GNU binutils-objdump工具 一、windows怎么查看目标文件.o二、安装GNU binutils (objdump)三、使用GNU binutils (objdump)参考资料 一、windows怎么查看目标文件.o 可以使用GNU binutils (objdump)进行查看编译生成的目标文件.o。 二、安装GNU binutils (objdump) 点击下载…

Apache Airflow 多个 Provider 存在漏洞

项目介绍 Airflow 是一个使用 python 语言编写的 data pipeline 调度和监控工作流的平台。 Airflow 是通过 DAG&#xff08;Directed acyclic graph 有向无环图&#xff09;来管理任务流程的任务调度工具&#xff0c; 不需要知道业务数据的具体内容&#xff0c;设置任务的依赖…

辅助驾驶功能开发-功能规范篇(21)-4-XP行泊一体方案功能规范

XPilot Parking 自动泊车系统 • 超级自动泊车辅助(Super AutoParking Assist)、语音控制泊车辅助(Autoparking with Speech) - 产品定义 超级自动泊车辅助是⼀个增强的自动泊车辅助系统。在超级自动泊车辅助系统中,识别车位将会变得实时可见, 并且不可泊入的⻋位也将…