网络(二)——套接字编程

文章目录

  • 理解源IP地址和目的IP地址
  • 认识端口号
  • 认识TCP/UDP协议
  • 网络字节序
  • socket编程接口
    • socket 常见API
    • sockaddr结构

理解源IP地址和目的IP地址

在IP数据包头部中, 有两个IP地址, 分别叫做源IP地址, 和目的IP地址;
源IP即发送方的地址,目的IP即接受方的地址;
但只有IP是不够的,完不成通信;
IP只能确认我们将信息发到哪一台机器上,但具体发给哪一个进程是不确定的;
因此,还需要有一个其他的标识来区分出,要发给哪个进程;

认识端口号

端口号(port)是传输层协议的内容

  • 端口号是一个2字节16位的整数
  • 端口号用来标识一个进程, 告诉操作系统, 当前的这个数据要交给哪一个进程来处理
  • IP地址 + 端口号能够标识网络上的某一台主机的某一个进程
  • 一个端口号只能被一个进程占用
IP表示目的地址
端口号(port)用来表示唯一一个服务的
IP + port可以标识全网唯一的服务

前面我们谈到PID表示唯一一个进程;此时端口号表示唯一一个进程;
它们之间有什么关系么?

一个进程可以绑定多个端口号;但是一个端口号不能被多个进程绑定;
端口号是传输层到应用层寻找服务时需要使用的字段,而进程的PID用于操作系统管理不同的进程;

认识TCP/UDP协议

TCP协议

  • 传输层协议
  • 有连接:通信前需要先建立连接
  • 可靠传输:TCP协议有一些措施来保证传输的可靠性
  • 面向字节流

UDP协议

  • 传输层协议
  • 无连接:通信前不需要建立连接
  • 不可靠传输:无措施来保证可靠性
  • 面向数据报

网络字节序

我们已经知道,内存中的多字节数据相对于内存地址有大端和小端之分, 磁盘文件中的多字节数据相对于文件中的偏移地址也有大端小端之分, 网络数据流同样有大端小端之分. 那么如何定义网络数据流的地址呢?

  • 发送主机通常将发送缓冲区中的数据按内存地址从低到高的顺序发出;
  • 接收主机把从网络上接到的字节依次保存在接收缓冲区中,也是按内存地址从低到高的顺序保存;
  • 因此,网络数据流的地址应这样规定:先发出的数据是低地址,后发出的数据是高地址.
  • TCP/IP协议规定,网络数据流应采用大端字节序,即低地址高字节.
  • 不管这台主机是大端机还是小端机, 都会按照这个TCP/IP规定的网络字节序来发送/接收数据;
  • 如果当前发送主机是小端, 就需要先将数据转成大端; 否则就忽略, 直接发送即可;

在这里插入图片描述
调用以下库函数做网络字节序和主机字节序的转换

在这里插入图片描述
如果主机是小端字节序,这些函数将参数做相应的大小端转换然后返回;
如果主机是大端字节序,这些函数不做转换,将参数原封不动地返回;

socket编程接口

socket 常见API

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

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

// 开始监听socket (TCP, 服务器)
int listen(int socket, int backlog);

// 接收请求 (TCP, 服务器)
int accept(int socket, struct sockaddr* address, socklen_t* address_len);

// 建立连接 (TCP, 客户端)
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

sockaddr结构

套接字不仅支持跨网络的进程间通信,还支持本地的进程间通信(域间套接字)。在进行跨网络通信时我们需要传递的端口号和IP地址,而本地通信则不需要,因此套接字提供了sockaddr_in结构体和sockaddr_un结构体,其中sockaddr_in结构体是用于跨网络通信的,而sockaddr_un结构体是用于本地通信的。

