socket编程-TCP各函数及其用法

news2024/9/23 19:16:41

socket编程-TCP


socket主要类型

流套接字(SOCK_STREAM)

流套接字用于提供面向连接、可靠的数据传输服务。该服务将保证数据能够实现无差错、无重复送,并按顺序接收。流套接字之所以能够实现可靠的数据服务,原因在于其使用了传输控制协议,即TCP协议。

数据报套接字(SOCK_DGRAM)

数据报套接字提供一种无连接的服务。该服务并不能保证数据传输的可靠性,数据有可能在传输过程中丢失或出现数据重复,且无法保证顺序地接收到数据。数据报套接字使用UDP协议进行数据的传输。由于数据报套接字不能保证数据传输的可靠性,对于有可能出现的数据丢失情况,需要在应用层中做相应的处理。

原始套接字(SOCK_RAW)

原始套接字与标准套接字(标准套接字指的是前面介绍的流套接字和数据报套接字)的区别在于:原始套接字可以读写内核没有处理的IP数据包,而流套接字只能读取TCP协议的数据,数据报套接字只能读取UDP协议的数据。因此,如果要访问其他协议发送的数据必须使用原始套接。

实际应用中,数据报socket的应用场景比较少

流套接字(SOCK_STREAM)流程图

在这里插入图片描述

socket函数:socket函数用于创建一个新的socket,也就是向系统申请一个socket资源,socket函数用户客户端和服务端。socket()函数的返回值其本质是一个文件描述符,是一个整数。一般情况下,socket创建都会成功,除非系统资源耗尽,socket函数一般不会返回失败(-1)。其函数声明如下:

int socket(int domain, int type, int protocol);
  1. domain
     协议域,又称协议族(family)。常用的协议族有AF_INET、AF_INET6、AF_LOCAL(或称AF_UNIX,Unix域Socket)、AF_ROUTE等。协议族决定了socket的地址类型,在通信中必须采用对应的地址 ,如AF_INET决定了要用ipv4地址(32位的)与端口号(16位的)的组合、AF_UNIX决定了要用一个绝对路径名作为地址。

  2. type
     指定socket类型。常用的socket类型有SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等。流式socket(SOCK_STREAM)是一种面向连接的socket,针对于面向连接的TCP服务应用。数据报式socket(SOCK_DGRAM)是一种无连接的socket,对应于无连接的UDP服务应用。

  3. protocol
     指定协议。常用协议有IPPROTO_TCP、IPPROTO_UDP、IPPROTO_STCP、IPPROTO_TIPC等,分别对应TCP传输协议、UDP传输协议、STCP传输协议、TIPC传输协议。

    示例代码如下:

	//1.创建套接字
    int ServerSockFd;
    if((ServerSockFd=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP))==-1)//socket创建失败
    {
        perror("socket 创建失败");
        return -1;
    }

sockaddr结构体与sockaddr_in结构体:

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

struct sockaddr {  
     sa_family_t sin_family;//地址族
    char sa_data[14]; //14字节,包含套接字中的目标地址和端口信息               
   };

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

