【计算机网络】Linux环境中的网络套接字编程

2023-11-07


前言

本编文章是博主学习了网络套接字编程后对相关知识的总结,阅读本文可对网络编程有基本的了解。文章内容包括认识IP地址和端口号的作用、了解网络字节序等网络编程中的基本概念、掌握Socket API的基本用法,从而能够实现简单的UDP客户端/服务器和TCP客户端/服务器,并且理解TCP服务器建立连接、发送数据、断开连接的基本流程。

一、预备知识

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

IP地址被用来给Internet上的电脑一个编号。大家日常见到的情况是每台联网的PC上都需要有IP地址,才能正常通信。如果一台电脑上的数据要传输给另一台电脑,那么该电脑对应的IP地址就是源IP地址,而对于的目的电脑的IP地址就是目的IP地址。如果一台电脑与目的电脑进行通信,就必须知道目的IP地址,才能将信息发送给正确的对象。

认识端口号

知道了目的IP并不能建立双方之间的通信,两台主机通信的目的不是为了将数据发送给对方,而是为了访问目的主机的某一个访问。而目的主机上通常不仅仅只有一个服务,因此就引入了端口号进行区分。

所谓的端口,就好像是门牌号一样,客户端可以通过IP地址找到对应的服务器端,但是服务器端是有很多端口的,每个应用程序对应一个端口号,通过类似门牌号的端口号,客户端才能真正的访问到该服务器。为了对端口进行区分,将每个端口进行了编号,这就是端口号。

端口号的作用

端口号的主要作用是表示一台计算机中的特定进程所提供的服务。网络中的计算机是通过IP地址来代表其身份的,它只能表示某台特定的计算机,但是一台计算机上可以同时提供很多个服务,如数据库服务、FTP服务、Web服务等,我们就通过端口号来区别相同计算机所提供的这些不同的服务,如常见的端口号21表示的是FTP服务,端口号23表示的是Telnet服务端口号25指的是SMTP服务等。端口号一般习惯为4位整数,在同一台计算机上端口号不能重复,否则,就会产生端口号冲突这样的例外。

端口号的使用规则

TCP与UDP段结构中端口地址都是16比特,可以有在 0 ~ 65535 范围内的端口号。对于这65536个端口号有以下的使用规定:

  1. 端口号小于256的定义为常用端口,服务器一般都是通过常用端口号来识别的。任何TCP/IP实现所提供的服务都用1 ~ 1023之间的端口号,是由ICANN来管理的;端口号从1024—49151是被注册的端口,也成为“用户端口”,被IANA指定为特殊服务使用;
  2. 客户端只需保证该端口号在本机上是唯一的就可以了。客户端端口号因存在时间很短暂又称临时端口号;
  3. 大多数TCP/IP实现给临时端口号分配1024—5000之间的端口号。大于5000的端口号是为其他服务器预留的。

认识UDP协议和TCP协议

在TCP/IP网络体系结构中,TCP(传输控制协议,Transport Control Protocol)、UDP(用户数据报协议,User Data Protocol)是传输层最重要的两种协议,为上层用户提供级别的通信可靠性。

传输控制协议(TCP)
TCP(传输控制协议)定义了两台计算机之间进行可靠的传输而交换的数据和确认信息的格式,以及计算机为了确保数据的正确到达而采取的措施。协议规定了TCP软件怎样识别给定计算机上的多个目的进程如何对分组重复这类差错进行恢复。协议还规定了两台计算机如何初始化一个TCP数据流传输以及如何结束这一传输。TCP最大的特点就是提供的是面向连接、可靠的字节流服务

用户数据报协议(UDP):
UDP(用户数据报协议)是一个简单的面向数据报的传输层协议。不提供可靠性,也不提供报文到达确认、排序以及流量控制等功能。它只是把应用程序传给IP层的数据报发送出去,但是并不能保证它们能到达目的地。因此报文可能会丢失、重复以及乱序等。但由于UDP在传输数据报前不用在客户和服务器之间建立一个连接,且没有超时重发等机制,故而传输速度很快。UDP最大的特点就是提供的是非面向连接的、不可靠的数据流传输

了解网络字节序

通过以前对C语言的学习,我们知道了内存中的多字节数据相对于内存地址有大端和小端之分(大端和小端),磁盘文件中的多字节数据相对于文件中的偏移地址也有大端小端之分。当然,网络数据流中也同样有大小端之分。