为了让套接字的网络通信和本地通信能够使用同一套函数接口,于是就出现了sockeaddr结构体,该结构体与sockaddr_in和sockaddr_un的结构都不相同,但这三个结构体头部的16个比特位都是一样的,这个字段叫做协议家族。
在这里插入图片描述
此时当我们在传递在传参时,就不用传入sockeaddr_in或sockeaddr_un这样的结构体,而统一传入sockeaddr这样的结构体。在设置参数时就可以通过设置协议家族这个字段,来表明我们是要进行网络通信还是本地通信,在这些API内部就可以提取sockeaddr结构头部的16位进行识别,进而得出我们是要进行网络通信还是本地通信,然后执行对应的操作。此时我们就通过通用sockaddr结构,将套接字网络通信和本地通信的参数类型进行了统一。

实际我们在进行网络通信时,定义的还是sockaddr_in这样的结构体,只不过在传参时需要将该结构体的地址类型进行强转为sockaddr。
UdpServer

#pragma once

#include <iostream>
#include <string>
#include <cerrno>
#include <cstring>
#include <unistd.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "nocopy.hpp"
#include "Log.hpp"
#include "Comm.hpp"
#include "InetAddr.hpp"

const static uint16_t defaultport = 8888;
const static int defaultfd = -1;
const static int defaultsize = 1024;

class UdpServer
{
public:
    UdpServer(uint16_t port = defaultport)
        : _port(port), _sockfd(defaultfd)
    {
    }
    void Init()
    {
        // 1. 创建socket,就是创建了文件
        _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
        if (_sockfd < 0)
        {
            lg.LogMessage(Fatal, "socket errr, %d : %s\n", errno, strerror(errno));
            exit(Socket_Err);
        }

        lg.LogMessage(Info, "socket success, sockfd: %d\n", _sockfd);

        // 2. 绑定,指定网络信息
        struct sockaddr_in local;
        bzero(&local, sizeof(local)); // memset
        local.sin_family = AF_INET;
        local.sin_port = htons(_port);
        local.sin_addr.s_addr = INADDR_ANY; // 0

        // 填写结构体
        int n = ::bind(_sockfd, (struct sockaddr *)&local, sizeof(local));
        if (n != 0)
        {
            lg.LogMessage(Fatal, "bind errr, %d : %s\n", errno, strerror(errno));
            exit(Bind_Err);
        }
    }
    void Start()
    {
        // 服务器不退出
        char buffer[defaultsize];
        for (;;)
        {
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            ssize_t n = recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&peer, &len);
            if (n > 0)
            {
                InetAddr addr(peer);
                buffer[n] = 0;
                std::cout << "[" << addr.PrintDebug() << "]# " << buffer << std::endl;
                sendto(_sockfd, buffer, strlen(buffer), 0, (struct sockaddr *)&peer, len);
            }
        }
    }
    ~UdpServer()
    {
    }

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

UdpClient

#include <iostream>
#include <cerrno>
#include <cstring>
#include <string>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>

void Usage(const std::string &process)
{
    std::cout << "Usage: " << process << " server_ip server_port" << std::endl;
}

// ./udp_client server_ip server_port
int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        Usage(argv[0]);
        return 1;
    }

    std::string serverip = argv[1];
    uint16_t serverport = std::stoi(argv[2]);

    // 1. 创建socket
    int sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock < 0)
    {
        std::cerr << "socket error: " << strerror(errno) << std::endl;
        return 2;
    }
    std::cout << "create socket success: " << sock << std::endl;
    
    struct 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());

    while (true)
    {
        // 我们要发的数据
        std::string inbuffer;
        std::cout << "Please Enter# ";
        std::getline(std::cin, inbuffer);
        // 我们要发给server
        ssize_t n = sendto(sock, inbuffer.c_str(), inbuffer.size(), 0, (struct sockaddr*)&server, sizeof(server));
        if(n > 0)
        {
            char buffer[1024];
            //收消息
            struct sockaddr_in temp;
            socklen_t len = sizeof(temp);
            ssize_t m = recvfrom(sock, buffer, sizeof(buffer)-1, 0, (struct sockaddr*)&temp, &len); 
            if(m > 0)
            {
                buffer[m] = 0;
                std::cout << "server echo# " << buffer << std::endl;
            }
            else
                break;
        }
        else
            break;
    }

    close(sock);
    return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

