当前位置: 首页 > article >正文

计算机网络自顶向下(2)----socket编程

1.套接字

        套接字是用于在计算机网络中进行通信的一种技术。它是操作系统提供的一种接口,通过该接口,应用程序可以通过网络连接进行数据的传输和接收。

        套接字包含了一个IP地址和一个端口号,用于唯一标识一个网络连接。通过套接字,应用程序可以使用不同的协议(如TCP、UDP)进行数据的传输。

2.socket

        套接字(Socket)是一种编程接口,用于实现网络通信。它是操作系统提供的一种抽象,允许应用程序通过网络进行数据传输和接收。

        在网络编程中,套接字通常被称为 Socket。通过 Socket,应用程序可以通过网络连接到其他计算机并与其进行通信。Socket 提供了一种标准的编程接口,使得应用程序可以使用不同的协议(如 TCP、UDP)进行数据传输。

1.创建socket

int socket(int domain, int type, int protocol);
  • domain:指定地址家族,例如AF_INETAF_INET6
  • type:指定socket类型,例如SOCK_STREAM(用于TCP)或SOCK_DGRAM(用于UDP)。
  • protocol:通常设置为0,让系统选择type参数对应的默认协议。

2.绑定

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
  • sockfd:socket文件描述符。
  • addr:指向包含地址信息的sockaddr结构的指针。
  • addrlensockaddr结构的大小。

        在Linux网络编程中,recvfrom 是一个系统调用,用于从(已连接的或未连接的)socket接收数据。对于未连接的socket(例如UDP socket),recvfrom 允许接收来自任何发送者的数据,并返回发送者的地址信息。

#include <sys/socket.h>

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                 struct sockaddr *src_addr, socklen_t *addrlen);
  • sockfd:接收数据的socket文件描述符。
  • buf:指向缓冲区的指针,该缓冲区用于存储接收到的数据。
  • len:缓冲区的大小,即要接收的最大数据量。
  • flags:指定调用行为的标志,例如 MSG_DONTWAIT(非阻塞操作)或 MSG_PEEK(查看数据但不从缓冲区中移除)。
  • src_addr:指向 sockaddr 结构的指针,该结构用于存储发送者的地址信息。如果不需要此信息,可以设置为 NULL
  • addrlen:指向 socklen_t 类型的指针,它初始化为 src_addr 结构的大小。在调用返回时,addrlen 被设置为实际存储在 src_addr 中的地址的大小。

  sendto 是Linux网络编程中的一个系统调用,用于向一个指定的地址发送数据。这个系统调用通常用于UDP协议,因为UDP是无连接的,所以需要在每次发送数据时指定目标地址。对于TCP协议,由于它是面向连接的,所以通常使用 send 或 write 系统调用来发送数据。

#include <sys/socket.h>

ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
               const struct sockaddr *dest_addr, socklen_t addrlen);
#pragma once

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

// echo server -> client -> server
enum
{
    SOCKET_ERROR = 1,
    BIND_ERROR,
    USAGE_ERROR
};

const static int defaultfd = -1;

class UdpServer
{
public:
    UdpServer(uint16_t port) : _sockfd(defaultfd), _port(port), _isrunning(false)
    {
    }
    void InitServer()
    {
        // 1. 创建udp socket 套接字 --- 必须要做的
        _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
        if (_sockfd < 0)
        {
            LOG(FATAL, "socket error, %s, %d\n", strerror(errno), errno);
            exit(SOCKET_ERROR);
        }
        LOG(INFO, "socket create success, sockfd: %d\n", _sockfd);

        // 2.0 填充sockaddr_in结构
        struct sockaddr_in local; // struct sockaddr_in 系统提供的数据类型。local是变量,用户栈上开辟空间。int a = 100; a = 20;
        bzero(&local, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(_port); // port要经过网络传输给对面,先到网络,_port:主机序列-> 主机序列,转成网络序列
        // a. 字符串风格的点分十进制的IP地址转成 4 字节IP
        // b. 主机序列,转成网络序列
        // in_addr_t inet_addr(const char *cp) -> 同时完成 a & b
        // local.sin_addr.s_addr = inet_addr(_ip.c_str()); // "192.168.3.1" -> 字符串风格的点分十进制的IP地址 -> 4字节IP
        local.sin_addr.s_addr = INADDR_ANY; // htonl(INADDR_ANY);

        // 2.1 bind sockfd和网络信息(IP(?) + Port)
        int n = bind(_sockfd, (struct sockaddr *)&local, sizeof(local));
        if (n < 0)
        {
            LOG(FATAL, "bind error, %s, %d\n", strerror(errno), errno);
            exit(BIND_ERROR);
        }
        LOG(INFO, "socket bind success\n");
    }
    void Start()
    {
        // 一直运行,直到管理者不想运行了, 服务器都是死循环
        // UDP是面向数据报的协议
        _isrunning = true;
        while (true)
        {
            char buffer[1024];
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer); // 必须初始化成为sizeof(peer)
            // 1. 我们要让server先收数据
            ssize_t n = recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&peer, &len);
            if (n > 0)
            {
                buffer[n] = 0;
                InetAddr addr(peer);
                LOG(DEBUG, "get message from [%s:%d]: %s\n", addr.Ip().c_str(), addr.Port(), buffer);
                // 2. 我们要将server收到的数据,发回给对方
                sendto(_sockfd, buffer, strlen(buffer), 0, (struct sockaddr *)&peer, len);
            }
        }
        _isrunning = false;
    }
    ~UdpServer()
    {
    }