以下是对网络数据流的地址定义

  • 发送主机通常将发送缓冲区中的数据按内存地址从低到高的顺序发出;
  • 接收主机从网络中接收到的数据依次保存到缓冲区中,也同样按照内存地址从低到高的顺序保存;
  • 由此可以保证网络数据流的地址都是:先发出的数据在低地址处,后发出的数据在高地址处;
  • 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位短整数。
  • 例如htonl表示将32位的长整数从主机字节序转换为网络字节序,例如将IP地址转换后准备发送。
  • 如果主机是小端字节序,这些函数将参数做相应的大小端转换然后返回;
  • 如果主机是大端字节序,这些函数不做转换,将参数原封不动地返回。

二、socket 套接字

socket 常见API

#include <sys/socket.h>

// 创建 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);

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

sockaddr 和 sockaddr_in

struct sockaddrstruct sockaddr_in 这两个结构体用来处理网络通信的地址。

1、sockaddr

sockaddr在头文件#include <sys/socket.h>中定义,sockaddr的缺陷是:sa_data把目标地址和端口信息混在一起了。其结构如下:

/* POSIX.1g specifies this type name for the `sa_family' member.  */
typedef unsigned short int sa_family_t;
 
/* This macro is used to declare the initial common members
   of the data types used for socket addresses, `struct sockaddr',
   `struct sockaddr_in', `struct sockaddr_un', etc.  */
 
#define	__SOCKADDR_COMMON(sa_prefix) \
  sa_family_t sa_prefix##family
  
 
/* Structure describing a generic socket address.  */
struct sockaddr
  {
    __SOCKADDR_COMMON (sa_);	/* Common data: address family and length.  */
    char sa_data[14];		/* Address data.  */
  };
struct sockaddr {  
     sa_family_t sin_family;//地址族
    char sa_data[14]; //14字节,包含套接字中的目标地址和端口信息               
   }; 

2、sockadd_in
sockaddr_in在头文件#include<netinet/in.h>#include <arpa/inet.h>中定义,该结构体解决了sockaddr的缺陷,把portaddr 分开储存在两个变量中,其结构定义如下:

/* Structure describing an Internet socket address.  */
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)];
  };

sin_port和sin_addr都必须是网络字节序(NBO),一般可视化的数字都是主机字节序(HBO)。

虽然socket API中的接口是sockaddr,但是我们真正在基于IPv4编程时,使用的数据结构是sockaddr_in,这个结构里主要有三部分信息:地址类型、端口号、IP地址。

3、sockaddr 和 sockaddr_in的结构

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

4、总结

二者长度一样,都是16个字节,即占用的内存大小是一致的,因此可以互相转化。二者是并列结构,指向sockaddr_in结构的指针也可以指向sockaddr

sockaddr常用于bindconnectrecvfromsendto等函数的参数,指明地址信息,是一种通用的套接字地址。
sockaddr_ininternet环境下套接字的地址形式。所以在网络编程中我们会对sockaddr_in结构体进行操作,使用sockaddr_in来建立所需的信息,最后使用类型转化就可以了。一般先把sockaddr_in变量赋值后,强制类型转换后传入用sockaddr做参数的函数。即sockaddr_in用于socket定义和赋值;而sockaddr用于函数参数。

用法举例:

#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
 
int main(int argc,char **argv)
{
    int sockfd = 0;
    struct sockaddr_in addr_in;
    struct sockaddr * addr;
 
    sockfd = socket(AF_INET, SOCK_STREAM, 0);  //获得fd
    bzero(&addr_in,sizeof(addr_in));  // 初始化结构体
    /*
     8008的主机字节序  小端字节序 0001 1111 0100 1000 = 8008
     8008的网络字节序  大端字节序 0100 1000 0001 1111 = 18463
    */
    addr_in.sin_port = htons(8008);
    addr_in.sin_family = AF_INET;  // 设置地址家族
    addr_in.sin_addr.s_addr = inet_addr("192.168.3.30");  //设置地址
    printf("sockaddr_in.sin_addr.s_addr = %d \n", addr_in.sin_addr.s_addr);
    printf("addr = %s \n", inet_ntoa(addr_in.sin_addr));
//    addr_in.sin_addr.s_addr = htonl(INADDR_ANY);  //设置地址
 
    printf("struct sockaddr size = %ld \n", sizeof (addr));
    printf("struct sockaddr_in size = %ld \n", sizeof (addr_in));
    addr = (struct sockaddr *)&addr_in;
//    bind(sockfd, (struct sockaddr *)&addr_in, sizeof(struct sockaddr));  /* bind的时候进行转化 */
    bind(sockfd, addr, sizeof(struct sockaddr));
    
    ... ...
    
    return 0;
}

