Liinux——(网络)socket编程

news2024/12/29 7:51:50

预备知识

源IP地址和目的IP地址

  • 在IP数据包头部中, 有两个IP地址, 分别叫做源IP地址, 和目的IP地址

认识端口号

  • 端口号(port)是传输层协议的内容.
  • 端口号是一个2字节16位的整数;
  • 端口号用来标识一个进程, 告诉操作系统, 当前的这个数据要交给哪个进程来处理;
  • IP地址 + 端口号能够标识网络上的某一台主机的某一个进程;
  • 一个端口号只能被一个进程占用

传输层协议(TCP和UDP)的数据段中有两个端口号, 分别叫做源端口号和目的端口号. 就是在描述 "数据是谁发的, 要发给谁

简单认识TCP协议

  • 传输层协议
  • 有连接
  • 可靠传输
  • 面向字节流

简单认识UDP协议

  • 传输层协议
  • 无连接
  • 不可靠传输
  • 面向数据报

网络字节序

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

补充
计算机在存储数据时是有大小端的概念的:
大端模式: 数据的高字节内容保存在内存的低地址处,数据的低字节内容保存在内存的高地址处。
小端模式: 数据的高字节内容保存在内存的高地址处,数据的低字节内容保存在内存的低地址处。
在这里插入图片描述

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

网络字节序与主机字节序之间的转换
为使网络程序具有可移植性,使同样的C代码在大端和小端计算机上编译后都能正常运行,可以调用以下库函数做网络字节序和主机字节序的转换。

#include <arpa/inet.h>

uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);

  • 这些函数名很好记,h表示host,n表示network,l表示32位长整数,s表示16位短整数。ntoh表示网络转主机,hton表示主机转网络
  • 例如htonl表示将32位的长整数从主机字节序转换为网络字节序,例如将IP地址转换后准备发送。
  • 如果主机是小端字节序,这些函数将参数做相应的大小端转换然后返回;
  • 如果主机是大端字节序,这些 函数不做转换,将参数原封不动地返回。

socket编程接口

socket常见API

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

参数解析:
int domain
参数 domain 用于指定一个通信域;这将选择将用于通信的协议族。下面是三个常用的协议簇

  • AF_INET 这是大多数用来产生socket的协议,使用TCP或UDP来传输,用IPv4的地址
  • AF_INET6 与上面类似,不过是来用IPv6的地址
  • AF_UNIX 本地协议,使用在Unix和Linux系统上,一般都是当客户端和服务器在同一台及其上的时候使用

int type

  • SOCK_STREAM 这个协议是按照顺序的、可靠的、数据完整的基于字节流的连接。这是一个使用最多的socket类型,这个socket是使用TCP来进行传输。
  • SOCK_DGRAM 这个协议是无连接的、固定长度的传输调用。该协议是不可靠的使用UDP来进行它的连接。
  • SOCK_SEQPACKET该协议是双线路的、可靠的连接,发送固定长度的数据包进行传输。必须把这个包完整的接受才能进行读取。
  • SOCK_RAW socket类型提供单一的网络访问,这个socket类型使用ICMP公共协议。(ping、traceroute使用该协议)
  • SOCK_RDM 这个类型是很少使用的,在大部分的操作系统上没有实现,它是提供给数据链路层使用,不保证数据包的顺序

int protocol
参数 protocol 通常设置为 0,表示为给定的通信域和套接字类型选择默认协议。当对同一域和套接字类型支持多个协议时,可以使用 protocol 参数选择一个特定协议。

  • 在 AF_INET 通信域中,套接字类型为SOCK_STREAM 的默认协议是传输控制协议(TCP);
  • 在 AF_INET 通信域中,套接字类型为 SOCK_DGRAM 的默认协议时 UDP。

返回值:成功:返回指向新创建的socket的文件描述符,失败:返回-1,设置errno
在这里插入图片描述

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

socket上面介绍了这里不用多说

主要介绍一下这const struct sockaddr *address
这个结构体指针指向一个 struct sockaddr 类型变量,如下所示:
一般有三种结构 sockaddr sockaddr_in sockaddr_un(后面两种在使用的时候都要进行一下强制转换)

客户端不用bind,OS自动给其bind——客户端的port要随机OS分配防止出现启动冲突,
服务端要自己bind——服务器的端口不能随意改变,众所周知且不能随意改变 同一家公司的port号需要统一规范化
什么时候绑定呢?——在我们首次调用系统用发送数据的函数的时候,os会在底层随机选择clientport+自己的IP

