学习网络编程No.5【TCP套接字通信】

这篇具有很好参考价值的文章主要介绍了学习网络编程No.5【TCP套接字通信】。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

引言:

北京时间:2023/8/25/15:52,昨天刚把耗时3天左右的文章更新,充分说明我们这几天并不是在摆烂中度过,而是在为了更文不懈奋斗,历时这么多天主要是因为该部分知识比较陌生,所以需要我们花费大量的时间去细细研究,为后面无论是TCP套接字,还是网络的学习都能更加融会贯通。并且这几天在闲暇时间把《一念永恒》听了一下,目前还在过渡期,不过根据一些伏笔我认为小高潮即将来临,根据前期的一些内容我意识到为什么该小说能被动漫公司拍成动漫,主要应该是因为耳根对于主角前期的角色塑造相比于其它小说来说更加独具匠心,以人性最朴实的长生、怕死为目的,再配上各种小心思和漫不经心为行文规律,很好的就将大众在心目中对人性的理解给刻画出来,再加上耳根独到的幽默理解,可以说一个完美的小说主角就被呈现出来了。这也可能就是耳根能屹立网文巅峰的原因吧!相比那些爽文,其实也不错,但是相比于这种角色刻画更深的小说,差距还是非常大的,白金就是白金,虽然套路单一,但是文笔确实无可挑剔!ok,不谈了,该篇博客我们承接上篇博客有关UDP套接字的知识,来看一看有关TCP套接字相关的知识,并根据TCP套接字实现一份TCP版本的网络通信客户端和服务端。

学习网络编程No.5【TCP套接字通信】,学习网络编程,网络,学习,tcp/ip

深入套接字编程

在上篇博客中我们重点对socket编程中的sockaddr结构体进行了深入理解,并且对有关socket编程的接口进行了详细认识,最后结合sockaddr_in结构体和有关接口实现了三种不同场景的UDP服务端/客户端,并成功让其完成数据的接收和传输,真正意义上实现了一份基于UDP协议的socket网络通信代码。但由于时间以及内容问题,上篇博客我们只讲解了基于UDP版本的socket网络通信,所以该篇博客就让我们一起来看看如何使用socket编程实现一份TCP版本的服务端/客户端,从而实现基于TCP协议的socket网络通信代码。

理解TCP版本的套接字接口

