计算机网络:套接字

1.相关函数:

1.1 socket()

获取套接字socket( int domain , int type , int potacl);

参数解析:

domain

指定通信域,主要的类型:

  1. UNIX(AF_UNIX) 同一台主机的进程间通信。

  2. IPV4域(AF_INET)跨网络通信。

  3. IPV6域(AF_INET6)跨网络通信。

type:

  1. SOCK_STREAM (流式套接字)
  • 通信模式:提供面向连接的、可靠的、双向的、基于字节流的通信。
  • 核心特性:
  • 可靠:数据无差错、无丢失、无重复,且按发送顺序到达(TCP 协议的特性)。
  • 流式:数据没有明确的"消息"边界。发送方多次写入的数据,接收方可能一次读出,反之亦然。应用层需要自己处理消 息边界(如添加长度头或分隔符)。
  • 连接导向:通信前必须建立连接(三次握手),通信后需要断开连接(四次挥手)。
  • 底层协议:通常与 TCP 协议配对。
  • 典型应用:HTTP、HTTPS、FTP、SSH、数据库连接等所有需要可靠数据传输的场景。
  1. SOCK_DGRAM (数据报套接字)
  • 通信模式:提供无连接的、不可靠的、固定最大长度的、基于数据报的通信。
  • 核心特性:
  • 不可靠:不保证数据一定到达,不保证顺序,不保证不重复。
  • 有边界:每个 send 或 sendto 调用产生一个独立的数据报,接收方 recvfrom 会完整收到整个数据报。消息不会粘在一 起。
  • 无连接:无需建立和断开连接,直接发送给目标地址。
  • 底层协议:通常与 UDP 协议配对。
  • 典型应用:DNS 查询、音视频流、在线游戏、广播/多播、SNMP 等能容忍少量丢包或要求低延迟的场景。

protocal:
场景 推荐设置 说明
普通 TCP套接字 protocol = 0 系统自动选择 TCP
普通 UDP 套接字 protocol = 0 系统自动选择 UDP
原始套接字 必须显式指定 IPPROTO_ICMP、IPPROTO_RAW
使用非默认协议 指定具体协议常量 IPPROTO_SCTP
需要协议过滤 指定要过滤的协议 原始套接字按协议过滤
返回值:成功返回一个socket,失败返回-1。与文件描述符一样,通过不同的int类型的描述符打开某个文件或指定获取特定端口的数据。

复制代码
sockfd = socket(AF_INET, SOCK_DGRAM, 0);  // 使用获取udp通信的套接字

1.2 struct_sockaddr_in结构体

用于指定套接字对应的端口和允许接收来自特定ip的消息。

参数解析:
_SOCKADDR_COMMON (sin)


通过拼接的方式实现的变量 sa_family_t sin_family。
用于指定通信域,对应socket中的domain。
sin_port指定套接字需要对应的端口号。

复制代码
typedef uint16_t in_port_t;

struct in_addrsin_addr:指定32位的ip。

复制代码
typedef uint32_t in_addr_t;
struct in_addr
  {
    in_addr_t s_addr;
  };

struct sockaddr_in serveraddr;
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(port);
// serveraddr.sin_addr.s_addr=inet_addr(ip.cstr());     // 指定特定的ip访问
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); // 接收所有的ip

注意:

1.由于主机和网络在数据表示上的差异,需要使用函数进行数据来回转化,htons(host to net shore)将shore类型(16个比特位)的数据从主机转换为网络数据。

  1. 我们输入ip的形式是"127.0.0.1"需要调用inet_addr函数转化为网络数据,INADDR_ANY定义为0x00000000,当ip为0.0.0.0时,表示可以接收所有访问服务端port的ip,服务器接收ip一般设为INADDR_ANY。

1.3 bind()

bind( int sockfd ,const struct sockaddr * addr ,socklen_t len ):服务器通过 bind() 告诉操作系统:"请在我的这个套接字上监听指定的 IP 和端口"。

实现初始化函数

复制代码
 void Init()
    {
        // 1.获取套接字
        sockfd = socket(AF_INET, SOCK_DGRAM, 0);

        // 2.指定直接接收消息的端口和ip地址到sockaddr_in
        struct sockaddr_in serveraddr;
        serveraddr.sin_family = AF_INET;
        serveraddr.sin_port = htons(port);
        // serveraddr.sin_addr.s_addr=inet_addr(ip.cstr());     // 指定特定的ip访问
        serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); // 接收所有的ip

        // 3.绑定套接字和sockaddr_in
        int ret = bind(sockfd, (const sockaddr *)&serveraddr, (socklen_t)sizeof(serveraddr));
        if (ret == -1)
        {
            cout << "绑定失败:" << strerror(errno) << endl;
            exit(1);
        }
    }

