详解【网路编程】之Socket套接字编程

news2024/11/15 5:51:37


谢谢帅气美丽且优秀的你看完我的文章还要点赞、收藏加关注

没错,说的就是你,不用再怀疑!!!

希望我的文章内容能对你有帮助,一起努力吧!!!


1、Socket套接字

Socket 是一个编程接口(网络编程接口),是一种特殊的文件描述符( write/read )。 Socket 并不 仅限于 TCP/IP

Socket 独立于具体协议的编程接口,这个接口位于 TCP/IP 四层模型的应用层与传输层之间

1.1Socket的类型

  • 流式套接字:( SOCK_STREAM )
    • 面向字节流,针对于传输层协议为 TCP 协议的网络应用
  • 数据报套接字:( SOCK_DGRAM )
    • 面向数据报,针对于传输层协议为 UDP 协议的网络应用
  • 原始套接字:( SOCK_RAW )
    • 直接跳过传输层

1.2基于的TCP套件字编程流程

任何网络应用都会有通信双方:

  • Send 发送端
  • recv 接收端

TCP 网络应用

  • Client 客户端( TCP )
  • Server 服务端( TCP )

任何的网络应用:

  • 传输层的协议(UDP/TCP)+端口号+IP地址

网络地址:

  • 任何网络应用任意一方都需要有一个 网络地址 ( IP+端口 )

1.2.1TCP网络应用执行的大致过程

  • 建立连接
    • 三次握手
  • 发送/接收数据
    • 发送数据:write/send/sendto
    • 接收数据:read/recv/recvfrom
  • 关闭连接
    • 四次挥手

1.3TCP网络应用的编程流程

1.3.1TCP-Server服务端

1)建立一个套件字:( socket )

2)绑定一个网络地址:( bind )

  • 并不是任意的地址都可以(需要合法且能够正常访问)
  • 把一个套接字和一个网络地址进行绑定。如果想让其他人来主动联系/连接,就需要绑定一个 地址,并且需要把这个地址告诉其他人。不进行绑定,并代表套接字没有地址,不进行绑定套 接字在进行通信时候,内核会动态为套接字指定一个地址。

3)等待监听:( listen)

让一个套接字进入一个 监听状态

4)等待客户端的连接:( accept )

  • 等待客户端来发起连接和客户端建立 TCP 连接
    • 三次握手
  • 函数成功返回表示和一个客户端完成连接
  • 多次调用函数就可以与不同的客户端进行连接

5)数据的传输: 读/写

发送数据: write/send/sendto

接收数据: read/recv/recvfrom

6)关闭套接字:( close/shutdown)

四次挥手

1.3.2TCP-Client客户端

  • 建立一个套接字: socket
  • 绑定地址:可选/可以绑定也可也不绑定
  • 发起连接请求: connect
    • 主动的与 TCP-Server 建立连接。三次握手
  • 数据的传输:读/写
    • 发送数据:write/send/sendto
    • 接受数据:read/recv/recvfrom
  • 关闭套接字
    • close

1.4网络通信协议

协议:就是通信双方约定好的通信规则。

下面举例一个基础的C/S过程:

客户端:

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

typedef enum
{
    CMD,
    MSG,
    IMG
}Data_t;

struct package
{
	// 数据类型
	Data_t DataType;
	
	// 数据实际的大小
	int DataSize;
	
	// 数据本体
	unsigned char Datas[0]; // 柔性数组
};

/*
    通过main函数的参数传递IP和端口
*/
int main(int argc,const char *argv[])
{
    // 申请套接字
    int sock_fd = socket(AF_INET,SOCK_STREAM,0);
    if(sock_fd == -1)
    {
        perror("申请失败");
        return -1;
    }

    std::cout << "套接字申请成功" << std::endl;

    std::cout << "客户端发起连接..." << std::endl;
    // 等待客户端连接
    struct sockaddr_in server;
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = inet_addr(argv[1]);
    server.sin_port = atoi(argv[2]);
    
    if(connect(sock_fd,(struct sockaddr*)&server,sizeof(server) )== -1)
    {
        perror("连接失败");
        close(sock_fd);
        return -1;
    }

    std::cout << "客户端连接成功" << std::endl;

    // 数据传输
    while(1)
    {
        std::string str;
        std::cout << "输入消息:";
        std::cin >> str;

        // 应用层封包过程
        struct package *data = (struct package*)new char[sizeof(struct package)+str.size()];
        data->DataType = MSG;
        data->DataSize = str.size();
        strcpy((char *)data->Datas,str.c_str());

        int ret = write(sock_fd,data,sizeof(struct package)+str.size());
        if(ret == -1||str == "exit")
            break;
    }

    // 关闭套接字
    close(sock_fd);
    return 0;
}