在上篇博客中,我们对套接字常见API进行了详解介绍,但准确的来说我们介绍的大部分都是基于UDP版本下的套接字接口,所以此时对于TCP版本来说,我们需要补充几个在TCP版本下套接字编程中会被使用到的接口,其中为什么在TCP版本下需要新增不同的套接字接口,本质原因非常简单,也就是基于TCP特点带来区别,因为TCP需要满足可靠性传输,事先建立连接,面向字节流等原则。所以接下来我们就来看看TCP版本套接字编程的新增接口吧!

  • int listen(int sockfd, int backlog); 功能:将套接字设置为监听状态(服务器处于阻塞),用于监听指定端口号客户端的连接请求,并且维护一个监听队列/等待队列用于存储向该服务端发送的连接请求,最终将该监听队列提供给accept接口使用,从而实现服务端和客服端之间的连接。换一个角度理解,也就是说服务器代码在执行时会被阻塞在该处(监听),从而一直处于监听状态,只有当某客户端发送连接请求被监听到(存储在监听队列)之后,代码才会继续向后执行,从而让accept接口完成连接。第一个参数sockfd(重新理解sockfd): 在创建套接字时返回的一个套接字描述符,虽然之前我们一直称其为文件描述符,但更准确应该称为套接字描述符,同理存储在对应的文件描述符表上,是一种套接字的引用(套接字的标识符),也就是可通过该套接字描述符访问到套接字,从而可以对套接字进行管理和操作,最终让套接字实现不同的功能,完成对应的工作(接口)。第二个参数backlog:同理上述所说,该参数与监听队列有关,用于表示监听队列存储客户端连接请求的上限(也就是个数)。返回值:同理监听成功返回0,监听失败返回-1且错误码被设置。

  • int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); 功能:同理上述listen接口中所说,该接口用于与指定端口号客户端建立连接,直接从监听队列中获取客户端请求,注意:在客户端使用connect接口发送连接请求时,并不会直接将客户端的端口号和IP地址发送给服务端,而将端口号和IP地址发送给服务端的这个过程一般是系统依据TCP协议完成,所以最终accept接口再根据TCP协议对监听队列中对客户端发送的请求连接进行解包,从而获取客户端IP地址和端口号的同时,也获取到服务端的IP地址和端口号(客户端发送),从而让客户端和服务端建立连接。第一个参数sockfd:同理套接字描述符,用于对指定套接字进行accept操作,第二个参数addr:同理作为输出型参数,接收客户端的IP地址和端口号,让服务端可以显式的打印和识别客户端的IP地址和端口号,第三个参数addrlen:同理表示sockaddr_in结构体的长度,也就是大小。返回值:注意,此时这个返回值是一个新的套接字描述符,也就是说在accept接口中一定也存在socket接口的使用,具体为什么需要返回新的文件描述符,而不是像UDP版本一样使用一个套接字文件描述符就能实现网络通信,下述讲解。

  • int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen); 功能:同理上述accept接口中所说,在客户端中使用,向服务端发送连接请求,当然基于此时我们对listen和accept接口的理解,明白,此时connect接口发送的请求肯定是先被listen监听到,监听成功之后才被accept处理,尝试建立连接,并且对于connect接口来说,并不是每次一申请就能成功,一般需要多次申请,因为有很多原因会导致失败,如:监听队列满了的情况,然后同理就是该客户端向指定服务端发送请求,肯定是需要知道该服务端的IP地址和端口号,所以connect接口在发送请求时,就会把服务端的IP地址和端口号发送过去,这也就是为什么accept接口默认不仅知道客户端IP地址和端口号,还知道服务端IP地址和端口号的原因。第一个参数sockfd:同理套接字描述符,用于对指定套接字进行connect操作,第二个参数addr:同理,表示客户端需要知道向那个服务端发送连接请求,表示服务端的IP地址和端口号,第三个参数addrlen:同理表示服务端sockaddr_in的长度。返回值:同理成功返回0,失败返回-1且错误码被设置。

  • ssize_t read(int fd, void *buf, size_t count); 功能:首先明白在当初学习系统编程之文件系统相关知识时,对该接口有过一定的了解,该接口就是用来从特定的文件描述符中读取数据,同理此时对于套接字描述符而言,read接口同样可以从其读取数据,第一个参数fd:同理套接字描述符,向指定套接字中读取数据,第二个参数buffer:作为输出型参数,存储被读取数据,第三个参数count:读取数据的字节数,返回值:同理返回实际读取数据的字节数。

  • ssize_t write(int fd, const void *buf, size_t count); 功能:同理,该接口用来向特定的文件描述符中写入数据,在socket编程中也就是向套接字描述符中写入数据,第一个参数fd:同理套接字描述符,第二个参数buffer:存储需要写入数据,第三个参数count:写入数据的大小。返回值:同理写入数据的实际字节数。