其中inet_addr()作用是将一个IP字符串转化为一个网络字节序的整数值,用于sockaddr_in.sin_addr.s_addr的初始化。inet_ntoa()作用是将一个sin_addr结构体输出成IP字符串(network to ascii)。

三、UDP Socket编程

这里我们使用UDP套接字编程实现一个简单的英译汉服务器。

封装UdpSocket

udpSocket.hpp

#pragma once

#include <iostream>
#include <string>
#include <cstring>
#include <cassert>

#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

class udpSocket
{
public:
    udpSocket() : _fd(-1) {}

    ~udpSocket(){}

public:
    // 创建套接字
    bool Socket()
    {
        _fd = socket(AF_INET, SOCK_DGRAM, 0); // AF_INET表示采用IPv4, SOCK_DGRAM表示采用udp协议
        if (_fd < 0)
        {
            std::cerr << "create socket error!" << std::endl;
            return false;
        }

        return true;
    }

    // 关闭套接字
    bool Close()
    {
        close(_fd);
        return true;
    }

    // Bind套接字相关信息
    bool Bind(const std::string &ip, uint16_t port)
    {
        sockaddr_in addr;
        // 填充协议家族
        addr.sin_family = AF_INET;
        // 填写端口号,port会通过网络发送给客户端
        addr.sin_port = htons(port);

        // 服务器必须有IP地址,“xx.yy.zz.aaa”,字符串风格的点分十进制 -》4字节IP -> uint32_t IP
        // INADDR_ANY(0): 不关心会bind到哪个IP,bind任意IP,服务器一般的做法
        // inet_addr指定填充确定的IP,特殊用途,或者测试时使用,除了做转化,还会自动给我们进行 h—>n
        addr.sin_addr.s_addr = ip.empty() ? htonl(INADDR_ANY) : inet_addr(ip.c_str());

        // bind 网络信息
        // struct sockaddr_in填充信息, struct sockaddr充当函数参数
        if (bind(_fd, (const sockaddr *)&addr, sizeof addr) == -1)
        {
            std::cerr << "bind error!" << std::endl;
            return false;
        }
        return true;
    }

    // 接收数据
    bool RecvFrom(std::string *buf, std::string *ip = nullptr, uint16_t *port = nullptr)
    {
        char inbuf[1024 * 10];
        sockaddr_in peer;
        socklen_t len = sizeof(peer);

        // 将接收的数据读入到inbuf缓冲区中,并且获取客户端的sockaddr_in信息
        ssize_t read_size = recvfrom(_fd, inbuf, sizeof(inbuf) - 1, 0, (sockaddr *)&peer, &len);
        if (read_size < 0)
        {
            std::cerr << "recvfrom error!" << std::endl;
            return false;
        }

        // 将缓冲区中的内容放到输出型参数中
        buf->assign(inbuf, read_size);
        if (ip != nullptr)
        {
            *ip = inet_ntoa(peer.sin_addr);
        }
        if (port != nullptr)
        {
            *port = ntohs(peer.sin_port);
        }
        return true;
    }

    // 发送数据
    bool SendTo(const std::string &buf, const std::string &ip, uint16_t port)
    {
        sockaddr_in addr;
        addr.sin_family = AF_INET;
        addr.sin_port = htons(port);
        addr.sin_addr.s_addr = ip.empty() ? htonl(INADDR_ANY) : inet_addr(ip.c_str());

        //发送数据给客户端
        ssize_t write_size = sendto(_fd, buf.c_str(), buf.size(), 0, (const sockaddr *)&addr, sizeof(addr));
        if (write_size < 0)
        {
            std::cerr << "sendto error!" << std::endl;
            return false;
        }
        return true;
    }

private:
    int _fd;
};

实现UDP通用服务器