创建yaml文件并读取

yaml文件可以存放token数据&#xff0c;那怎么操作存放并读取呢&#xff1f; 1、创建yaml文件 1、导入类库 import yaml 注意要导入的类库&#xff0c;在解释器中叫pyaml 2、创建文件 #创建yaml文件 yamlfileopen("testdata.yaml",w,encodingutf-8) 3、设置数据…

PostgreSQL 中如何解决因频繁的小事务导致的性能下降?

&#x1f345;关注博主&#x1f397;️ 带你畅游技术世界&#xff0c;不错过每一次成长机会&#xff01;&#x1f4da;领书&#xff1a;PostgreSQL 入门到精通.pdf 文章目录 PostgreSQL 中解决因频繁小事务导致性能下降的方法 PostgreSQL 中解决因频繁小事务导致性能下降的方法…

电气工程VR虚拟仿真实训平台以趣味化方式增强吸引力

在工业4.0时代和教育信息化的双重推动下&#xff0c;我们致力于推动实训课件的跨界合作与共创。VR实训课件不仅促进了不同领域、不同行业之间的紧密合作&#xff0c;更让学习变得生动直观。我们凭借3D技术生动、直观、形象的特点&#xff0c;开发了大量配套3D教材&#xff0c;让…

如何允许从互联网(外网)进入路由器管理页面

1.绑定UDP端口 操作如图所示&#xff1a; 2.然后再绑定虚拟换回网卡 3.然后再把出端口编号设置成为2 使他成为一个双向输入输出具体操作如图所示&#xff1a; 4.进入防火墙然后再启动防火墙进行端口配置&#xff1a; 1.进入端口g0/0/0配置ip地址&#xff08;注意配置的ip地…

游戏热更新——AssetBundle

AssetBundle AssetBundle的定义与使用 AssetBundle是一个压缩包包含模型、贴图、预制体、声音、甚至是整个场景&#xff0c;可以在游戏运行的时候被加载 AssetBundle自身保存着相互的依赖关系 压缩包可以使用LZMA和LZ4压缩算法&#xff0c;减少包大小&#xff0c;更快的进行网…

第五天安全笔记(持续更新)

第五天防御笔记 NAT种类&#xff1a; 静态NAT动态NATNapt 特点&#xff1a; 一对多----easy ip 多对多的napt 服务器的映射关系: 1.源NAT----基于IP地址进行转换&#xff0c;包括静态NAT&#xff0c;动态NAT&#xff0c;以及NAPT 2.目标NAT---基于目标IP地址进行转换&a…

网络故障处理及分析工具:Wireshark和Tcpdump集成

Wireshark 是一款免费的开源数据包嗅探器和网络协议分析器&#xff0c;已成为网络故障排除、分析和安全&#xff08;双向&#xff09;中不可或缺的工具。 本文深入探讨了充分利用 Wireshark 的功能、用途和实用技巧。 无论您是开发人员、安全专家&#xff0c;还是只是对网络操…

【Linux杂货铺】3.程序地址空间