行文来到此处,对于有关TCP版本新增的套接字接口我们就大致有了一定了解,但是根据上述所说,此时我们还需要解决一个问题,也就是为什么accept接口将服务端和客户端建立连接之后,需要返回一个新的套接字描述符,而不是像UDP版本一样,使用一个套接字描述符就能实现,本质也就是我想强调出TCP与UDP实现网络通信之间的区别,所以在特别区分UDP和TCP不同版本的套接字通信之前,此时我们回顾一下UDP版本下的套接字通信,第一步:同理创建套接字,获取套接字描述符,第二步:初始化sockaddr_in结构体,并将其绑定到内核套接字中,第三步:使用sendto和recvfrom这类多参数接口用于指定目标地址以及接收目标地址,最终成功实现客户端和服务端之间的数据传输。可发现在UDP版本下实现套接字通信相比于TCP需要使用listen、accept和connect等接口来说较为容易,而根据上述对接口的分析以及功能,TCP版本下的套接字通信,不仅需要对服务端套接字监听,且还需要对监听队列中的客户端连接请求建立联系,然后在建立连接之后才允许进行数据间传输,所以对于TCP套接字来说,此时就不仅仅只是像UDP一样完成数据间传输就行,重点在于它还需要对服务端和客户端建立连接,所以同理对于listen接口和accept接口来说,它们本质都需要直接作用于套接字描述符,从而让套接字具备监听和建立连接的能力,且又因为套接字还需要具备数据传输的能力,所以如果一个套接字监听到客户端请求,完成连接,进行数据传输时,此时其它客户端就不能再使用该套接字与该服务端建立连接,因为此时对于服务端来说,它唯一的套接字此时正在完成数据传输工作,所以这样就会导致服务端的效率非常低下,不能并发处理多个客户端,所以最终为了解决这一问题,此时在accpet接口完成服务端与客户端的连接之后,就需要返回一个新的套接字描述符,本质也就是向操作系统再申请一个网络通信请求并分配一定的资源(空间资源等),只有这样,才能让每个客户端在与该服务端建立连接之后都拥有一个属于自己的套接字,本质理解也就是让服务端不再像UDP版本一样,整份代码只有唯一的套接字,而是多个套接字,每与一个客户端建立连接,就有一个新的套接字供其使用,让服务端在建立连接和传输数据之间无冲突。

注意: 虽然TCP协议需要满足可靠性传输、建立连接、面向字节流等特性,但并不意味着上述接口就能实现这些特性,如TCP协议需要满足可靠性传输,而想要实现可靠性传输则需要通过一系列机制,如:确认应答、重传、流量控制和拥塞控制等… ,这些机制的实现都是体现在TCP协议当中,也就是说我们使用套接字编程实现TCP版本的网络通信,本质只需要让系统识别这是一份TCP协议版本的代码,并且编码符合TCP的这三大特性就行,本质也就是因为这些特性都是TCP协议规定好的,我们只需要遵从,这也就是为什么我们在编码时使用read和write接口作为来进行数据传输,因为其具有面向字节流的特性。同理再次强调,套接字的本质就是一种网络通信机制,socket创建套接字的本质也就是向操作系统申请进行网络通信请求,向操作系统分配资源的一个过程,并且使用各种接口对套接字进行操作,本质就是一种对网络通信功能的实现与控制而已。

正式进入TCP套接字代码编写

在正式进入代码编写前,我们需要先明白几个点,首先在上述对TCP套接字接口有了一定理解,此时我们明白TCP套接字相对于UDP套接字而言,关键就在于在数据传输前让服务端与客户端建立连接,所以我们对accept接口进行着重讲解,明白为什么其返回值是一个新的套接字描述符,但,此时我们要明白,无论是UDP版本的服务端还是TCP版本的服务端,如果我们不对其进行多执行流的控制,那么它们无论如何都不支持客户端的并发访问,所以就算是TCP套接字中accept会返回一个新的套接字描述符,它也无法同时进行数据传输和与客户端建立连接,本质就是因为单执行流必须按照顺序执行代码,明白这点之后,此时对于TCP套接字网络通信我们依然分为三个场景,单执行流服务端、父子进程服务端、多线程及线程池服务端

首先是客户端的实现

同理,客户端代码不存在特殊改动,所以此时客户端代码我们只举例一份,如下所示:
学习网络编程No.5【TCP套接字通信】,学习网络编程,网络,学习,tcp/ip

第一个场景:单执行流实现

学习网络编程No.5【TCP套接字通信】,学习网络编程,网络,学习,tcp/ip