服务端:

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

typedef enum
{
    CMD,
    MSG,
    IMG
}Data_t;


struct package
{
	// 数据类型
	Data_t DataType;
	
	// 数据实际的大小
	int DataSize;
	
	// 数据本体
	unsigned char Datas[0]; // 柔性数组
};

/*
    通过main函数的参数传递IP和端口
*/
int main(int argc,const char *argv[])
{
    // 申请套接字
    int sock_fd = socket(AF_INET,SOCK_STREAM,0);
    if(sock_fd == -1)
    {
        perror("申请失败");
        return -1;
    }

    std::cout << "套接字申请成功" << std::endl;

    // 绑定网络地址
    struct sockaddr_in local; // 绑定本机的网络地址
    local.sin_family = AF_INET;
    local.sin_addr.s_addr = inet_addr(argv[1]); // 将字符串的ip地址转化为网络字节序列
    local.sin_port = atoi(argv[2]);
    
    if(bind(sock_fd,(struct sockaddr*)&local,sizeof(local)) == -1)
    {
        perror("绑定失败");
        close(sock_fd);
        return -1;
    }

    std::cout << "套接字绑定成功" << std::endl;

    // 进入监听状态
    if(listen(sock_fd,10) == -1)
    {
        perror("监听失败");
        close(sock_fd);
        return -1;
    }

    std::cout << "套接字监听成功" << std::endl;

    std::cout << "等待客户端连接..." << std::endl;
    // 等待客户端连接
    struct sockaddr_in client;
    socklen_t client_size = sizeof(client);
    int newSock = accept(sock_fd,(struct sockaddr*)&client,&client_size);

    // 判断客户端是否连接成功
    if(newSock == -1)
    {
        perror("客户端连接失败");
        close(sock_fd);
        return -1;
    }

    std::cout << "客户端连接成功" << std::endl;

    // 数据传输
    while(1)
    {
        struct package *data = (struct package*)malloc(sizeof(struct package));

        // 获取第一次数据
        int ret = read(newSock,data,sizeof(struct package));
        if(ret == -1)
            break;

        std::cout << "本次数据包的大小:" << data->DataSize << std::endl;

        // 重新为结构体分配空间
        data = (struct package*)realloc(data,sizeof(struct package)+data->DataSize);

        // 获取第二次数据
        ret = read(newSock,data->Datas,data->DataSize);
        if(ret == -1 || std::string((char *)data->Datas) == "exit")
            break;

        std::cout << "来自服务端的消息<" << inet_ntoa(client.sin_addr) << ">" << data->Datas << std::endl;

        free(data);
    }

    // 关闭套接字
    close(sock_fd);
    return 0;
}

1.5UDP套接字编程

UDP 传输层的协议,面向无连接,数据报的传输层协议。

“ 无连接 ”:不可靠

  • 在网络环境较好的情况下, UDP 效率较高
  • 在网络环境较差的情况下, UDP 可能存在丢包的情况
  • 同时一些“ 实时应用 ” 采用 UDP
  • 在应用层加一些保证传输可靠的“ 控制协议 ”

1.5.1UDP Server(接收端) :

  • 创建一个套接字
  • 绑定网络地址
  • 数据通信
  • 关闭套接字

1.5.2UDP Client(发送端) :

  • 创建一个套接字
  • 关闭套接字

下面是有关UDP的C/S过程基础示例:

服务端:

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


int main()
{
    int sock_id = socket(AF_INET,SOCK_DGRAM,0);

    if(sock_id == -1)
    {
        perror("套接字申请失败");
        return -1;
    }

    while(1)
    {
        char buf[100]={0};

        std::cout << "请输入发送的消息:";
        std::cin >> buf;

        // 指定目标地址
        struct sockaddr_in revcAddress;
        revcAddress.sin_family = AF_INET;
        revcAddress.sin_addr.s_addr = inet_addr("192.168.8.130");
        revcAddress.sin_port = 8899;
        if(sendto(sock_id,buf,strlen(buf),0,(struct sockaddr*)&revcAddress,sizeof(revcAddress)) == -1||std::string(buf) == "exit")
            break;
    }



    return 0;
}

客户端:

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