/* 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)];
  };
  1. 二者长度一样,都是16个字节,即占用的内存大小是一致的,因此可以互相转化。二者是并列结构,指向sockaddr_in结构的指针也可以指向sockaddr。
  2. sockaddr常用于bind、connect、recvfrom、sendto等函数的参数,指明地址信息,是一种通用的套接字地址。
  3. sockaddr_in 是internet环境下套接字的地址形式。所以在网络编程中我们会对sockaddr_in结构体进行操作,使用sockaddr_in来建立所需的信息,最后使用类型转化就可以了。一般先把sockaddr_in变量赋值后,强制类型转换后传入用sockaddr做参数的函数:sockaddr_in用于socket定义和赋值;sockaddr用于函数参数。

两个函数 htons() 和 inet_addr()。

  1. htons()作用是将端口号由主机字节序转换为网络字节序的整数值。(host to net)
  2. inet_addr()作用是将一个IP字符串转化为一个网络字节序的整数值,用于sockaddr_in.sin_addr.s_addr。
  3. inet_ntoa()作用是将一个sin_addr结构体输出成IP字符串(network to ascii)。
  4. htonl()将u_long从主机转换为 TCP/IP 网络字节顺序 (,这是大端)
  5. 已服务端为例进行演示
	//2.进行绑定,指定用于通信的ip地址和端口
    struct sockaddr_in ServerAddr;//服务端地址信息数据结构
    //将协议簇指定为ipv4
    ServerAddr.sin_family=AF_INET;
    //服务端程序绑定本主机的任意IP地址
    ServerAddr.sin_addr.s_addr=htonl(INADDR_ANY);
    服务端程序绑定指定ip
    //ServerAddr.sin_addr.s_addr=inet_addr("127.0.0.1");
    ServerAddr.sin_port=htons(9000);

bind函数:服务端把用于通信的地址和端口绑定到socket上。如果绑定的地址错误,或端口已被占用,bind函数一定会报错,否则一般不会返回错误。如无错误发生,则bind()返回0。否则的话,将返回-1。其函数声明如下:

int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
  1. 参数sockfd,需要绑定的socket,是一个文件描述符。
  2. 参数addr,存放了服务端用于通信的地址和端口,用结构体sockaddr_in强制转换得到。
  3. 参数addrlen,表示addr结构体的大小。
  4. 返回值:成功则返回0,失败返回-1,错误原因存于errno 中。

    示例代码如下:
	if(bind(ServerSockFd,(sockaddr*)&ServerAddr,sizeof(ServerAddr))!=0)
    {
        perror("bind failed");
        close(ServerSockFd);
        return -1;
    }

listen函数把主动连接socket变为被动连接的socket,使得这个socket可以接受其它socket的连接请求,从而成为一个服务端的socket。//返回:0-成功, -1-失败 listen函数一般不会返回错误。其函数声明如下:

int listen(int sockfd, int backlog);
  1. 参数sockfd,已经被bind过的socket。
     socket函数返回的socket是一个主动连接的socket,在服务端的编程中,程序员希望这个socket可以接受外来的连接请求,也就是被动等待客户端来连接。需要通过某种方式来告诉系统当前的socket是可连接的,则可以通过调用listen函数来完成这件事。

  2. 参数backlog,这个参数涉及请求队列的大小。
     当套接字正在处理客户端请求时,如果有新的请求进来,套接字是没办法处理的,只能把请求放进缓冲区,等待当前请求结束后,再从缓冲区中读取出来进行处理。如果不断有新的请求进来,按照先后顺序在缓冲区排队,直到缓冲区满,这个缓冲区就称为请求队列。

  3. 当调用listen之后,服务端的socket就可以调用accept来接受客户端的连接请求。

示例代码如下:

	//3.将socket设置为监听模式 listen成功返回0,失败返回-1
    if(listen(ServerSockFd, 5))
    {
        perror("listen failed");
        close(ServerSockFd);
        return -1;
    }

accept函数等待客户端的连接,如果没有客户端连上来,它就一直等待,这种方式称之为阻塞。accept等待到客户端的连接后,创建一个新的socket,函数返回值就是这个新的socket,服务端使用这个新的socket和客户端进行报文的收发。accept在等待的过程中,如果被中断或其它的原因,函数返回-1,表示失败,如果失败,可以重新accept。

// 返回值:成功则返回0,失败返回-1,错误原因存于errno 中。
// accept在等待的过程中,如果被中断或其它的原因,函数返回-1,表示失败,如果失败,可以重新accept。
int accept(int sockfd,struct sockaddr *addr,socklen_t *addrlen);
  1. 参数sockfd是已经被listen过的socket。
  2. 参数addr用于存放客户端的地址信息,用sockaddr结构体表达,如果不需要客户端的地址,可以填0。
  3. 参数addrlen用于存放addr参数的长度,如果addr为0,addrlen也填0。

示例代码如下:

	//4.接收客户端的请求
    int ClientSockFd;
    struct sockaddr_in ClientAddr;
    int len=sizeof(ClientAddr);
    if((ClientSockFd=accept(ServerSockFd, (sockaddr*)&ClientAddr, (socklen_t*)&len))==-1)
    {
        perror("accept failed");
        close(ServerSockFd);
        close(ClientSockFd);
        return -1;
    }

send函数用于把数据通过socket发送给对端。不论是客户端还是服务端,应用程序都用send函数来向TCP连接的另一端发送数据。

// 函数返回已发送的字符数。出错时返回-1,错误信息errno被标记
// 如果send函数返回的错误(<=0),表示通信链路已不可用。
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
  1. sockfd:已建立好连接的socket。
  2. buf:需要发送的数据的内存地址,可以是C语言基本数据类型变量的地址,也可以数组、结构体、字符串,内存中有什么就发送什么。
  3. len:需要发送的数据的长度,为buf中有效数据的长度。
  4. flags填0, 其他数值意义不大。

示例代码如下:

		if((ret=send(ClientSockFd,sen,sizeof(sen),0))==-1)
        {
            std::cout<<"send ret="<<ret<<std::endl;
            break;
        }
        std::cout<<"服务端发送:"<<rec<<std::endl;
        std::cout<<"send ret="<<ret<<std::endl;

recv函数用于接收对端通过socket发送过来的数据。不论是客户端还是服务端,应用程序都用recv函数接收来自TCP连接的另一端发送过来数据。

// 函数返回已接收的字符数。出错时返回-1,失败时不会设置errno的值。
// 如果recv函数返回的错误(<=0),表示通信通道已不可用。
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
  1. sockfd:已建立好连接的socket。
  2. buf:用于接收数据的内存地址,可以是C语言基本数据类型变量的地址,也可以数组、结构体、字符串,只要是一块内存就行了。
  3. len:需要接收数据的长度,不能超过buf的大小,否则内存溢出。
  4. flags填0, 其他数值意义不大。

示例代码如下:

		//recv接收函数,失败返回-1
        if((ret=recv(ClientSockFd,rec,sizeof(rec),0))==-1)
        {
            std::cout<<"recv ret="<<ret<<std::endl;
            break;
        }

        std::cout<<"服务端接收:"<<rec<<std::endl;
        std::cout<<"recv ret="<<ret<<std::endl;

connect函数用于将参数sockfd 的socket 连至参数serv_addr 指定的服务端,参数addrlen为sockaddr的结构长度。返回值:成功则返回0,失败返回-1,错误原因存于errno 中。connect函数只用于客户端,,如果服务端的地址错了,或端口错了,或服务端没有启动,connect一定会失败。

int connect(int sockfd, struct sockaddr * serv_addr, int addrlen);
  1. sockfd:标识未连接套接字的描述符。
  2. serv_addr:指向应建立连接的 sockaddr 结构的指针。
  3. addrlen:名称参数指向的 sockaddr 结构的长度(以字节为单位)

示例代码如下:

	//申请服务端地址信息数据结构 因为要与服务端进行相连
    struct sockaddr_in ServerAddr;
    //绑定端口和地址
    ServerAddr.sin_family=AF_INET;//ipv4
    ServerAddr.sin_port=htons(9000);
    ServerAddr.sin_addr.s_addr=inet_addr("127.0.0.1");

    //2.连接套接字
    if(connect(ClientSockFd, (sockaddr*)&ServerAddr, sizeof(ServerAddr))==-1)
    {
        perror("connect failed");
        return -1;
    }
    std::cout<<"客户端连接成功"<<std::endl;

最后完整简易服务端代码:https://blog.csdn.net/weixin_45256307/article/details/129417531

最后完整简易客户端代码:https://blog.csdn.net/weixin_45256307/article/details/129417589

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

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

相关文章

react框架基础入门

前端三大框架&#xff1a;angularvue —-2||3react区别&#xff1a;vue国内框架 封装较完成。全程使用封装的api来完成。react国外技术框架—-偏向于底层js实现。没有的大量的封装。需要使用js手动实现。react需求在不断增大。必会框架。官网https://react.docschina.org/ 中…

一文搞懂Linux时区设置、自定义时区文件

概念介绍 常说的 Linux 系统时钟有两个 一个是硬件时钟&#xff08;RTC&#xff09;&#xff0c;即BIOS时间&#xff0c;一般保存的是 GMT0 时间&#xff0c;没时区、夏令时的概念 一个是当地时钟&#xff08;LTC&#xff09;&#xff0c;即我们日常经常看到的时间&#xff0…

elasticsearch 分布式搜索引擎2

1.DSL查询文档 elasticsearch的查询依然是基于JSON风格的DSL来实现的。 1.1.DSL查询分类 Elasticsearch提供了基于JSON的DSL&#xff08;Domain Specific Language&#xff09;来定义查询。常见的查询类型包括&#xff1a; 查询所有&#xff1a;查询出所有数据&#xff0c;一…

手把手带你玩转Linux

今天这篇文章带你走进Linux世界的同时,带你手把手玩转Linux,加深对Linux系统的认识。 一、搞好Linux工作必须得不断折腾,说白了,只是动手力量必须强。我在初学Linux的那片,家中三台计算机,我在上边总是反反复复的进行着重装、网络ghost、双系统安装等的尝试。有很长一段时间里,…

CSS 之 background-clip 和 background-origin 属性

一、background-clip&#xff08;背景的绘制区域&#xff09; 1、纯色背景 该属性规定了背景的绘制区域&#xff0c;属性值有三种&#xff1a;border-box&#xff08;覆盖到边框区域&#xff09;、padding-box&#xff08;覆盖到padding区域&#xff0c;不包含border&#xf…

数据库学习笔记(4)——SQL语言之DQL

复杂分组查询举例 子查询&#xff1a;把select查询结果当作数据使用时&#xff0c;这种结构就是子查询 子查询练习 -- 子查询练习&#xff1a;查询学生成绩在70分以上的学生学号和姓名 select stu_id as id, stu_name as 姓名 from tb_student where stu_id in (select cc_sid…

Forter 对支付服务商应对欺诈的四个建议和Gartner的两个关键结论

Gartner新版2023年度《线上欺诈检测市场指南》发布恰逢其时&#xff0d;企业正面临来自专业黑产和欺诈者与日俱增的压力。而在2023年&#xff0c;许多商户将调整反欺诈策略&#xff0c;对拒付率和转化率进行更严格的监测&#xff0c;以最大限度减少损失并增加营收。以下是Gartn…

工作记录:bi重构

2023.3.8&#xff0c;我在组内进行工作汇报。内容记录如下&#xff1a; 本次重构的特点 改动大影响后续开发 所以有必要进行工作汇报&#xff0c;让组内同事了解代码的改动与现状。 为什么要重构代码&#xff1f; 正在开发的数据报告模块包含大量 widget 功能&#xff0c;…

量子计算(8)pyqpanda编程3测量操作

作为一名高产博主&#xff0c;小编我一天不写文章就浑身难受&#xff0c;这不&#xff0c;一闲下来就来给大家科普量子计算编程操作了。 今天我们要来探讨“测量操作”&#xff0c;众所周知&#xff0c;薛定谔的猫是一种既死又活的状态&#xff0c;很多人认为&#xff0c;猫是死…

数据分析介绍,让你更了解数据分析

同学们好&#xff01; 第一次接触互联网行业吗&#xff1f;没有关系&#xff0c;看完这篇介绍让你了解到底什么是数据分析&#xff1f;并对它强大的功能所吸引。希望你能耐心的看完&#xff0c;了解更多的行业信息&#xff01; 1、是不是不知道什么是数据分析&#xff1f; 定…

14.卷积神经网络中的不变性

欢迎访问个人网络日志&#x1f339;&#x1f339;知行空间&#x1f339;&#x1f339; 文章目录1.问题介绍2.DCNN的Invariance3.后记参考资料1.问题介绍 最近看的2015年前后的几篇语义分割网络中反复提到了一个点,深度卷积神经网络的内置不变性。 2014年11月发表的Fully Conv…

线程池的线程是如何复用的

前言 进程和线程的关系相信大家都知道&#xff0c;这里我就不做过多的解释了&#xff0c;既然一个进程是由多个线程组成的&#xff0c;那么线程池又是由若干个线程队列组成的&#xff0c;在并发量比较高的情景下&#xff0c;我们通常会去创建线程池就执行任务&#xff0c;而不…

CentOS 8 桌面版右上角网络图标消失的解决办法及查看虚拟机ip地址没有ens项

抄录与小李行 我们在手动修改网络连接的配置文件后&#xff0c;桌面右上角的网络连接图标可能会消失。 正常显示的网络图标&#xff1a; 网络图标消失后的样子&#xff1a; CentOS 的网络连接由网络管理工具 NetworkManager 负责&#xff0c; 这个问题多半与其有关。 可能的…

BC双驱、ChatGPT大火,AI独角兽撬开盈利大门?

配图来自Canva可画 放眼AI行业&#xff0c;各大AI玩家长期亏损、“钱”景堪忧。 回看过去一年&#xff0c;部分AI独角兽的亏损问题愈发尖锐——云从科技2022年净亏损同比扩大至8.5亿元&#xff1b;寒武纪2022年净亏损11.6亿元&#xff0c;较上年同期扩大41.4%&#xff1b;地平…

ZooKeeper命令及JavaAPI操作

ZooKeeper数据模型 ZooKeeper是一个树形目录服务&#xff0c;其数据模型和Uiix的文件目录树很类似&#xff0c;拥有一个层次化结构。这里面的每一个节点都被称为&#xff1a;ZNode&#xff0c;每个节点上都会保存自己的数据和节点信息。节点可以拥有子节点&#xff0c;同时也允…

数据链路层及交换机工作原理

目录 一&#xff0c;帧格式 1.1 帧头类型字段的作用 1.2 MAC地址 1.3 MTU值 二&#xff0c;交换机工作原理 2.1 交换机的端口 2.2 端口状态 三&#xff0c;交换机基本工作模式及命令 3.1 交换机的工作模式&#xff1a; 3.2 命令 一&#xff0c;帧格式 其中类型是指&am…

Linux: 向内核提交补丁

文章目录1. 前言2. 提交内核补丁的步骤2.1 从代码仓库下载内核源码2.2 建立本地分支 linux-next_master2.3 制作一个补丁2.4 提交补丁2.4.1 配置发送邮箱2.4.2 配置 git send-email2.4.3 发送补丁1. 前言 限于作者能力水平&#xff0c;本文可能存在谬误&#xff0c;因此而给读…

汽车制造商与IT公司之间的技术合作案例

如果您对最新汽车技术感兴趣的话,您可能经常听到汽车制造商和IT公司正在合作开发技术的消息。汽车生产商为何自身不进行技术开发,而是与IT企业合作呢?因为最近随着以IT技术为基础的电动汽车等环保汽车或无人驾驶汽车等的登场和发展,汽车制造商单独进行技术开发需要花费很多时间…

零死角玩转stm32初级篇6-中断

本篇博文目录:一.中断相关概念知识1.STM32 的中断和异常2.NVIC 中断控制器3.NVIC 结构体成员4.抢占优先级和响应优先级5.NVIC 的优先级组6.EXTI 外部中断7.中断服务函数二.按键点灯的二种实现方式1.按键和LED的原理图以及各种输入模式(浮空输入,上拉输入,下拉输入和模拟输入)2.…

到墨西哥的液体货物国际快递怎么邮寄

到墨西哥的液体货物国际快递怎么邮寄?有些时候邮递到墨西哥的物品&#xff0c;采用国际快递是一种很好的国际货物运输方式&#xff0c;但如果货品是液体的话&#xff0c;相对而言便会复杂一些。以下是方联货运物流分享的一些对于如何邮递液体货物国际快递的意见及注意事项&…