重点强调 根据上述代码及其注解,我们需要重点明白两点,一点是在某客户端向服务端发送连接请求之前,该服务端是在listen接口处处于阻塞状态,而不是accept接口处,二点是accept接口中自带数据发送和接收功能,也就是recvfrom和sendto接口的实现,并且本质建立连接这个概念就是在内部对recvfrom和sendto接口进行封装,因为accept接口天生具有获取客户端地址信息和服务端地址信息的能力(客户端会将服务端的地址信息也传过来),从外部理解也就是在服务端与客户端之间建立一个双向通道通信,而对于返回一个新的套接字描述符的本质也就可以理解为返回accept接口中使用socket接口专门为recvfrom和sendto接口创建的那个套接字描述符。

第二个场景:父子进程实现服务端

学习网络编程No.5【TCP套接字通信】,学习网络编程,网络,学习,tcp/ip
对于使用父子进程来实现多执行流解决服务端并发访问问题,从上述代码来看非常合理,唯一的缺点就是创建进程带来的效率消耗问题,本质也就是该服务端除了会为每一个客户端创建一个新套接字描述符之外,此时还需要为每一个客户端创建一个子进程,所以这也就是为什么我们需要使用多线程方法来改善的原因。当然这部分知识下述讲到,这里我重点想要回顾一下父进程回收子进程的问题,上述代码是一种设计套路,并不是经典的我们学过的回收子进程的方法,我们学过的父进程的回收子进程的方法一共有三种,第一种是直接使用waitpid接口,但因为此时我们的目的是子进程处理客户端数据的同时,父进程同时也可以去调用accept接口,让另一个客户端和服务端建立连接,所以我们不能让父进程处于阻塞式等待,所以在使用waitpid接口时,应该采用非阻塞式等待, waitpid(id, nullptr, WNOHANG); 但最后因为需要防止父进程在执行accept接口时,监听队列中没有客户端的请求连接,而导致父进程被阻塞在accept接口处,无法执行waitpid接口回收子进程,所以这个方法不推荐在服务端上使用,第二种是使用子进程退出信号回收子进程,因为子进程退出时会向父进程发送一个退出信号SIGCHLD,所以我们只需要在父进程收到该信号时,让父进程将该信号的信号递达方式设置为忽略,此时系统自动会帮父进程回收该子进程, signal(SIGCHLD, SIG_IGN); 第三种同理是使用子进程退出信号SIGCHLD处理,在父进程收到该信号时,让父进程将该信号的默认处理方式设置为waitpid接口,实现子进程的回收,signal(SIGCHLD, handler); 明白了上述有关子进程回收相关知识,对于使用父子进程实现服务端的知识,我们就理解到这,本质没有难度,重点就在于子进程的回收而已。

第三个场景:多线程实现服务端

学习网络编程No.5【TCP套接字通信】,学习网络编程,网络,学习,tcp/ip
以上代码就是将服务端以多线程的形式实现多执行流来解决并发访问问题,本质对比父子进程实现多执行流,好处就在于不需要频繁创建进程,提高服务端效率,编码关键就在于新建线程参数控制,其它过程同理本质就是为了实现多执行流,一个负责处理客户端数据,一个负责建立连接。当然因为我们学习了线程池相关的知识,所以对于这种临时创建线程的方法,我们依然可以使用线程池的方法来改进,具体下篇博客详解,主要是因为还需要重点讲解有关日志方面的知识,所以需要留一份代码来看看日志具体如何使用。文章来源地址https://www.toymoban.com/news/detail-689261.html

总结:有关TCP版本的socket网络通信我们就讲到这啦!剩余有关线程池服务端这个终极版本,我们留到下篇博客结合日志一起学习,See you!