struct sockaddr {
 sa_family_t sa_family;
 char sa_data[14];
}
struct sockaddr_in
  {
    __SOCKADDR_COMMON (sin_);//协议簇
    in_port_t sin_port;			/* 端口号  */
    struct in_addr sin_addr;		/* ip地址  */

    /* Pad to size of `struct sockaddr'.  */
    unsigned char sin_zero[sizeof (struct sockaddr) -
			   __SOCKADDR_COMMON_SIZE -
			   sizeof (in_port_t) -
			   sizeof (struct in_addr)];
  };

一般在使用时要将这个结构体的内容清零
在这里插入图片描述
这里有一个问题:在传输ip地址的时候,肯定时以字符串的风格如"1.1.1.1"但是我们这里要接收的话,是要以四字节整数为基础接收的,所以说,要进行转化(不是强转),那么该怎么办呢?
在这里插入图片描述

在这里插入图片描述
这里面的提供了解决办法

in_addr_t inet_addr(const char*cp)

这个接口的作用就是将一个字符串风格的ip地址,转换为四字节ip地址,并且默认内部已经将主机序列转换为了网络序列
在这里插入图片描述

注意事项:
云服务器:不需要bind ip地址,需要让服务器自己指定ip地址
自己本地装的虚拟机,或者是物理机是允许的

绑定的时候需要ip地址,但云服务器一般不要指明某一个确定的ip,一个服务器可能有多张网卡,或者说有多个ip,为了提高效率,我们直接使用INADDR_ANY绑定本主机上的 任意ip或者说是所有的ip,只要端口号是我们指明的端口号,都能转到机器上

UDP接收信息

在这里插入图片描述

ssize_t fecvfrom(int sockfd,void*buf,size_t len,in flags.struct sockaddr* src_addr,socklen_t *addrlen);

返回值说明:

  • 成功则返回实际接收到的字符数,失败返回-1,错误原因会存于errno 中。

参数说明:

  • sockfd: socket描述符;

  • buf: UDP数据报缓存区(包含所接收的数据);

  • len: 缓冲区长度。

  • flags: 调用操作方式(一般设置为0)。

  • src_addr: 指向发送数据的客户端地址信息(需要知道客户端的ip和端口号)的结构体(sockaddr_in需类型转换)

addrlen:指针,指向实际输出时结构体的大小
在这里插入图片描述
在这里插入图片描述

UDP发送信息
 int sendto(int sockfd, const void *buf, ssize_t len, unsigned int flags, const struct sockaddr *dest_addr, int addrlen);

返回值说明:

  • 成功则返回实际传送出去的字符数,失败返回-1,错误原因会存于errno 中。

参数说明:

  • sockfd: socket描述符;

  • buf:UDP数据报缓存区(包含待发送数据);

  • len: UDP数据报的长度;

  • flags:调用方式标志位(一般设置为0);

  • dest_addr:  指向接收数据的主机地址信息的结构体(sockaddr_in需类型转换);

  • addrlen:发送的结构体的长度;

轮哭了,

用上面的接口应用的简单通信程序

err.hpp

#pragma once

enum
{
    USAGE_ERR = 1,
    SOCKET_ERR,
    BIND_ERR
};

udp_client.cc

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

// 127.0.0.1: 本地环回,就表示的就是当前主机,通常用来进行本地通信或者测试

static void usage(std::string proc)
{
    std::cout << "Usage:\n\t" << proc << " serverip serverport\n" << std::endl; 
}

// ./udp_client serverip serverport
int main(int argc, char *argv[])
{
    if(argc != 3)
    {
        usage(argv[0]);
        exit(USAGE_ERR);
    }
    std::string serverip = argv[1];
    uint16_t serverport = atoi(argv[2]);

    int sock = socket(AF_INET, SOCK_DGRAM, 0);
    if(sock < 0)
    {
        std::cerr << "create socket error" << std::endl;
        exit(SOCKET_ERR);
    }
    // client 这里要不要bind呢?要的!socket通信的本质[clientip:clientport, serverip:serverport]
    // 要不要自己bind呢?不需要自己bind,也不要自己bind,OS自动给我们进行bind -- 为什么?client的port要随机让OS分配防止client出现
    // 启动冲突 -- server 为什么要自己bind?1. server的端口不能随意改变,众所周知且不能随意改变的 2. 同一家公司的port号
    // 需要统一规范化

    // 明确server是谁
    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 message;
        std::cout << "[请输入]# ";
         std::cin >> message;

        // 什么时候bind的?在我们首次系统调用发送数据的时候,OS会在底层随机选择clientport+自己的IP,1. bind 2. 构建发送的数据报文
        //发送
        sendto(sock, message.c_str(), message.size(), 0, (struct sockaddr*)&server, sizeof(server));

        //接受
        char buffer[1024];
        struct sockaddr_in temp;
        socklen_t len = sizeof(temp);
        int n = recvfrom(sock, buffer, sizeof(buffer)-1, 0, (struct sockaddr*)&temp, &len);
        if(n > 0)
        {
            buffer[n] = 0;
            std::cout << "server echo# "<<buffer << std::endl;
        }
    }

    return 0;
}

}