1.4 recvfrom

复制代码
recvfrom (int __fd, void *__restrict __buf, size_t __n, int __flags,
	  __SOCKADDR_ARG __addr, socklen_t *__restrict __addr_len)

参数解析:

_fd:自己监听的套接字。

_buf:接收的消息放在buf中,输出型参数。

_n:buf最大可以存储数据的字节数。

_addr:和struct sockaddr_in 一样,在这里,存储的是访问者(客户端)的ip,port,domain,输出型参数。

_len:_addr的长度的地址。

复制代码
 // 接收消息
    void start()
    {

        while (1)
        {
            char buffer[1024];
            struct sockaddr_in cilentmessage;
            socklen_t len = sizeof(cilentmessage);
            ssize_t ret = recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr *)&cilentmessage,&len);
            if (ret > 0)
            {
                buffer[ret] = 0;
                int16_t port = htons(cilentmessage.sin_port);
                string ip = inet_ntoa(cilentmessage.sin_addr);
                string message = buffer;
                cout << "ip: " << ip << "  port: " << port << "  message: " << message << endl;
            }
        }
    }

2. 实现主函数

复制代码
void manual()
{
    printf("use manual: \n            filename:        port: \n");
}
int main(int argc,const char* argv[])
{
    if(argc!=2)
    {
        manual();
        exit(2);
    }
    string port=argv[1];
    unique_ptr<server> myserver(new server(stoi(port)));
    myserver->Init();
    myserver->start();
    return 0;
}

执行程序,查看网络情况


客户端正在执行,实现成功。

3. 用户端

复制代码
cilent.cpp:

#include<iostream>
#include<memory>
#include"cilent.h"
using namespace std;

void manual()
{
    printf("use manual: \n            filename:       ip:      port: \n");
}
int main(int argc,const char* argv[])
{
    if(argc!=3)
    {
        manual();
        exit(2);
    }
    string port=argv[2];
    string ip=argv[1];
    unique_ptr<cilent> mycilent(new cilent(stoi(port),ip));
    mycilent->Init();
    mycilent->start();
    return 0;
}




cilent.h:

class cilent
{
    int16_t port;
    string ip;
    int sockfd;

public:
    cilent(int16_t _port, string _ip) : port(_port), ip(_ip) {}
    ~cilent() {}
    void Init()
    {
        sockfd = socket(AF_INET, SOCK_DGRAM, 0);
        if (sockfd == -1)
        {
            cout << "error: " << strerror(errno) << endl;
            exit(2);
        }
        // cilent不需要bind,OS会自己选择使用哪个端口去访问客户端
    }

    void start()
    {
        struct sockaddr_in servermess;
        memset(&servermess,0,sizeof(servermess));
        servermess.sin_port = htons(port);
        servermess.sin_family = AF_INET;
        servermess.sin_addr.s_addr = inet_addr(ip.c_str()); // 使用127.0.0.1测试
        char buffer[1024];
        while (1)
        {
            cout<<"Please Enter: ";
            fgets(buffer,sizeof(buffer),stdin);
            socklen_t len = sizeof(servermess);
            int ret = sendto(sockfd, buffer, strlen(buffer), 0, (struct sockaddr *)&servermess,len);
        }
    }
};

4. 测试交流

相关推荐
YuanDaima20481 小时前
WSL2 与 Ubuntu 22.04 基础环境部署指南
linux·运维·服务器·人工智能·ubuntu·docker
万邦科技Lafite1 小时前
API接口一键获取商品评论,根据商品评论分析客户画像
linux·服务器·数据库·windows·microsoft·电商开放平台
其实防守也摸鱼1 小时前
DVWA--Brute Force (暴力破解)通关指南
服务器·网络·安全·靶场·教程·工具·dvwa
sulikey2 小时前
操作系统磁盘 I/O:为何选择“块“而非“扇区“?
linux·操作系统·io·磁盘·磁盘io
一个人旅程~2 小时前
如何永久关闭bitlocker并防止出现更新后被强制加密?
linux·windows·经验分享·电脑
源远流长jerry2 小时前
TCP 三次握手深度解析:从内核源码到生产实践
linux·运维·网络·网络协议·tcp/ip
黄筱筱筱筱筱筱筱2 小时前
RHCE---web服务器①
linux·运维·服务器
上海云盾安全满满2 小时前
服务器被攻击了,更换IP是否有用吗
服务器·网络·tcp/ip
AC赳赳老秦3 小时前
全链路自动化巡检:用 OpenClaw 实现服务器 - 应用 - 数据库全链路巡检,自动生成报告与整改建议
服务器·数据库·人工智能·深度学习·自动化·deepseek·openclaw