到了这里,关于学习网络编程No.5【TCP套接字通信】的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【网络编程】网络编程套接字(三)TCP网络程序

    与前边的UDP网络程序相同,创建套接字的接口都是socket,下边对socket接口进行介绍: 协议家族选择AF_INET,因为我们要进行网络通信。 而第二个参数,为服务类型,传入SOCK_STREAM,我们编写TCP程序,所以要选择流式的服务。 第三个参数默认传入0,由前两个参数就可以推出这是

    2024年02月16日
    浏览(62)
  • 【Linux网络】网络编程套接字(TCP)

    目录 地址转换函数 字符串IP转整数IP 整数IP转字符串IP 关于inet_ntoa 简单的单执行流TCP网络程序 TCP socket API 详解及封装TCP socket  服务端创建套接字  服务端绑定  服务端监听  服务端获取连接  服务端处理请求 客户端创建套接字 客户端连接服务器 客户端发起请求 服务器测试

    2024年03月21日
    浏览(71)
  • 【Linux】网络---->套接字编程(TCP)

    TCP的编程流程:大致可以分为五个过程,分别是准备过程、连接建立过程、获取新连接过程、消息收发过程和断开过程。 1.准备过程:服务端和客户端需要创建各自的套接字,除此之外服务端还需要绑定自己的地址信息和进行监听。注意:服务端调用listen函数后,处理监听状

    2024年02月04日
    浏览(67)
  • Linux网络编程——tcp套接字

    本章Gitee仓库:tcp套接字 客户端: 客户端: 关于构造和初始化,可以直接在构造的时候,将服务器初始化,那为什么还要写到 init 初始化函数里面呢? 构造尽量简单一点,不要做一些“有风险”的操作。 tcp 是面向连接的,通信之前要建立连接,服务器处于等待连接到来的

    2024年02月20日
    浏览(57)
  • 网络编程套接字之三【TCP】

    目录 1. ServerSocket API(给服务器端使用的类) 2. Socket API(既给服务器使用,也给客户端使用) 3. 写TCP回显—服务器 4. 使用线程池后的TCP服务器代码(最终) 5. 写回显-客户端 6. TCP回显—客户端代码 7. 运行回显服务器和客户端 TCP流套接字编程  ServerSocket 是创建TCP服务端Socket的

    2024年01月19日
    浏览(60)
  • 网络编程套接字(2)——简单的TCP网络程序

    我们将TCP服务器封装成一个类,当我们定义出一个服务器对象后需要马上对服务器进行初始化,而初始化TCP服务器要做的第一件事就是创建套接字。 TCP服务器在调用socket函数创建套接字时,参数设置如下: 协议家族选择 AF_INET ,因为我们要进行的是网络通信。 创建套接字时

    2024年02月06日
    浏览(60)
  • 【Linux网络编程】网络编程套接字(TCP服务器)

    作者:爱写代码的刚子 时间:2024.4.4 前言:本篇博客主要介绍TCP及其服务器编码 只介绍基于IPv4的socket网络编程,sockaddr_in中的成员struct in_addr sin_addr表示32位 的IP地址 但是我们通常用点分十进制的字符串表示IP地址,以下函数可以在字符串表示和in_addr表示之间转换 字符串转in

    2024年04月14日
    浏览(79)
  • TCP/IP网络编程(一) 理解网络编程和套接字

    网络编程和套接字概要 网络编程就是编写程序使两台联网的计算机相互交换数据 为了与远程计算机进行数据传输,需要连接因特网,而编程种的套接字就是用来连接该网络的工具。 构建套接字 1.调用soecket函数创建套接字 2.调用bind函数给套接字分配地址 3.调用listen函数将套

    2024年02月11日
    浏览(174)
  • 【网络通信】socket编程——TCP套接字

    TCP依旧使用代码来熟悉对应的套接字,很多接口都是在udp中使用过的 所以就不会单独把他们拿出来作为标题了,只会把第一次出现的接口作为标题 通过TCP的套接字 ,来把数据交付给对方的应用层,完成双方进程的通信 在 tcpServer.hpp 中,创建一个命名空间 yzq 用于封装 在命名

    2024年02月13日
    浏览(51)
  • JavaEE-网络编程套接字(UDP/TCP)

    下面写一个简单的UDP客户端服务器流程 思路: 对于服务器端:读取请求,并解析– 根据解析出的请求,做出响应(这里是一个回显,)–把响应写回客户端 对于客户端:从控制台读取用户输入的内容–从控制台读取用户输入的内容–从控制台读取用户输入的内容–将其显示在

    2024年02月07日
    浏览(62)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包