udp_server.cc

#include "udp_server.hpp"
#include <memory>
#include <string>
#include <cstdio>

using namespace ns_server;
using namespace std;

static void usage(string proc)
{
    std::cout << "Usage:\n\t" << proc << " port\n"<< std::endl;
}


// ./udp_server port
int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        usage(argv[0]);
        exit(USAGE_ERR);
    }
    uint16_t port = atoi(argv[1]);
    unique_ptr<UdpServer> usvr(new UdpServer(port));

    usvr->InitServer(); // 服务器的初始化
    usvr->start();

    return 0;
}


udp_client.hpp

#pragma once

#include <iostream>
#include <string>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "err.hpp"


udp_server.hpp

#pragma once

#include <iostream>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <functional>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <unordered_map>
#include "err.hpp"

namespace ns_server
{
    const static uint16_t default_port = 8080;
    using func_t = std::function<std::string(std::string)>;

    class UdpServer
    {
    public:
        UdpServer(uint16_t port = default_port) : port_(port)
        {
            std::cout << "server addr: " << port_ << std::endl;
        }
        void InitServer()
        {
              // 1. 创建socket接口,打开网络文件
            sock_ = socket(AF_INET, SOCK_DGRAM, 0);
            if (sock_ < 0)
            {
                std::cerr << "create socket error: " << strerror(errno) << std::endl;
                exit(SOCKET_ERR);
            }
            std::cout << "create socket success: " << sock_ << std::endl; // 3

            // 2. 给服务器指明IP地址(??)和Port
            struct sockaddr_in local; // 这个 local 在哪里定义呢?用户空间的特定函数的栈帧上,不在内核中!
            bzero(&local, sizeof(local));

            local.sin_family = AF_INET; // PF_INET
            local.sin_port = htons(port_);
            // inet_addr: 1,2
            // 1. 字符串风格的IP地址,转换成为4字节int, "1.1.1.1" -> uint32_t -> 能不能强制类型转换呢?不能,这里要转化
            // 2. 需要将主机序列转化成为网络序列
            // 3. 云服务器,或者一款服务器,一般不要指明某一个确定的IP
            local.sin_addr.s_addr = INADDR_ANY; // 让我们的udpserver在启动的时候,bind本主机上的任意IP
            if (bind(sock_, (struct sockaddr *)&local, sizeof(local)) < 0)
            {
                std::cerr << "bind socket error: " << strerror(errno) << std::endl;
                exit(BIND_ERR);
            }
            std::cout << "bind socket success: " << sock_ << std::endl; // 3
        }
        void start()
        {
            char buffer[1024];
           while (true)
            {
                // 收
                struct sockaddr_in peer;
                socklen_t len = sizeof(peer); // 这里一定要写清楚,未来你传入的缓冲区大小
                int n = recvfrom(sock_, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&peer, &len);
                if (n > 0)
                    buffer[n] = '\0';
                else continue;

                std::string clientip = inet_ntoa(peer.sin_addr);
                uint16_t clientport = ntohs(peer.sin_port);
                std::cout << clientip<< "-"<<clientport<<"#"<<"收到信息# " << buffer << std::endl;
                //发
                sendto(sock_, buffer, strlen(buffer), 0, (struct sockaddr*)&peer, sizeof(peer));
            }
        }

        ~UdpServer()
        {

        }

    private:
        int sock_;
        uint16_t port_;
        // std::string ip_; 
    };
} // end NS_SERVER


在这里插入图片描述

在这里插入图片描述

开始监听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* ,那么这个sockaddr结构体到底是什么呢?

sockaddr结构

socket API是一层抽象的网络编程接口,适用于各种底层网络协议,如IPv4、IPv6,以及后面要讲的UNIX DomainSocket. 然而, 各种网络协议的地址格式并不相同