udpServer.hpp

#pragma once
#include "udpSocket.hpp"

// C++11 能够兼容函数指针、仿函数和Lambda表达式
#include <functional>
typedef std::function<void (const std::string &, std::string* resp)> Handler;

class udpServer
{
public:
    udpServer()
    {
        assert(_sock.Socket());
    }
    ~udpServer()
    {
        assert(_sock.Close());
    }

public:
    bool Start(const std::string &ip, uint16_t port, Handler handler)
    {
        // 绑定IP 和 端口号

        if (!_sock.Bind(ip, port))
        {
            return false;
        }

        while (true)
        {
            // 尝试读取请求
            std::string req;
            std::string client_ip;
            uint16_t client_port;
            if (!_sock.RecvFrom(&req, &client_ip, &client_port))
            {
                continue;
            }

            std::string resq;
            //根据请求获得响应
            handler(req, &resq);

            //返回相应给客户端
            _sock.SendTo(resq, client_ip, client_port);
            //debug
            printf("[%s:%d] req: %s, resq: %s\n", client_ip.c_str(), client_port, req.c_str(), resq.c_str());
        }
        _sock.Close();
        return true;
    }

private:
    udpSocket _sock;
};

实现英译汉服务器

dict_server.cpp

#include <iostream>
#include <string>
#include <unordered_map>
#include "udpServer.hpp"

std::unordered_map<std::string, std::string> g_dict;

void Translate(const std::string &req, std::string *resp)
{
    auto it = g_dict.find(req);
    if (it == g_dict.end())
    {
        std::cout << "未查找到!" << std::endl;
        return;
    }
    *resp = it->second;
}

int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        printf("Usage: ./dict_server [ip] [port]\n");
        return 1;
    }

    // 数据初始化
    g_dict.insert(std::make_pair("hello", "你好"));
    g_dict.insert(std::make_pair("world", "世界"));
    g_dict.insert(std::make_pair("c++", "最好的编程语言"));
    g_dict.insert(std::make_pair("byte", "字节"));

    // 启动服务器
    udpServer().Start(argv[1], atoi(argv[2]), Translate);

    return 0;
}

实现UDP通用客户端

udpClient.hpp

#pragma once

#include "udpSocket.hpp"

class udpClient
{
public:
    udpClient(const std::string &ip, uint16_t port)
        : _ip(ip), _port(port)
    {
        assert(_sock.Socket());
    }
    ~udpClient() { _sock.Close(); }
public:
    bool RecvFrom(std::string* buf)
    {
        return _sock.RecvFrom(buf);
    }

    bool SendTo(const std::string& buf)
    {
        return _sock.SendTo(buf, _ip, _port);
    }
private:
    udpSocket _sock;
    // 服务器的IP和端口号
    std::string _ip;
    uint16_t _port;
};

实现英译汉客户端

client.cpp

#include <iostream>
#include <string>
#include "udpClient.hpp"

int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        printf("Usage: ./dict_server [ip] [port]\n");
        return 1;
    }

    udpClient client(argv[1], atoi(argv[2]));
    while(true)
    {
        std::string word;
        std::cout << "请输入您要查找的单词:";
        if(!(std::cin >> word))
        {
            std::cout << "end..." << std::endl;
            break;
        }

        client.SendTo(word);

        std::string res;
        client.RecvFrom(&res);
        std::cout << word << "的意思是:" << res << std::endl;
    }

    return 0;
}

运行结果:

四、地址转换函数

sockaddr_in中的成员struct in_addr sin_addr表示32位的IP地址,但是我们通常用点分十进制的字符串表示IP 地址,以下函数可以在字符串表示 和in_addr表示之间转换。

字符串转in_addr的函数

#include <arpa/inet.h>

int inet_aton(const char* strptr, struct in_addr* addrptr);
in_addr_t inet_addr(const char* strptr);
int inet_pton(int family, const char* strptr, void* addrptr);

in_addr转字符串的函数

char* inet_ntoa(struct in_addr inaddr);
const char* inet_ntop(int family, const void* addrptr, char* strptr, size_t len);

其中inet_ptoninet_ntop不仅可以转换IPv4的in_addr,还可以转换IPv6的in6_addr,因此函数接口是void* addrptr

代码样例:

#include <iostream>
#include <cstdio>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int main()
{
    sockaddr_in addr;
    inet_aton("127.0.0.1", &addr.sin_addr);
    
    uint32_t* ptr = (uint32_t*)(&addr.sin_addr);
    printf("addr: %x\n", *ptr);
    
    printf("addr_str: %s\n", inet_ntoa(addr.sin_addr));
    
    return 0;
}

运行结果:
在这里插入图片描述

关于inet_ntoa
inet_ntoa这个函数返回了一个char*类型的指针,很显然是这个函数自己在内部为我们申请了一块内存来保存IP的结果。那么是
否需要调用者手动释放呢?答案是不需要。

在man手册上说,inet_ntoa函数,是把这个返回结果放到了静态存储区,所以这个时候不需要我们手动进行释放。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【计算机网络】Linux环境中的网络套接字编程 的相关文章

  • 查找哪些页面不再与写入时复制共享

    假设我在 Linux 中有一个进程 我从中fork 另一个相同的过程 后forking 因为原始进程将开始写入内存 Linux写时复制机制将为进程提供与分叉进程使用的不同的唯一物理内存页 在执行的某个时刻 我如何知道原始进程的哪些页面已被写
  • 如何通过ssh检查ubuntu服务器上是否存在php和apache

    如何通过ssh检查Ubuntu服务器上apache是 否安装了php和mysql 另外如果安装的话在哪个目录 如果安装了其他软件包 例如 lighttpd 那么它在哪里 确定程序是否已安装的另一种方法是使用which命令 它将显示您正在搜索
  • 如何确保应用程序在 Linux 上持续运行

    我试图确保脚本在开发服务器上保持运行 它会整理统计数据并提供网络服务 因此它应该会持续存在 但一天中有几次 它会因未知原因而消失 当我们注意到时 我们只需再次启动它 但这很麻烦 并且某些用户没有权限 或专有技术 来启动它 作为一名程序员 我
  • Gtk-ERROR **:检测到 GTK+ 2.x 符号

    我正在使用 gcc 编译我的 c 应用程序 并使用以下标志 gcc evis c pkg config cflags libs gtk 2 0 libs clutter gtk 1 0 libs gthread 2 0 Wall o evi
  • 我不明白 execlp() 在 Linux 中如何工作

    过去两天我一直在试图理解execlp 系统调用 但我还在这里 让我直奔主题 The man pageexeclp 将系统调用声明为int execlp const char file const char arg 与描述 execl exe
  • 当 grep "\\" XXFile 我得到“尾随反斜杠”

    现在我想查找是否有包含 字符的行 我试过grep XXFile但它暗示 尾随反斜杠 但当我尝试时grep XXFile没关系 谁能解释一下为什么第一个案例无法运行 谢谢 区别在于 shell 处理反斜杠的方式 当你写的时候 在双引号中 sh
  • MySQL 中的创建/写入权限

    我的设备遇到一些权限问题SELECT INTO OUTFILE陈述 当我登录数据库并执行简单的导出命令时 例如 mysql gt select from XYZ into outfile home mropa Photos Desktop
  • Linux - 从第二个选项卡获取文本

    假设我们有这样的文件 一些文本11 一些文本12 一些文本13 一些文本21 一些文本22 一些文本23 文本由制表符分隔 我们知道第 1 列中的一些文本 但希望从第 2 列中获取文本 我知道我可以通过以下方式获取线路 grep somet
  • 在两次之间每分钟执行一次 Cronjob

    我需要在 crontab 中每分钟运行一个 bash 脚本8 45am and 9 50am每天的 Code 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 8 home pull sh gt ho
  • 使用 Grep 查找两个短语之间的文本块(包括短语)

    是否可以使用 grep 来高亮所有以以下内容开头的文本 mutablePath CGPathCreateMutable 并以以下内容结尾 CGPathAddPath skinMutablePath NULL mutablePath 这两个短
  • 监视目录的更改

    很像一个类似的问题 https stackoverflow com questions 112276 directory modification monitoring 我正在尝试监视 Linux 机器上的目录以添加新文件 并希望在这些新文
  • 子目录中的头文件(例如 gtk/gtk.h 与 gtk-2.0/gtk/gtk.h)

    我正在尝试使用 GTK 构建一个 hello world 其中包括以下行 include
  • Fortran gfortran linux 中的“分段错误(核心转储)”错误

    我正在创建一个程序 该程序将分析目录中的文件 fits 然后它将在另一个目录中创建另一个文件 txt 它只是一个转换器 当我尝试执行该程序 编译正常 时 它给了我一条错误消息 程序收到信号 SIGSEGV 分段错误 无效的内存引用 此错误的
  • Windows CE 与嵌入式 Linux [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 现在我确信我们都清楚 Linux 与 Windows 桌面的相对优点 然而 我对嵌入式开发世界的了解却少得多 我主要对行业解决方案感兴
  • 捕获实时流量时如何开启纳秒精度?

    如何告诉 libpcap v1 6 2 将纳秒值存储在struct pcap pkthdr ts tv usec 而不是微秒值 捕获实时数据包时 Note This question is similar to How to enable
  • git 错误:无法处理 https

    当我尝试使用 git clone 时https xxx https xxx我收到以下错误我不处理协议 https 有人可以帮我吗 完整消息 dementrock dementrock A8Se git 克隆https git innosta
  • 如何使用 Nmap 检索 TCP 和 UDP 端口? [关闭]

    Closed 这个问题是无关 help closed questions 目前不接受答案 我需要在使用 Nmap 的同一扫描中以尽可能最快的方式检索 TCP 和 UDP 端口 我会尽力解释得更好 如果我使用最常用的命令 nmap 192 1
  • 警告:请求的映像平台 (linux/amd64) 与检测到的主机平台 (linux/arm64/v8) 不匹配

    警告 请求的映像平台 linux amd64 与检测到的主机平台 linux arm64 v8 不匹配 并且未请求特定平台 docker 来自守护程序的错误响应 无法选择具有功能的设备驱动程序 gpu 我在 mac 上尝试运行此命令时遇到此
  • 后台分叉无法正常工作[重复]

    这个问题在这里已经有答案了 我运行这个程序 在前景和背景中 int main int pid printf App Start pid d n getpid while 1 pid fork if pid 0 printf Child n
  • 为什么我的 Dockerfile CMD 不起作用?

    所以在我的 Dockerfile 的末尾我有这样的内容 WORKDIR home CMD django admin startproject whattt CMD bin bash 当我创建映像然后运行容器时 一切都按预期运行 没有错误 D

随机推荐

  • 关于HR系统升级为集团版的设计总结

    刚刚完了公司HR系统的升级 系统实现了从单一公司使用到多公司使用的转变 在升级的一个多月的时间内 虽然很苦 但感觉自已在系统架构上受益非浅 具体有以下感悟 一 MVC还是很重要 系统框架是五年前用delphi设计的 采用的基类加扩展类的模式
  • fluent p1模型_Fluent辐射传热模型理论以及相关设置(一)

    原标题 Fluent辐射传热模型理论以及相关设置 一 本文来源于网络 原作者 Libo CHen 感谢作者的辛苦整理和撰写 1概述 在传热的仿真中 有时候会不可避免的涉及到辐射传热 而我们对Fluent中辐射模型的了解甚少 很难得到可靠的计
  • 关于Unity-Web的Development build。

    Unity转手游之后是否该勾选development Build 首先看一看官方文档的解释 Development Build When you check the Development Build checkbox Unity gene
  • 【杂记】EMC、EMI、EMS、TVS、ESD概念学习总结

    注 杂记 系列为日常网搜资料的简单堆砌而积累成之 如有错误恭谢指出 标识为 原创 其实不完全是 只是多引用再整理 大都引自网络 侵删 EMC EMC Electromagnetic Compatibility 属于概念 为电磁兼容性 电磁兼
  • java 中unsigned类型的转换

    java 中unsigned类型的转换
  • oh-my-zsh安装与常用插件

    zsh 介绍 工欲善其事 必先利其器 zsh也是一种 shell 兼容最常用的 bash 这种 shell 的命令和操作 bash 虽然很标准 但是自己日常使用方便更重要 oh my zsh 提供了丰富的插件和提 安装 先使用命令查看系统支
  • whisper:robust speech recognition via large-sacle weak supervision

    OpenAI Whisper 精读 论文精读 45 哔哩哔哩 bilibili更多论文 https github com mli paper reading 视频播放量 68331 弹幕量 327 点赞数 2332 投硬币枚数 1192 收
  • QJsonObject的使用示例

    介绍 负责封装JSON对象 是键 值对列表 其中键是惟一的字符串 值由QJsonValue表示 1 QJsonObjec 封装了Json里的对象 接口与QMap相似 都具有size insert 和remove 等操作 还可以使用标准C 迭
  • SQL Server(三)-查询数据(2)

    函数与分组查询数据 一 系统函数 在SQL Server 2008中系统函数是指在SQL Server 2008中自带的函数 主要分为聚合函数 数据类型转换函数 日期函数 数学函数及其他一些常用的函数 1 聚合函数 对一组值进行计算 然后返
  • 雅思词汇表8000词版_你别不信,雅思光靠背单词也能上6.5!

    点击上方蓝字关注我哦 打算备考雅思 大家都是知道IELTS考试对词汇量要求是比较高的 如果自身的英语基础薄弱 想短时间内在雅思成绩上有所突破是很困难的一件事情 因此很多考生会 病急乱投医 购买各式各样雅思词汇手册进行疯狂记忆 每本单词手册都
  • Keil 重定向 fputc 函数 以及 printf 函数的代码尺寸测试

    本文的开发环境为 Keil Cortex M3 内核处理器 重定向 fputc 函数方法 如果想使用库函数 printf 必须要将 fputc 重定向到自己的串口上 术语 重定向 可以理解为用户重写 fputc 函数 在重写的函数体内调用自
  • vulnhub-KIOPTRIX: LEVEL 1.3 (#4)-KioptrixVM4靶场

    以下演示均在测试环境进行 遵纪守法 靶场下载地址 Kioptrix Level 1 3 4 VulnHub 镜像下载解压之后是一个 vhd文件 需要新建虚拟机 虚拟机操作系统任选一个linux系列的系统 一直下一步 到了设置磁盘 按照截图设
  • Qt系列文章之(十) ui文件的使用

    上一篇文章在主函数中构造了一个简单的主窗口界面 继承了一些基本元素 如菜单栏 工具栏 悬浮窗口 主界面等元素 不过这些元素都是在栈区开辟的临时变量 放在主函数里面来实现 这不是一种标准的UI界面开发手段 一般在界面项目开发之中有几个典型的开
  • PyCharm远程连接失败、错误,报错:Can‘t connect...【解决方法与错误分析】

    学习网站推荐 前些天发现了一个巨牛的人工智能学习网站 通俗易懂 风趣幽默 忍不住分享一下给大家 点击跳转到网站 文章目录 一 前言 二 报错 2018版 2020版 三 错误分析 我的错误原因 其他3种可能因粗心导致的原因 四 如果你不想再
  • HTTPX从入门到放弃

    1 什么是HTTPX HTTPX是一款Python栈HTTP客户端库 它提供了比标准库更高级别 更先进的功能 如连接重用 连接池 超时控制 自动繁衍请求等等 HTTPX同时也支持同步和异步两种方式 因此可以在同步代码和异步代码中通用 HTT
  • Swift语法学习--协议基础

    文章目录 协议定义 typealias关键词类型定义新的名称 associatedtype增加协议功能 协议定义 typealias关键词类型定义新的名称 不做赘述 typealias Distance Double typealias P
  • 游戏对象与图形基础

    游戏对象与图形基础 这是有游戏编程的第四次作业 对MVC深入 文章目录 游戏对象与图形基础 说明文档 作业内容 1 基本操作演练 建议做 2 编程实践 需求分析 新版的设计与实现 说明文档 本次实验完成了所有基本要求 尽量将步骤展示出 闪光
  • java中把判断大小的字符串转换成可判断的布尔值

    ScriptEngineManager manager new ScriptEngineManager ScriptEngine se manager getEngineByName js String str 1 lt 3 boolean
  • OpenCV

    OpenCV polylines绘制多边形 1 四边形 Mat image 300 300 CV 8UC3 Scalar 255 255 255 300X300大小的白色图像 Point pnt 1 4 Point 100 100 左上角
  • 【计算机网络】Linux环境中的网络套接字编程

    文章目录 前言 一 预备知识 理解源IP地址和目的IP地址 认识端口号 认识UDP协议和TCP协议 了解网络字节序 二 socket 套接字 socket 常见API sockaddr 和 sockaddr in 三 UDP Socket编