int main()
{
    int sock_id = socket(AF_INET,SOCK_DGRAM,0);

    if(sock_id == -1)
    {
        perror("套接字申请失败");
        return -1;
    }

    // 进行地址绑定,绑定了你才可以接收消息
    struct sockaddr_in local;
    local.sin_family = AF_INET;
    local.sin_port   = 8899;
    local.sin_addr.s_addr = inet_addr("192.168.8.130");
    if(bind(sock_id,(struct sockaddr*)&local,sizeof(local)) == -1)
    {
        perror("套接字申请失败");
        close(sock_id);
        return -1;
    }


    while(1)
    {
        char buf[100]={0};


        // 指定目标地址
        struct sockaddr_in sendAddress;
        socklen_t size = sizeof(sendAddress);
        if(recvfrom(sock_id,buf,1000,0,(struct sockaddr*)&sendAddress,&size) == -1||std::string(buf) == "exit")
            break;

        std::cout << "来自于<"<< inet_ntoa(sendAddress.sin_addr) <<"> : " << buf << std::endl;
    }



    return 0;
}

2、设置套接字

每一个套接字都有一些不同行为和属性:

  • 如:每个套接字在内核中,都会有两个缓冲区
    • send buffer
    • recv buffer
  • 每个缓冲区的大小是多少?
  • 缓冲区可以设置其大小的。

2.1获取套接字选项

2.2设置套接字选项

2.3套接字选项表

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

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

相关文章

4G 和 5G 中的单域注册(VoLTE和VoNR适用)VoNR 中的 CSRetry

目录 1. 4G 和 5G 中的单域注册&#xff08;VoLTE和VoNR适用&#xff09; 1.1 主要内容 1.2 什么是 4/5G 网络中的单域注册 1.3 为什么需要单域注册 1.4 单域注册主要参数之&#xff1a;Dual-Registration-5G-Indicator 1.5 单域注册主要参数之&#xff1a;DualRegistrat…

基于微信小程序地图实现点位标注、覆盖物、地图聊天

目录 小程序部分map标签的使用获取用户经纬度并转换地址地图点击事件覆盖物标注点击并实现弹窗交互数据库及接口部分数据库表结构设计API搭建小程序接口使用注意事项wx.getLocation深入控制地图小程序部分 map标签的使用 创建小程序的步骤这里不再重复赘述,在wxml页面中放一个…

EtherCAT运动控制器PT/PVT实现用户自定义轨迹规划

ZMC408CE硬件介绍 ZMC408CE是正运动推出的一款多轴高性能EtherCAT总线运动控制器&#xff0c;具有EtherCAT、EtherNET、RS232、CAN和U盘等通讯接口&#xff0c;ZMC系列运动控制器可应用于各种需要脱机或联机运行的场合。 ZMC408CE支持8轴运动控制&#xff0c;最多可扩展至32轴…

美团到店面经

redis中大key引起的问题 1、阻塞请求 Big Key对应的value较大&#xff0c;我们对其进行读写的时候&#xff0c;需要耗费较长的时间&#xff0c;这样就可能阻塞后续的请求处理。Redis的核心线程是单线程&#xff0c;单线程中请求任务的处理是串行的&#xff0c;前面的任务完不成…

【RabbitMQ】 相关概念 + 工作模式

本文将介绍一些MQ中常见的概念&#xff0c;同时也会简单实现一下RabbitMQ的工作流程。 MQ概念 Message Queue消息队列。是用来存储消息的队列&#xff0c;多用于分布式系统之间的通信。 系统间调用通常有&#xff1a;同步通信和异步通信。MQ就是在异步通信的时候使用的。 同…

Go 1.19.4 错误处理-Day 11

1. 错误处理机制 1.1 先看一段代码&#xff0c;引出Golang错误处理机制 看到了上面的代码后&#xff0c;思考两个问题&#xff1f; &#xff08;1&#xff09;有没有办法&#xff0c;能让test()执行出错了&#xff0c;它下面的fmt代码依然能够运行。 &#xff08;2&#xff09…

service 管理 web 管理插件

clusterIP 资源清单文件 [rootmaster ~]# kubectl create service clusterip websvc --tcp80:80 --dry-runclient -o yaml 解析域名 创建后端应用 负载均衡 固定 IP 服务 端口别名 nodePort 对外发布服务 Ingress 对外发布服务 查询 ingress 控制器类名称 kubectl g…

一个功能强大、易于使用的开源WEB表单构建工具Formbricks

大家好&#xff0c;今天给大家分享的是一个功能强大、易于使用的表单构建工具Formbricks&#xff0c;能够帮助开发者和非开发者快速构建各种类型的表单。 项目介绍 Formbricks 是一个开源的、基于 Web 的表单构建器&#xff0c;旨在帮助开发者和非开发人员轻松创建复杂的表单&…

大数据技术——Hadoop运行环境搭建

目录 一、 Hadoop运行环境搭建 1.1 模板虚拟机环境准备 1.2 克隆虚拟机 一、 Hadoop运行环境搭建 1.1 模板虚拟机环境准备 0&#xff09;安装模板虚拟机&#xff0c;IP地址192.168.10.100、主机名称hadoop100、内存4G、硬盘50G 具体操作参照下列文档 大数据技术之模板虚…