在这里插入图片描述
这些结构体中 struct sockadd_in 表示基于网络 进行网络通信的套接字;
struct sockaddr_un叫做域间套接字,用来两个进程之间进行本地通信的这个是取代system V本地之间进行通信的解决方案

  • IPv4和IPv6的地址格式定义在netinet/in.h中,IPv4地址用sockaddr_in结构体表示,包括16位地址类型, 16
    位端口号和32位IP地址.
  • IPv4、IPv6地址类型分别定义为常数AF_INET、AF_INET6. 这样,只要取得某种sockaddr结构体的首地址,不需要知道具体是哪种类型的sockaddr结构体,就可以根据地址类型字段确定结构体中的内容.
  • *socket API可以都用struct sockaddr 类型表示, 在使用的时候需要强制转化成sockaddr_in; 这样的好处是程序的通用性, 可以接收IPv4, IPv6, 以及UNIX Domain Socket各种类型的sockaddr结构体指针做为
    参数

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

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

相关文章

碾压GPT-4!Claude3到底有多强?

2024年3月4日&#xff0c;官方宣布推出 Claude 3 模型系列&#xff0c;它在广泛的认知任务中树立了新的行业基准。该系列包括三个按能力递增排序的最先进模型&#xff1a;Claude 3 Haiku、Claude 3 Sonnet 和 Claude 3 Opus。每个后续模型都提供越来越强大的性能&#xff0c;允…

【C++】—— 建造者模式

目录 &#xff08;一&#xff09;概念详解 &#xff08;二&#xff09;代码详解 &#xff08;三&#xff09;建造者优缺点详解 &#xff08;一&#xff09;概念详解 建造者模式是⼀种创建型设计模式&#xff0c;使⽤多个简单的对象⼀步⼀步构建成⼀个复杂的对象&#xff0c…

静图转换gif动图怎么操作?用这个网站一键生成

静态图片是指一张固定的、不具有动画效果的图像。它是由像素点组成的&#xff0c;每个像素点都有自己的颜色值&#xff0c;形成了整个图像。静态图片通常以常见的图像格式保存&#xff0c;并且在打开时显示相同的内容&#xff0c;没有任何动态变化。动态图片是指一系列图像帧按…

MySQL8.0安装教程+使用Navicat远程连接

MySQL8.0安装教程使用Navicat远程连接 版本&#xff1a;MySQL8.0.28 环境&#xff1a;Windows11 1.MySQL下载 进入官网https://www.mysql.com/进行下载&#xff1a; 2.安装MySQL 下载好后&#xff0c;点击运行程序开始安装&#xff0c;安装步骤如下&#xff1a; 以下步骤验…

linux GitLab 私有仓库的搭建

下载地址 gitLab 安装包下载地址&#xff1a;https://about.gitlab.com/install/ 环境准备&#xff1a; 环境&#xff1a;CentOS7.6 安装包&#xff1a;gitlab-ce-8.9.5-ce.0.el7.x86_64.rpm 硬件配置&#xff1a; 4G 安装步骤&#xff1a; 安装&#xff1a; [rootserver3 ~]…

java变量是什么?怎么定义的?有哪些注意事项?

1、变量的概念 先看下面这个案例&#xff0c;我们要输出10&#xff0c;是不是可以这么输出&#xff1a; public class Demo {public static void main(String[] args) {System.out.println(10);System.out.println(10);System.out.println(10);System.out.println(10);System…

物联网导论

物联网起源 物联网&#xff1a;是一个基于互联网、传统电信网等信息承载体&#xff0c;让所有能够被独立寻址的普通物理对象实现互联互通的网络。它具有普通对象设备化、自治终端互联化和普适服务智能化三个重要特征。 按照规定的协议&#xff0c;将具有感知、通信、计算等功…

【前端】HTML常用标签

因为想当个全栈&#xff0c;所以巩固了一下HTML与CSS和JS基础&#xff0c;这一篇博客是HTML部分 文章目录 HTML 基础标签 1HTML 基础框架HTML 基础标签语义标签文本格式化标签div 与 span 标签图像标签超链接特殊字符 基础标签 2 | 表格表格的使用表格标签表格属性表格的头部与…

堡垒机jumpserver的安装部署和使用

jumpserver的介绍&#xff1a; 官方文档&#xff1a;jumpserver官方中文文档 JumpServer 是广受欢迎的开源堡垒机&#xff0c;是符合 4A 规范的专业运维安全审计系统。JumpServer 帮助企业以更安全的方式管控和登录所有类型的资产&#xff0c;实现事前授权、事中监察、事后审计…

千寻星地一体农机导航燃爆2024内蒙农机展