1.程序地址空间的引入 fork(&#xff09;函数在调用的时候子如果是子进程则返回0&#xff0c;如果是父进程则返回子进程的pid&#xff0c;在代码中我们分别在子进程和父进程读取全局变量g_val的时候居然出现了俩个不同的值。如下&#xff1a; #include<stdio.h> #includ…

t-SNE降维可视化并生成excel文件使用其他画图软件美化

t-sne t-SNE&#xff08;t-分布随机邻域嵌入&#xff0c;t-distributed Stochastic Neighbor Embedding&#xff09;是由 Laurens van der Maaten 和 Geoffrey Hinton 于 2008 年提出的一种非线性降维技术。它特别适合用于高维数据的可视化。t-SNE 的主要目标是将高维数据映射…

FastGPT+OneAI接入网络模型

文章目录 FastGPT连接OneAI接入网络模型1.准备工作2.开始部署2.1下载 docker-compose.yml2.2修改docker-compose.yml里的参数 3.打开FastGPT添加模型3.1打开OneAPI3.2接入网络模型3.3重启服务 FastGPT连接OneAI接入网络模型 1.准备工作 本文档参考FastGPT的官方文档 主机ip接…

QTabWidget、QListWidget、QStackedWidget

The QTabWidget class provides a stack of tabbed widgets. More... The QListWidget class provides an item-based list widget. More... QStringList strlist;strlist<<"系统"<<"外观"<<"截图"<<"贴图"…

宝塔5.9 老版本 登录不进去 密码忘记 验证码不显示笔记

reboot似乎是能重置验证码不显示的唯一方式了 改密码的话在 python tools.py panel 123 我把一段注释了 记录一下 以防以后bug

Matlab-Simulink模型保存为图片的方法

有好多种办法将模型保存为图片&#xff0c;这里直接说经常用的 而且贴到Word文档中清晰、操作简单。 simulink自带有截图功能&#xff0c;这两种方法都可以保存模型图片。选择后直接就复制到截切板上了。直接去文档中粘贴就完事了。 这两个格式效果不太一样&#xff0c;第一种清…

Kafka基础入门篇(深度好文)

Kafka简介 Kafka 是一个高吞吐量的分布式的基于发布/订阅模式的消息队列&#xff08;Message Queue&#xff09;&#xff0c;主要应用与大数据实时处理领域。 1. 以时间复杂度为O(1)的方式提供消息持久化能力。 2. 高吞吐率。&#xff08;Kafka 的吞吐量是MySQL 吞吐量的30…

Java的高级特性

类的继承 继承是从已有的类中派生出新的类&#xff0c;新的类能拥有已有类的属性和行为&#xff0c;并且可以拓展新的属性和行为 public class 子类 extends 父类{子类类体 } 优点 代码的复用 提高编码效率 易于维护 使类与类产生关联&#xff0c;是多态的前提 缺点 类缺乏独…

【HTML入门】第十二课 - iframe框架

在早期没有出现Vue和React之前呢&#xff0c;做管理系统&#xff0c;iframe是非常普遍的技术。比如管理系统左侧有非常多的菜单&#xff0c;然后点击菜单后&#xff0c;右边就要展现不同的页面。 又或者呢&#xff0c;我们看一些网站&#xff0c;他们侧边展示着五彩绚烂的广告&…

C++:哈希表

哈希表概念 哈希表可以简单理解为&#xff1a;把数据转化为数组的下标&#xff0c;然后用数组的下标对应的值来表示这个数据。如果我们想要搜索这个数据&#xff0c;直接计算出这个数据的下标&#xff0c;然后就可以直接访问数组对应的位置&#xff0c;所以可以用O(1)的复杂度…

Dataset for Stable Diffusion

1.Dataset for Stable Diffusion 笔记来源&#xff1a; 1.Flickr8k数据集处理 2.处理Flickr8k数据集 3.Github&#xff1a;pytorch-stable-diffusion 4.Flickr 8k Dataset 5.dataset_flickr8k.json 6.About Train, Validation and Test Sets in Machine Learning Tarang Shah …

提升机器视觉与机器学习软件安全性的实践策略

在近几年科技爆发中&#xff0c;机器学习&#xff08;ML&#xff09;和机器视觉&#xff08;MV&#xff09;的结合正在改变各行各业。机器学习通过数据驱动的算法让计算机能够自我学习&#xff0c;而机器视觉赋予计算机识别和理解图像的能力。这种结合使得计算机可以高效地执行…

fastadmin后台无法删除文件,如何解决?

&#x1f3c6;本文收录于《CSDN问答解答》专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收藏&…