8.15日学习打卡---Spring Cloud Alibaba(三)

8.15日学习打卡 目录&#xff1a; 8.15日学习打卡为什么需要服务网关Higress是什么安装DockerCompose部署Higress创建网关微服务模块Higress路由配置Higress策略配置-跨域配置Higress解决如何允许跨域Higress策略配置之什么是HTTP认证Higress策略配置-Basic 认证什么是JWT认证J…

腾讯云AI代码助手 —— 编程新体验,智能编码新纪元

阅读导航 引言一、开发环境介绍1. 支持的编程语言2. 支持的集成开发环境&#xff08;IDE&#xff09; 二、腾讯云AI代码助手使用实例1. 开发环境配置2. 代码补全功能使用&#x1f4bb;自动生成单句代码&#x1f4bb;自动生成整个代码块 3. 技术对话3. 规范/修复错误代码4. 智能…

C++ | stack/queue

前言 本篇博客讲解cSTL中的stack/queue &#x1f493; 个人主页&#xff1a;普通young man-CSDN博客 ⏩ 文章专栏&#xff1a;C_普通young man的博客-CSDN博客 ⏩ 本人giee: 普通小青年 (pu-tong-young-man) - Gitee.com 若有问题 评论区见&#x1f4dd; &#x1f389;欢迎大…

LMDeploy 量化部署实践闯关任务-50%的A100跑的过程

基础任务&#xff08;完成此任务即完成闯关&#xff09; 使用结合W4A16量化与kv cache量化的internlm2_5-7b-chat模型封装本地API并与大模型进行一次对话&#xff0c;作业截图需包括显存占用情况与大模型回复&#xff0c;参考4.1 API开发(优秀学员必做)使用Function call功能让…

docker compose部署rabbitmq集群,并使用haproxy负载均衡

一、创建rabbitmq的data目录 mkdir data mkdir data/rabbit1 mkdir data/rabbit2 mkdir data/rabbit3 二、创建.erlang.cookie文件&#xff08;集群cookie用&#xff09; echo "secretcookie" > .erlang.cookie 三、创建haproxy.cfg配置文件 global log stdout fo…

力扣 | 动态规划 | 动态规划在树的应用

文章目录 一、96. 不同的二叉搜索树二、95. 不同的二叉搜索树 II三、337. 打家劫舍 III 一、96. 不同的二叉搜索树 LeetCode&#xff1a;96. 不同的二叉搜索树 只求个数实际上比较简单&#xff0c;定义dp[i]表示结点个数为i的二叉搜索树的种树。&#xff08;其实和记忆化搜索…

SpringBoot 自定义 starter

1. 官方文档 SpringBoot 版本 2.6.13&#xff0c;相关链接 Developing with Spring Boot 1.1 什么是 Starter Starters are a set of convenient dependency descriptors that you can include in your application. You get a one-stop shop for all the Spring and relate…

【Redis】数据结构篇

文章目录 键值对数据库是怎么实现的&#xff1f;动态字符串SDSC 语言字符串的缺陷SDS结构设计 整数集合整数集合结构设计整数集合的升级操作 哈希表哈希表结构设计哈希冲突链式哈希Rehash渐进式rehashrehash触发条件 压缩列表压缩列表结构设计连续更新压缩列表的缺陷 quicklist…

深入InnoDB核心:揭秘B+树在数据库索引中的高效应用

目录 一、索引页与数据行的紧密关联 &#xff08;一&#xff09;数据页的双向链表结构 &#xff08;二&#xff09;记录行的单向链表结构 二、未创建索引情况 &#xff08;一&#xff09;无索引下的单页查找过程 以主键为搜索条件 以非主键列为搜索条件 &#xff08;二…

财务会计与管理会计(六)

文章目录 高端费用查询图表VLOOKUP函数应用一段简单的VBA代码的应用 入库税金的二维分析SUMPRODUCT函数的应用 多姿多彩的数据表MOD函数的应用和万能表的应用 判断取值与查找取值的关系INDEX与ATCH函数在查找取值中的应用 在职期间项目分布统计表IF函数的应用 自动填充序号应用…

安卓中Room持久化库的使用

在Android开发中&#xff0c;Room是Google提供的一个持久化库&#xff0c;旨在为应用提供SQLite的抽象层&#xff0c;以简化数据库的访问和操作。相比直接使用SQLite&#xff0c;Room提供更清晰、更简洁的数据库访问机制。 1. Room的基础知识 1.1 引入Room依赖 首先&#xff…