private:
    int _sockfd;
    // std::string _ip; // 暂时这样写,这个地方不是必须的.TODO
    uint16_t _port;  // 服务器所用的端口号
    bool _isrunning;
};
#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

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

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

    // 1. 创建socket
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if(sockfd < 0)
    {
        std::cerr << "socket error" << std::endl;
    }
    // 2. client要不要bind?一定要,client也要有自己的IP和PORT。要不要显式[和server一样用bind函数]的bind?不能!不建议!!
    // a. 如何bind呢?udp client首次发送数据的时候,OS会自己自动随机的给client进行bind --- 为什么?防止client port冲突。要bind,必然要和port关联!
    // b. 什么时候bind呢?首次发送数据的时候

    // 构建目标主机的socket信息
    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());

    std::string message;
    // 2. 直接通信即可
    while(true)
    {
        std::cout << "Please Enter# ";
        std::getline(std::cin, message);
        sendto(sockfd, message.c_str(), message.size(), 0, (struct sockaddr*)&server, sizeof(server));

        struct sockaddr_in peer;
        socklen_t len = sizeof(peer);
        char buffer[1024];
        ssize_t n = recvfrom(sockfd, buffer, sizeof(buffer)-1, 0, (struct sockaddr*)&peer, &len);
        if(n > 0)
        {
            buffer[n] = 0;
            std::cout << "server echo# " << buffer << std::endl;
        }
    }
    return 0;
}
#include <iostream>
#include <memory>
#include "UdpServer.hpp"

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

// ./udpserver port
// 云服务器的port默认都是禁止访问的。云服务器放开端口8080 ~ 8085
int main(int argc, char *argv[])
{
    if(argc != 2)
    {
        Usage(argv[0]);
        exit(USAGE_ERROR);
    }
    EnableScreen();
    // std::string ip = argv[1]; // TODO
    uint16_t port = std::stoi(argv[1]);
    std::unique_ptr<UdpServer> usvr = std::make_unique<UdpServer>(port); // C++14
    usvr->InitServer();
    usvr->Start();

    return 0;
}

3.网络命令

1.ping

  ping命令是计算机网络中常用的一个诊断工具,它用来测试与目标主机的连通性。ping命令发送ICMP(Internet Control Message Protocol)回显请求消息给目标主机并等待目标主机返回ICMP回显应答消息。

以下是ping命令的一些基本用法:

ping [选项] [目标地址]
  • -t:持续发送ping包,直到被用户以Ctrl + C中断。
  • -a:将目标IP地址解析为机器名。
  • -n:发送指定数量的回显请求,默认值为4。
  • -l:发送指定大小的数据包,默认值为32字节。
  • -r:记录路由过程。
  • -w:指定超时间隔,单位为毫秒。

2.netstat

  netstat 是一个在类 Unix 系统中广泛使用的命令行工具,用于显示网络连接、路由表、接口统计信息、伪装连接以及多播成员信息。使用 netstat 可以帮助管理员监控网络状态,诊断网络问题,以及检查系统的网络配置。

以下是一些 netstat 命令的常见用法和选项:

netstat [选项]
  • -a:显示所有连接和监听端口。
  • -t:仅显示 TCP 连接。
  • -u:仅显示 UDP 连接。
  • -n:不解析域名,显示 IP 地址和端口号。
  • -p:显示进程 ID 和程序名称。
  • -l:仅显示正在监听的套接字。
  • -r:显示路由表。
  • -i:显示网络接口统计信息。

输出信息解释

  • Proto:协议(TCP、UDP、ICMP 等)。
  • Recv-Q:接收队列中的字节数。
  • Send-Q:发送队列中的字节数。
  • Local Address:本地 IP 地址和端口号。
  • Foreign Address:远程 IP 地址和端口号。
  • State:连接状态(如 ESTABLISHED、SYN_SENT、LISTEN 等)。
  • User:拥有该套接字的用户。
  • Inode:套接字的索引节点号。
  • PID/Program name:进程 ID 和程序名称。

3.pidof

   pidof 是一个在类 Unix 系统中使用的命令行工具,它用于查找运行指定程序的进程 ID(PID)。这个工具在系统管理和脚本编写中非常有用,尤其是在需要根据程序名称获取其进程 ID 的情况下。

pidof [选项] [程序名称]
  • -s:仅返回一个 PID。
  • -c:仅返回具有相同启动参数的进程的 PID。
  • -x:仅返回通过脚本启动的进程的 PID。

http://www.kler.cn/news/336413.html

相关文章:

  • 分享国产32位单片机的电机控制方案
  • GEE 土地分类:利用Landsat C02 TOA数据进行土地分类精度超95%(希腊雅典为例)并监测不同年份的绿地面积
  • Android高级控件
  • (2025)408考研:王道操作系统文件管理强化
  • Sublime Text 下载地址分享
  • 看480p、720p、1080p、2k、4k、视频一般需要多大带宽呢?
  • 信息学奥赛使用的编程IDE:Dev-C++ 安装指南
  • Linux之进程概念
  • 毕业设计 深度学习昆虫识别系统(源码+论文)
  • Kafka的基本概念整理
  • UE5蓝图实战:打造自定义摄像机视野控制
  • 排序算法总结(含链表)
  • 民峰:为投资者提供稳健的财富管理方案
  • 鸿蒙开发(NEXT/API 12)【二次向用户申请授权】程序访问控制
  • 秋招内推2025-招联金融
  • 基于opencv-C++dnn模块推理的yolov5 onnx模型
  • King3399 SDK(ubuntu文件系统)编译简明教程
  • 我的笔记本电脑之前可以直接用音量键调节音量,后来需要fn键加音量键才能调节,这是为什么?
  • 【Spring】Spring MVC的项目准备和连接建立
  • BFS解决多源最短路问题_01矩阵_C++【含多源最短路问题介绍+dist数组介绍】