千寻星地一体农机导航燃爆2024内蒙农机展 2024年3月9日-11日&#xff0c;2024内蒙古农牧业机械展览会在内蒙古呼和浩特敕勒川国际会展中心展出。展会上&#xff0c;千寻位置以“北斗时空智能&#xff0c;助力数智农业”为主题&#xff0c;展示了北斗导航农机自动驾驶系统、星地…

ELEVENLABS AI在线AI语音合成工具,28种语言

一、Elevenlabs简介 ElevenLabs 可以使用合成语音、克隆语音或全新的「人工」语音将文本转化为语音&#xff0c;并且这些语音可以模仿不同性别、年龄和种族的声音。 ElevenLabs基于目前强大的生成式语音模型&#xff0c;提供文本生成语音、语音合成、语音克隆和语音识别分类功…

深入了解与全面解析华为认证(HCIA/HCIP/HCIE)

一、网络行业技术认证 网络行业对于技术评定一般分为两种&#xff0c;一种是企业认证&#xff0c;一种是国家认证 企业认证属于技术认证&#xff0c;在国内的互联网企业都会承认&#xff0c;用于评定一个人的技术等级或者企业招投标的资质。 网络行业认证最好的有三种&#x…

c++之旅——第六弹

大家好啊&#xff0c;这里是c之旅第五弹&#xff0c;跟随我的步伐来开始这一篇的学习吧&#xff01; 如果有知识性错误&#xff0c;欢迎各位指正&#xff01;&#xff01;一起加油&#xff01;&#xff01; 创作不易&#xff0c;希望大家多多支持哦&#xff01; 一,静态成员&…

网页脚本 bilibili006:视频下载脚本修改+油猴脚本发布

视频下载脚本修改 原始脚本的下载的视频名称总是错的&#xff0c;调用的代码为 document.querySelector(.tag-txt).textContent &#xff0c;发现这是标签的名称 查找视频名称所在的类名称 <h1 title"任天堂告yuzu模拟器&#xff0c;龙神模拟器会被殃及池鱼吗"…

工业数学模型——冶金烧结配矿(一)

1、工业场景 从很多种铁矿石中选出适合烧结配料的部分铁矿石及其比例&#xff0c;并使其成本最低。 2、数学模型 设Pi代表了第i种原料的成本&#xff0c;xi代表了第i种原料在总配料中的比例&#xff0c;其中i取值为1,2,…,n。计算1吨配料成本&#xff1a; 第种原料的成本是Y…

分布式系统超详解析

目录 常见概念 基本概念 应用/系统 模块/组件 分布式 集群 主/从 中间件 评价指标 可用性 响应时长 吞吐量/并发量 架构演进 单机架构 应用数据分离架构 引入更多的应用服务器结点 读写分离架构 引入缓存--冷热分离的结构 垂直分库 业务拆分--微服务 为了更…

【惠友小课堂】膝关节可以只换一半?单髁置换术,哪里坏了修哪里,重塑新“膝”望

髁 这个字念啥&#xff1f; 被指到的人来回答 不念“踝&#xff08;hui&#xff09;”也不念“果” 正确念法为“kē”&#xff08;科额~髁&#xff09; 膝关节是人体承重较大的关节&#xff0c;并且使用频率较高&#xff0c;因此膝关节很容易磨损、损伤等。年龄的增长、意外受…

魔法之线:探索string类的神秘世界

&#x1f389;个人名片&#xff1a; &#x1f43c;作者简介&#xff1a;一名乐于分享在学习道路上收获的大二在校生 &#x1f648;个人主页&#x1f389;&#xff1a;GOTXX &#x1f43c;个人WeChat&#xff1a;ILXOXVJE &#x1f43c;本文由GOTXX原创&#xff0c;首发CSDN&…

小马智行与卢森堡签署自动驾驶合作谅解备忘录

近日&#xff0c;自动驾驶企业小马智行宣布与卢森堡大公国政府签署谅解备忘录&#xff0c;促进自动驾驶汽车及技术在卢森堡的发展。该文件由小马智行联合创始人、CEO彭军与卢森堡经济部长Lex Delles共同签署&#xff0c;这也标志着小马智行与卢森堡政府就推动该地区的自动驾驶研…

Python:数据分析工具Streamlit

简介&#xff1a;Streamlit是一个开源Python库&#xff0c;可以轻松创建和共享用于机器学习和数据科学的漂亮的自定义web应用程序。只需几分钟&#xff0c;您就可以构建和部署功能强大的数据应用程序&#xff0c;同时可以结合 matplotlib 做出漂亮的图表&#xff0c;实现数据可…