网络编程基础知识(学网编看它就够了)

打印 上一主题 下一主题

主题 948|帖子 948|积分 2844

目次
一.网络编程的预备知识
    1.网络通讯
    2.网络协议条理模子
    3. 常用的与网络相关的设备以及各自的作用
    4.网络数据的传输过程
    5. 互联网地址(IP地址)
    6.端口号 
    7.网络字节序 
    二. socket套接字 
    三:什么是TCP的三次握手和四次挥手?
四.基础知识总结:
五.TCP套接字编程
1. TCP套接字编程的流程
2.TCP编程的流程 
3.socket详细的API函数解析 
    (1)socket:创建一个套接字 
    (2)网络地址结构体 
    (3)IP地址转换函数 
    (4)整数在主机字节序与网络字节序之间的转换函数
    (5)bind 
           (6)listen:让套接字进入"监听模式"
    (7)accept 
    (8)connect 
    (9)往套接字上面发送数据 
    (10)从套接字上吸收数据 
    (11) 关闭套接字  close/shutdown
六.基于linux gcc编译器使用TCP/UDP协议编写服务器和客户端(使用C语言):
​编辑 源代码:TCP服务器
 TCP客户端:
 源代码:UDP服务器
UDP客户端:
编程简要:


一.网络编程的预备知识


    1.网络通讯



        通讯都应该具备两个条件:
        (1)物理前言(物理层面)
            比如:土电话,座机等等....
        (2)协议(软件条理)
            网络协议
    2.网络协议条理模子


            互联网的本质就是一系列的网络协议,处于互联网上的两台盘算机假如要实现通讯,就必要遵守这些协议,比如:网线的接口范例,
        寻址方式,数据怎样发送等一系列协议。
            简单的说,协议就是盘算机之间通过网络实现通讯事先告竣的一种"约定";这种约定使差别的厂商的设备,差别cpu以及差别操作
        系统组成的盘算机,只要遵守相同协议就可以通讯。
            协议可以分成很多种,每一种协议都明确界定了它的举动规范:2台盘算机之间支持相同的协议,并且遵循相同协议进行处理,才能
        实现通讯。
            那么这些协议根据功能的差别和分工差别分别了差别的条理,常见的有2种:
                OSI七层模子                                   TCP/IP 四层模子
            --------------------------------------------------------------------------
                 应用层
                 表示层                 ---->                  应用层
                 会话层
                 
                 传输层                 ---->                  传输层 
                 
                 网络层                 ---->                  网络层 
                 
                 数据链路层             ---->                  网络接口层
                  物理层 
                 
            OSI七层模子只是理论上的,现代网络通讯更多的是利用四层模子。
            发送方从最高层开始,从上到下按顺序传输数据,每一层吸收到上层处理的数据时,添加该层的首部并可能对数据进行处理,吸收
        放则将顺序反过来,从首层开始,将数据的内容与该层对应的首部拆开,传给上一层。
            说白了,数据在发送的时候是数据从应用层到物理层的一个打包过程,吸收时数据从物理层到应用层的一个解包的过程。
        那么各层的大要的功能和作用如下:
            (1)应用层 
                应用层是直接面向用户的程序或者服务。比如我们发送数据是先由用户历程通报给应用层,而吸收数据也是由应用层
            直接出现给用户历程
                应用层要完成用户盼望在网络上完成各种工作。因为用户可能对发送的数据有各种的要求。
                这一层可以想象成快递公司的收件员,当客户(应用历程)打电话(发送哀求)给收件员(应用层)时,收件员可以根据用户的
            差别的需求提供差别的服务(差别的协议),比如发送的时间......等等
                工作于这一层的协议接触的比较多的重要就有HTTP,FTP等
            (2)表示层 
                表示层是为了异种机通讯提供的一种公共语言,以便于进行操作,常见的协议有ASCII,SSL/TLS等
                什么叫异种机?就比如我要用linux给window发数据,两个操作系统的语法是不一致,就像安装包,exe是不能在linux下使用
            shell在window下也是不能运行的,于是就必要表示层,帮助我们解决差别操作系统之间的通讯语法题目。
                就像是通讯的双方,一个是中国人,一个是韩国人,双方是无法直接交换的,那么发送方(中国)的表示层可能会把中文翻译
            成英语,吸收方(韩国)的表示层就会把英语翻译成韩文,如许就能正常交换。
                再比如就像浏览器哀求回了一堆数据,是解析成文本还是图片,就是由表示层决定。而且数据的压缩,加密,打包等功能也是
            这层完成
            (3)会话层 
                会话层的作用就是创建或者断开和管库应用程序之间的通讯
                会话层可以把其堪称快递公司的调度员。它管理着快递的相关信息,就比如快递何时发送已往,必要多少时间...
                它也要做同步管理,就比如我把东西运已往了,但是那边没人吸收,这种情况就是没有同步
            (4)传输层 
                传输层起着可靠传输的作用,确保数据被可靠的传送到目标地址。
                此层具有两个代表性的协议:TCP  UDP
                4.1 TCP: Transport Control Protocol 传输层控制协议
                    它是一种面向连接的传输层协议,它能提供可靠的通讯
                    就是说TCP协议是在确定对方能通讯的条件下才会传输数据,假如对方不能通讯,那么发送方不停再发送哀求包,直到对方
                回应,双方创建连接,开始传输数据。
                    但是传输的过程中,有可能数据会丢失,一旦丢失,TCP也会继续重发。就比如我发了1000个包,另外一台电脑就要告诉我
                是否吸收了这1000个包,假如丢了一些包,你也要告诉我是丢了哪些包,我会把丢的包重新发送一遍,如许的数据的完整率就会
                大大提高。
                    以是,TCP协议是数据无误,数据无丢失,数据无失序,数据无重复到达的通讯协议,因此TCP协议一般用于发送大量的比较
                重要的数据。
                4.2 UDP: User Datagram Protocol 用户数据报协议
                    它是不可靠的无连接的协议。在数据发送之前,因为不必要创建连接,以是数据传输效率就会高
                    那么UDP和TCP就不太一样了,UDP它不会去确认目标是否能够通讯,它只管往外发送数据,至于你能不能收到,有没有丢包一概
                不管。
                    以是UDP一般用于传输少量数据
                这两种协议各有各的利益,TCP更可靠稳固,而UDP虽然没有那么可靠但是效率高,以是目前应用比较多的就是UDP。
            (5)网络层 
                网络层负责将数据传输到目标地址,这一层重要负责寻址和路由选择。重要由IP,ICMP两个协议组成
                网络层将数据从主机A发送到主机B,那么我们发送已往的路径可能有很多种,比如数据从A出发,经过C,D再到B,也可以
            从A出发,经过X,Y再到B,也就是说,通讯双方之间的数据链路可能非常复杂。那么网络层就是负责找出一条相对更加高效的
            路径将数据通报已往。传输的地址使用的是IP地址。IP地址跟我们住址有点类似,从大范围缩小到小范围。IP地址也有如许的
            能力,通过不断的转发到更近的IP地址,最终到达目标地址,怎么选择就是网络层的事
            (6)数据链路层 
                互联设备之间传送和识别的数据帧
                有的买家在北京,有的买家在上海,以是要将仓库的货物分类封装。在数据链路层种,必要将比特流组装成字节的组合。
            类似把地址相同的货物放到同一个集装箱中,只要这个集装箱到达目标地,货物自然也就到了,我们把这种比特流组成字节
            的组合我们称之为数据帧
                对数据进行处理封装成数据帧并通报和错误检测的层就是数据链路层
            (7)物理层 
                它的重要作用就是通过光信号和电信号传输数据,物理层就是由实物所承载的,如将货物运送到目标地的交通工具属于
                物理层 
                比如:网线,网卡(每一个网卡在出厂的时候就会设定一个世界上唯一的MAC地址,一个48bits的数据)
                举一个完整的例子理解一下七层模子:
                
                应用层     经理A     写好信件                阅读信件         经理B
                
                表示层     翻译     将信件翻译成通用语言     将信件翻译
                                    假如有必要的可能还有     提示经理阅读信件
                                    加密和压缩
                
                会话层     秘书     找出收件人的地址          打开信封
                
                传输层     司机     将信封带到邮局            将信封带回公司
        --------------------------------------------------------------------------------------------------
                网络层    分拣员    将信件按地址(地域)分开    处理包裹信息,让指定的人取件
                
              数据链路层  包装工人  将同一个地域的信封打包    拆开来自各地的包裹
              
                物理层     运输车      将信件运输已往          吸收信件
                
                
                一个包裹被经理写好,然后交给翻译将包裹的内容翻译成通用语言,接着翻译将被翻译的包裹给秘书,秘书在包裹上写好收件人的地址后
                递给司机,司机将包裹送至邮局,邮局内里的分拣员未来自世界各地的包裹分类,把差别地域的包裹错开,接着包装工人将差别地域的包裹打包,
                放进运输车送往世界对应的地址。
                内里的经历,翻译,秘书,司机,分拣员,包装工人,运输车。
                分别代表:应用层,表示层,会话层,传输层,网络层,数据链路层,物理层在网络传输中扮演的角色。
                
                写好信        翻译交给经理
                翻译        秘书给翻译
                写好地址    司机给秘书
                传给网络    司机去拿货
                地域分拣    检察地址后关照司机
                打包信件    拆开包装
                传输信件    收到信件
            大致可以这么理解
            

    3. 常用的与网络相关的设备以及各自的作用



            我们知道假如两台电脑通过网线直连的话肯定是可以通讯,此时这两台电脑就构成了一个小的局域网,局域网内只有两台设备。
        但是这种应用场景非常少见,更常见的就是多台设备之间互联,也就是说一个局域网内有多个通讯设备。但是题目是。一个盘算机
        只有一个网口,怎么让多台盘算机互联?此时就必要用到交换机(switch)了。
            交换机:可以提供大量的网络接口将多台网络设备连接成一个局域网。交换机内部拥有一根总线,交换机所有的接口都是挂载
        这根总线上,意味着这些设备在物理层面都是互通的。
            连接上同一个交换机上的网络设备假如要进行通讯的话,可以使用一种广播的方式进行。
            路由器:路由器的功能就是将差别的子网之间的数据进行通报,这个过程称之为寻址过程。
            就比如快递公司发送邮件。邮件并不是刹时到达目标地,而是通过差别分站的分拣,不断的接近最终地址。路由器寻址过程也是类似
        的原理。通过最终地址,在路由表中进行匹配,通过算法去分析确定下一站的转发地址。这个地址可能是中心地址。
            调制解调器(猫):是调制器和解调器的缩写,是一种硬件。我们网络信号的通报大部门都是光/电(模拟)信号,而盘算机只能识别数字
        信号(1/0),以是盘算机从网络上吸收到这些信号之后,必要将其翻译成盘算性能够看懂的数字信号,有点类似A/D,D/A转换过程
            

    4.网络数据的传输过程



        看图示的封包和拆包的过程
        留意:既然我们网卡都有一个唯一的MAC地址,或者说通过MAC地址就能找到另外一张网卡。那么有MAC地址不就可以实现了网络中
            通讯了,为什么还必要IP地址?
            我们知道IP地址是网络层负责,网络接口层中负责MAC地址。而我们的以太网协议实在就是依赖MAC发送数据的 
            理论上,单单依赖MAC地址,北京的网卡就可以找到上海的网卡,但是有一个重大的缺点。以太网协议就会以广播的方式发送
        数据包,也就是处于同一个局域网内的设备都能收到,如许做,效率较低,而且广播方式之范围于发送者所在的子网,也就是说
        只要通讯双方不在同一个子网,广播不能通报出去的。这种计划是不公道的,否则互联网上每一台盘算机都会收到数据包,那么就会
        造成网络拥堵设置网络瘫痪。
            因为,必须找到一种方法,去区分哪些MAC地址是属于同一个子网络,哪些不是。假如是同一个子网络,就会采用广播的方式
        假如不是,就采用"路由选择"
            这才导致了"网络层"的诞生,它的作用就是引进一套新的地址,使得我们去区分差别得盘算机是否属于同一个子网。这套地址
        称之为"网络地址",简称"网址"
            于是在出现"网络层"之后,每台盘算机有了两种地址,一种MAC地址,另外一种是网络地址。MAC地址是由硬件(网卡)决定得,
        一般不能改动,网络地址则是由软件决定得,是可以改动的。
            以是网络地址就是帮助我们找到盘算机所在的子网络,MAC地址帮助我们找到自网络的目标网卡。
            假如只有IP地址而没有MAC地址的话,就相称于根据IP找到子网,但是子网可能有很多主机,不能确定要找的是谁
            

    5. 互联网地址(IP地址)



        IP地址:Internet Protocol Address的缩写 "网际协议地址"
        常见的IP地址分成两类:
            IPv4 --> 32bits         IPv6 -->128bits 
            目前应用最多还是IPv4,IPv6开始在教诲网中使用。
            理论上IPv4能够提供2^32个IP,而IPv6能够提供2^128个IP。
            IPv4地址有32bits,那么这32bits有什么寄义?
            一般把一个ipv4地址分成两部门:
                像一样平常生活中的电话号码一样:
                    区号 + 主机号  --> 0731 82551678 
                IPv4 32bits:
                    网段号:用来标识哪一个网段
                        比如处于同一个路由器下的主机的IP的网段,就是一样
                        在IP地址的一连的高位上
                    主机号:用来标识子网中差别的主机
                        处于同一个子网内的主机的IP地址的主机号肯定不一样
                        在IP地址的一连的低位上 
                    如: 192.168.31.8(IP地址用点分十进制标识)
                        每8bits一个数字,中心用.隔开
                网段号和主机号各占多少位?根据差别地址范例来决定
                但是总的来说:网段号+主机号就是32bits
        IP地址分类: 
            类别           分段                             IP地址范围                 私有地址范围
             A    0网段号(7bits)+主机号(24bits)      0.0.0.0 - 127.255.255.255      10.0.0.0 - 10.255.255.255
             B    10网段号(14bits)+主机号(16bits)    128.0.0.0 - 191.255.255.255    172.16.0.0 - 172.31.255.255
             C    110网段号(21bits)+主机号(8bits)    192.0.0.0 - 223.255.255.255    192.168.0.0 - 192.168.255.255 
             D    1110多播组号(28bits)               224.0.0.0 - 239.255.255.255 
             E    11110(留着以后用)                  240.0.0.0 - 247.255.255.255 
        在设置IP地址的时候,一般必要设置另外一个称之为子网掩码的东西。
            NETMASK子网掩码:
            就是用来指定一个IP地址中,哪些bits是网段号,哪些bits是主机号
            简单的说,netmask为1的就是网段号,为0的就是主机号
                netmask: 255.255.255.0
                -->那么就意味着IP中的高三字节为网段号,低一字节为主机号。
                那么我们可以通过这个判断两个IP是否属于同一个网段
                    如: 
                        192.168.1.4和192.168.3.5就不是同一个网段
                并不是所有的掩码都是合法的。
                如: 
                    255.255.114.0
                    --->  1111 1111 1111 1111 0111 0010 0000 0000 //不合法
                    255.255.192.0 
                    --->  1111 1111 1111 1111 1100 0000 0000 0000 //合法 
                也就是说1和0必须是一连的
                
        我们知道一台盘算机可以拥有一个独立的IP地址,一个局域网也可以有一个独立的地址(对外就好像只有一台盘算机),对于目前
    广泛使用IPv4地址,它的资源肯定是有限的,一台盘算机一个IP是不实际的,往往是一个局域网才拥有一个IP。
        在网络上通讯时,必须知道对方的IP地址。实际上数据包中已经附带了IP地址,把数据包发送到路由器后,路由器会根据IP地址找到
    对方的位置,完成一次数据的通报。
        那么一个局域网才能拥有一个独立IP,换句话说,IP地址只能定位到一个局域网,无法定法到一台盘算机 -->再依赖MAC地址,而且
    MAC地址已经被写死在网卡(某些牛逼的人通过一些技术也是可以修改)。局域网中的路由器会记录每台盘算机的MAC地址
        有了IP地址和MAC地址,虽然能够找目标盘算机,但是仍然不能通讯。盘算性能够收取数据包,但是它并不知道要给哪个网络程序
    来处理,以是通讯失败。
        为了区分差别的网络程序,盘算机会分每一个网旅程序分配一个独一无二的端口号(Port Number)。
    

    6.端口号 



        TCP和UDP采用16bits的端口号用来标识网络程序
        以是网络程序从传输层额角度来看:
            TCP应用
            UDP应用
        TCP端口号和UDP的端口号是独立的。
        也就是说TCP端口号20和UDP端口号20是两个差别的端口。
        以是一台主机上的网络应用程序的地址:
            MAC地址 + IP地址 + 传输层协议(TCP/UDP) + 端口号  
        端口号并不是你想指定什么就什么,而是由IANA机构管理
            比如:ftp服务器  -->21端口
                  http服务器 -->80端口
                  .....
            公共端口 0-1023 是已经被分配的 
    

    7.网络字节序 



        网络上传输数据都是字节流,对于一个多字节数值,在进行网络传输的时候,先通报哪个字节?也就是说,当吸收端收到第一个字节
    的时候,它是把这个字节当成高字节还是低字节处理,必须探究
        UDP/TCP/IP协议规定:把吸收到的第一个字节看成高字节看待,要求发送发送的第一个字节必须是高位字节;而发送端发送数据的时候
    第一个字节应该是该数值在内存中的起始地址对应的谁人字节,也就是说第一个字节(高字节)存放在低地址--->大端存放
        以是说,网络字节序是大端字节序
        

    二. socket套接字 



        Socket是一个编程接口,作用就是用来实现网络上差别主机的应用程序进行双向通讯。
        套接字当成一种特别的文件形貌符,也就意味着我们使用套接字实现网络通讯可以使用read/write。比如:客户端可以用write
    发送网络数据,服务端可以使用read吸收数据
        要通过互联网进行通讯,至少必要一对套接字,其中一个运行在客户端,称之为Client socket,另外一个运行在服务端,称之为
    server socket。
        socket可以分成三种范例:
            (1)流式套接字(SOCK_STREAM)
                流式套接字用于提供面向连接,可靠的数据传输服务
                重要针对于传输层协议为TCP协议的应用
                假如我们写代码利用TCP来传输数据,必要一个流式套接字
            (2)数据报套接字(SOCK_DGRAM)
                数据报套接字提供一种无连接的服务(不能包管数据传输的可靠性)
                重要针对于传输层协议为UDP协议的应用
            (3)原始套接字(SOCK_RAW)
                原始套接字可以直接跳过传输层读取没有处理的IP数据包。因此,假如要访问其他协议发送的数据必须使用原始套接字
    

    三:什么是TCP的三次握手和四次挥手?



    TCP(传输控制协议)是一种可靠的传输协议,用于在盘算机网络上创建可靠的连接。在创建和制止TCP连接时,使用了三次握手和四次挥手的机制。
    三次握手(Three-Way Handshake):
    1. 第一次握手:客户端发送一个序列号为seq(假设是1000)的带有 SYN(创建连接)标志的TCP包,指示客户端哀求创建连接。
    2. 第二次握手:服务器吸收到客户端的哀求后,发送序列号(假设为2000)的一个带有 SYN/ACK(创建连接/确认收到)标志的TCP包,表示同意创建连接。
    3. 第三次握手:客户端收到服务器的确认后,再次发送一个带有 ACK(确认收到)标志的TCP包,表示连接已经创建。
    其中确认包Ack是对方的TCP包的序号报+1
    其中一共涉及3个包的:seq,SYN,ACK
    也就是说ACK = seq(对方发来的TCP包序列号) + 1;
    通过这个三次握手过程,客户端和服务器都确认了彼此的能力和意愿,以创建可靠的连接。

    四次挥手(Four-Way Handshake):
    1. 第一次挥手:当客户端想要制止连接时,发送一个带有 FIN(结束)标志的TCP包,表示不再发送数据。
    2. 第二次挥手:服务器收到客户端的结束关照后,发送一个带有 ACK(确认)标志的TCP包,表示已经收到哀求,并预备关闭连接。
    3. 第三次挥手:服务器结束发送数据后,发送一个带有 FIN(结束)标志的TCP包,表示服务器不再发送数据。
    4. 第四次挥手:客户端吸收到服务器的结束关照后,发送一个带有 ACK(确认)标志的TCP包,表示已经吸收到服务器的哀求,并且同意关闭连接。
通过这个四次挥手过程,客户端和服务器都确认了双方不再发送数据,关闭了连接。
三次握手和四次挥手过程都是为了确保连接的可靠性和一致性,以及在创建和制止连接时的双向确认。


四.基础知识总结:


网络编程是指通过盘算机网络实现数据传输和通讯的编程方式。
在网络编程中,开发人员使用编程技术来创建网络连接、发送和吸收数据,并进行网络通讯。
网络编程可以涉及多个条理和协议,包罗以下方面:
1. Socket 编程:Socket 是网络编程中的一种编程接口,它提供了一种机制,使得差别盘算机上的应用程序能够通过网络进行通讯。
Socket 编程通过创建套接字(socket)对象,使用差别的网络协议(如 TCP 或 UDP)来创建连接、发送和吸收数据。
2. 网络协议:网络编程必要了解和使用各种网络协议,如 TCP/IP、HTTP、FTP、SMTP 等。
开发人员必要理解这些协议的工作原理和规范,以便精确地构建网络应用程序。
3. 客户端-服务器模子:网络编程通常使用客户端-服务器模子,其中客户端应用程序通过网络连接到服务器应用程序,发送哀求并吸收相应。
客户端发送哀求,服务器吸收并处理哀求,然后发送相应给客户端。
4. 数据传输和序列化:在网络编程中,数据必要在客户端和服务器之间传输。
数据可能以差别的格式进行序列化,如 JSON、XML 等。
开发人员必要处理数据的序列化和反序列化,以便在网络上进行精确的数据传输。
5. 网络安全和身份验证:网络编程也涉及网络安全和身份验证,以确保数据的安全性和保密性。
开发人员必要使用加密、数字证书、身份验证等技术来保护网络通讯的安全性。
网络编程使得盘算机之间可以高效地通讯和协作,扩展了应用程序的能力和范围。

五.TCP套接字编程


1. TCP套接字编程的流程


    (1)TCP网络应用大概的流程过程
        创建连接(相称于微信加好友)
            三次握手
        发送/吸收网络数据(聊天):
            write/send/sendto
            read/recv/recvfrom
        关闭连接:(聊完删除好友)
            四次挥手
            
            创建连接成功
        为什么必要三次握手这种机制?
            通讯双方能够成功通讯的条件条件是是双方创建好连接。
            那么双方能够连接到一起的条件是什么?
            必须双方都能收和发。
        以是三次握手实际上就是一个测试能不能成功创建连接的过程
            第一次握手实际上测试了客户端能不能发,第二次握手实际上测试服务端能不能收和发,第三次握手测试客户端   能不能收。
    三次握手的详细过程:
        先了解一下TCP数据包结构:
            (1)序号:Seq占32位,用来标识从盘算机A发送到盘算机B的数据包的编号
            (2)确认号:Ack确认号占32位,客户端和服务器都可以发送,Ack = Seq + 1。
            (3)标志位:每个标志位占1bit,共有6个,分别为URG,ACK,PSH,RST,SYN,FIN
                ACK:确认序号有效
                RST:重置连接
                SYN:创建一个新的连接
                FIN:断开一个连接
            使用connect创建连接时,客户端和服务器端互相发送这三个数据包,如图所示:
        总结图示的过程:
            (1)当客户端调用connect函数后,TCP协议就会组装一个数据报,并设置SYN标志位,表示这个数据包就是用来
        创建连接的,同时会生成一个序号添补进"序号Seq(1000)",将这个数据包发送给服务器。客户端就进入了SYN-SENT状态。
            (2)服务器收到这个数据包之后,检测SYN标志位,就知道这个是某个客户端发来用来创建连接的"哀求包"。
        服务器也会组装一个数据包,并设置了SYN和ACK,生成一个序号Seq(2000)。服务器会将前面吸收的数据包的序号+1
        填进确认号Ack(Seq+1:1001)内里,将这个包发送给客户端,进入SYN-RCVD状态。
            (3)客户端收到数据包之后,检测到已经设置了SYN和ACK标志位,就知道这个是服务器发来的"确认包",客户端会
        检测这个包的确认号Ack的值是否是1001,假如是说明回应成功。
            接下来,组装数据包,设置ACK标志位,表示客户端精确吸收了服务器发来的"数据包",同时将前面的包的Seq+1(2001)
        添补到这个包的确认号Ack,客户端将这个数据包发送出去之后,进入ESTABLISHED状态
            (4)服务器收到数据包之后,检测ACK标志位,检查这个包的确认号是否是2001,假如是表示连接创建成功
        此时进入ESTABLISHED状态
        当客户端和服务器都进入ESTABLISHED状态,连接创建成功,可以互相收发数据了。
        
    四次挥手:
        创建连接黑白常重要的,他是数据精确传输的条件,断开连接也是,它让盘算机释放不再使用的资源

            
    总结: 
        (1)客户端调用close函数之后,向服务器发送一个FIN数据包,进入FIN_WAIT_1状态
        (2)服务器收到FIN数据包之后,检测到了FIN标志位,于是向客户端发送一个ACK数据包,进入CLOSE_WAIT状态
            留意:服务器收到哀求后并不是立刻断开连接,而是先向客户端回应
        (3)客户端收到ACK包进入了FIN_WAIT_2状态,等待服务器发送最后的FIN包
        (4)服务器预备完毕之后,可以断开连接,于是主动向客户端发送FIN包,进入LAST_ACK状态
        (5)客户端收到服务器的FIN包之后,再次向服务器发送ACK包,进入TIME_WAIT状态。
        (6)服务器收到客户端的ACK包之后,断开连接,关闭套接字,进入CLOSED状态

2.TCP编程的流程 


    图示
    TCP server:
        socket():创建一个套接字
        bind():把一个套接字与网络地址相绑定
            假如你想让其他人主动来找你,你就必要绑定一个地址
            不调用bind,并不代表你的socket就没有地址,假如不绑定,内核会自动帮你指定一个地址
        listen():"监听模式"
        accept():吸收客户端连接哀求
            多次调用accept可以与差别的客户端创建连接
        read/recv/recvfrom  or write/send/sendto
        close/shutdown:四次挥手 
    
    TCP Client:
        socket()
        bind():可要可不要
        connect:发起连接哀求
        read/recv/recvfrom  or write/send/sendto
        close/shutdown

3.socket详细的API函数解析 



    (1)socket:创建一个套接字 



        NAME
       socket - create an endpoint for communication
        SYNOPSIS
               #include <sys/types.h>          /* See NOTES */
               #include <sys/socket.h>
         int socket(int domain, int type, int protocol);
            @domain:指定域或者协议族
                socket接口不仅仅范围于TCP/IP,它还有bluetooth,当地通讯....
                每一种通讯下面都有很多的协议,大概协议如下:
                    AF_INET        IPv4协议族
                    AF_INET6       IPv6协议族
                    AF_UNIX, AF_LOCAL     Unix域协议族  -->IPC手段
            @type:指定套接字的范例
                SOCK_STREAM:流式套接字 -->TCP 
                SOCK_DGRAM: 数据报套接字 -->UDP
                SOCK_RAW:原始套接字
            @protocol:指定应用层协议,可以指定为0(不知名的私有应用协议)
        返回值:
            成功的话返回一个套接字文件形貌符(>2)
            失败返回-1,同时errno被设置

    (2)网络地址结构体 


        socket接口它不仅仅用于IPv4,也可以用于IPv6.....,那么差别的协议的地址是不一样的。
        但是我们在编程的时候,必须指定网络地址,以是必要对地址进行一个统一(标准化)
        用如许的两个结构体来形貌网络通讯的地址:
        通用网络地址结构体:
        struct sockaddr 
        {
           sa_family_t sa_family;//指定协议族
           
           char        sa_data[14];//包含套接字中目标地址和端口信息
        };
        这个结构体的缺陷是把IP地址和端口地址混在一起保存在sa_data中。
        
        IPv4专用地址结构体: 
          struct sockaddr_in
          {
            sa_family_t    sin_family;    //指定协议族 AF_INET 
            in_port_t sin_port;        //指定端口号 1024-65535
            struct in_addr sin_addr;//指定IP地址 怎么赋值呢?IP地址转换。
            unsigned char sin_zero[8];//添补8个字节 无实际寄义 只是为了大小和其他协议族地址结构体一样
          };
          struct in_addr 
          {
            in_addrt_t s_addr;  //in_addrt_t  uint32_t  unsigned int 
          }
                
        这个结构体把IP地址和端口号分开保存了
    sockaddr常用于bind,connect,recvfrom,sendto等函数的参数。    
        

    (3)IP地址转换函数 



        我们知道IP地址是以点分十进制的形式存在的,实际上转换成一个无符号的32bit的数据
    SYNOPSIS
       #include <sys/socket.h>
       #include <netinet/in.h>
       #include <arpa/inet.h>
            将点分十进制的字符串转换为IP
       3.1 int inet_aton(const char *cp, struct in_addr *inp);
            a:addr  点分十进制的字符串
            n:network  网络地址 
            @cp:指向要转换的点分十进制的字符串  -->"192.168.31.8"
            @inp:指向一个IP结构体 struct in_addr
                    用来保存转换后的IP的地址
        返回值: 
            成功返回0,失败返回-1,同时errno被设置
        
        3.2
            in_addr_t inet_addr(const char *cp);
                将cp指向的"点分十进制的字符串"转换为IP网络地址
                
        3.3        
            in_addr_t inet_network(const char *cp);
                与inet_addr函数类似
        3.4 
            char *inet_ntoa(struct in_addr in);
                把网络地址IP转换成点分十进制
       
    例子: 
        struct sockaddr_in Serv;
        Serv.sin_family = AF_INET;
        Serv.sin_port = 1111;//不可行
        Serv.sin_addr.s_addr = inet_addr("192.168.31.8");
        //inet_aton("192.168.31.8", &Serv.sin_addr);
        

    (4)整数在主机字节序与网络字节序之间的转换函数



        NAME
       htonl, htons, ntohl, ntohs - convert values between host and network byte order
        SYNOPSIS
               #include <arpa/inet.h>
               uint32_t htonl(uint32_t hostlong);
               uint16_t htons(uint16_t hostshort);
               uint32_t ntohl(uint32_t netlong);
               uint16_t ntohs(uint16_t netshort);
            h:host      n:network 
            l:long 32bits 
            s:short  16bits 
        htonl:把一个32bits的整数的主机字节序转换成网络字节序
        htons:把一个16bits的整数的主机字节序转换成网络字节序
        ntohl:把一个32bits的整数的网络字节序转换成主机字节序
        ntohs:把一个16bits的整数的网络字节序转换成主机字节序
       Serv.sin_port = htons(1111);//可行

    (5)bind 



        NAME
       bind - bind a name to a socket
        把一个IPv4的网络地址绑定到一个socket上面去
    SYNOPSIS
           #include <sys/types.h>          /* See NOTES */
           #include <sys/socket.h>
        int bind(int sockfd, const struct sockaddr *addr,
                    socklen_t addrlen);
            @sockfd:要绑定地址的套接字形貌符
            @addr:通用的网络地址结构体的指针
            @addrlen:第二个参数指向的地址结构体的长度
    返回值: 
        成功返回0,失败返回-1 

        
   (6)listen:让套接字进入"监听模式"



        NAME
       listen - listen for connections on a socket
        SYNOPSIS
               #include <sys/types.h>          /* See NOTES */
               #include <sys/socket.h>
        int listen(int sockfd, int backlog);
            @sockfd:要进入监听模式的套接字
            @backlog:同时能够处理连接哀求的数量
                如: 5,10.....
        返回值: 
            成功返回0,失败返回-1,同时errno被设置


    (7)accept 



        用于TCP server吸收来自一个客户端的连接哀求 
        NAME
       accept - accept a connection on a socket
    SYNOPSIS
       #include <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>
       int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
            @sockfd:套接字 
            @addr:网络地址结构体的指针,用来保存客户端的地址信息的
            @addrlen:用来保存客户端地址结构体的长度
        返回值: 
            成功的返回与该客户端的连接套接字形貌符,后续与该客户端通光荣的都是这个套接字形貌符
        失败返回-1,同时errno被设置
        

    (8)connect 



        重要用于客户端主动向服务器发起连接哀求
        NAME
       connect - initiate a connection on a socket
        SYNOPSIS
               #include <sys/types.h>          /* See NOTES */
               #include <sys/socket.h>
        int connect(int sockfd, const struct sockaddr *addr,
                           socklen_t addrlen);
                @sockfd:套接字
                @addr:对方的地址,服务器的地址
                @addrlen:对方的地址结构体的长度
        返回值: 
            成功返回0,失败返回-1
    

    (9)往套接字上面发送数据 



        write/send/sendto这三个函数。TCP都可以用,UDP只能sendto
            NAME
       send, sendto - send a message on a socket
        SYNOPSIS
               #include <sys/types.h>
               #include <sys/socket.h>
       9.1  ssize_t send(int sockfd, const void *buf, size_t len, int flags);
            @sockfd:套接字
            @buf:保存你即将要发送的数据
            @len:你想发送的数据的长度
            @flags:一般为0 
        返回值: 
            成功返回实际发送的字节数,失败返回-1,同时errno被设置
                
       9.2  ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
                      const struct sockaddr *dest_addr, socklen_t addrlen);
                @dest_addr:指定吸收方的地址。
                    假如是TCP通讯,此处可以省略,因为TCP是面向连接的,意味着在数据发送之前就已经创建连接了。
                    假如是UDP通讯,必要指定
                @addrlen:对方地址的长度
                

    (10)从套接字上吸收数据 



        read/recv/recvfrom
        NAME
       recv, recvfrom - receive a message from a socket
        SYNOPSIS
               #include <sys/types.h>
               #include <sys/socket.h>
       10.1 ssize_t recv(int sockfd, void *buf, size_t len, int flags);
            recv的参数与read类似,省略
       10.2 ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                        struct sockaddr *src_addr, socklen_t *addrlen);
                @src_addr:用来保存发送方的地址 
                    TCP可以不用管,因为TCP肯定知道是谁发的,填NULL
                    UDP,也可以填NULL,表示不关系是谁发,也不会影响别人给你发数据
                @addrlen:用来保存发送者地址的实际的长度
        返回值:返回实际收到的数据的字节数,失败返回-1,同时errno被设置
        

    (11) 关闭套接字  close/shutdown



        NAME
       shutdown - shut down part of a full-duplex connection
    SYNOPSIS
           #include <sys/socket.h>
    int shutdown(int sockfd, int how);
        @sockfd:套接字
        @how:关闭的方式,有三种
            SHUT_RD:关闭读
            SHUT_WR:关闭写
            SHUT_RDWR:关闭读写 -->close(sockfd);
    返回值: 
        成功返回0,失败返回-1,同时errno被设置
 
六.基于linux gcc编译器使用TCP/UDP协议编写服务器和客户端(使用C语言):


 源代码:TCP服务器


  1. #include <stdio.h>
  2. #include <sys/types.h>
  3. #include <sys/socket.h>
  4. #include <string.h>
  5. #include <unistd.h>
  6. #include <stdlib.h>
  7. #include <arpa/inet.h>
  8. #include <netinet/in.h>
  9. int main(int argc,char *argv[])
  10. {
  11.         if(argc != 3)
  12.         {
  13.                 printf("Usage:%s <IP> <PORT>\n",argv[0]);
  14.                 return -1;
  15.         }
  16.         //1.创建套接字
  17.         int sockfd = socket(AF_INET,SOCK_STREAM,0);
  18.         if(sockfd == -1)
  19.         {
  20.                 perror("socket failed!!");
  21.                 return -1;
  22.         }
  23.         //2.绑定服务器的IP和PORT
  24.         struct sockaddr_in Serv;
  25.         Serv.sin_family = AF_INET;
  26.         Serv.sin_port=htons(atoi(argv[2]));
  27.         Serv.sin_addr.s_addr = inet_addr(argv[1]);
  28.        
  29.         int ret = bind(sockfd,(struct sockaddr *)&Serv,sizeof(Serv));
  30.         if(ret == -1)
  31.         {
  32.                 perror("bind failed!!");
  33.                 close(sockfd);
  34.                 return -1;
  35.         }
  36.         //3.监听模式
  37.         ret = listen(sockfd,10);
  38.         if(ret == -1)
  39.         {
  40.                 perror("listen failed!!");
  41.                 close(sockfd);
  42.                 return -1;
  43.         }
  44.         while(1)
  45.         {
  46.                 struct sockaddr_in clie;
  47.                 socklen_t len = sizeof(clie);
  48.                 //4.阻塞等待连接
  49.                 int confd = accept(sockfd,(struct sockaddr *)&clie,&len);
  50.                 if(confd == -1)
  51.                 {
  52.                         perror("confd failed!!");
  53.                         continue;
  54.                 }
  55.                 printf("connect [%s][port:%d] sucess!!!!!\n",inet_ntoa(clie.sin_addr),ntohs(clie.sin_port));
  56.                 //创建一个新的进程与客户端通信
  57.                 pid_t pid = fork();
  58.                 if(pid >0)
  59.                 {
  60.                         close(confd);
  61.                 }
  62.                 else if(pid == 0)//子进程
  63.                 {
  64.                         while(1)
  65.                         {
  66.                                 char recv_buf[1024]={0};       
  67.                                 int ret = recv(confd,recv_buf,1023,0);
  68.                                 if(ret > 0)
  69.                                 {
  70.                                         printf("recv data[%s]:%s\n",inet_ntoa(clie.sin_addr),recv_buf);
  71.                                         if(strncmp(recv_buf,"byebye",6) == 0)//接收到了退出指令
  72.                                         {
  73.                                                 printf("connect [%s][port:%d] sucess exit!!\n",inet_ntoa(clie.sin_addr),ntohs(clie.sin_port));
  74.                                                 close(confd);
  75.                                                 exit(0);
  76.                                         }
  77.                                         char *buf = "OK!!\n";
  78.                                         send(confd,buf,strlen(buf),0);
  79.                                 }
  80.                         }
  81.                        
  82.                 }
  83.                 else
  84.                 {
  85.                         close(confd);
  86.                         perror("fork failed");
  87.                         continue;
  88.                 }
  89.                
  90.         }
  91. }
复制代码
 TCP客户端:

  1. #include <stdio.h>
  2. #include <sys/types.h>
  3. #include <sys/socket.h>
  4. #include <string.h>
  5. #include <unistd.h>
  6. #include <stdlib.h>
  7. #include <arpa/inet.h>
  8. #include <netinet/in.h>
  9. int main(int argc,char *argv[])
  10. {
  11.         if(argc != 3)
  12.         {
  13.                 printf("Usage:%s <IP> <PORT>\n",argv[0]);
  14.                 return -1;
  15.         }
  16.         //1.创建套接字
  17.         int sockfd = socket(AF_INET,SOCK_STREAM,0);
  18.         if(sockfd == -1)
  19.         {
  20.                 perror("socket failed!!");
  21.                 return -1;
  22.         }
  23.         //2.指定服务器的地址
  24.         struct sockaddr_in Serv;
  25.         Serv.sin_family = AF_INET;
  26.         Serv.sin_port=htons(atoi(argv[2]));
  27.         Serv.sin_addr.s_addr = inet_addr(argv[1]);
  28.         //3.连接服务器
  29.         int ret = connect(sockfd,(struct sockaddr *)&Serv,sizeof(Serv));
  30.         if(ret == -1)
  31.         {
  32.                 perror("connect failed!!");
  33.                 return -1;
  34.         }
  35.         while(1)
  36.         {
  37.                 char send_buf[1024]={0};
  38.                 fgets(send_buf,1023,stdin);
  39.                 send(sockfd,send_buf,strlen(send_buf) - 1,0);
  40.                 if(strncmp(send_buf,"byebye",6) == 0)
  41.                 {
  42.                         break;
  43.                 }
  44.                 char buf[1024]={0};
  45.                 recv(sockfd,buf,1023,0);
  46.                 printf("recv data[%s]:%s\n",argv[1],buf);
  47.         }
  48.         //4.关闭套接字
  49.         shutdown(sockfd,SHUT_RDWR);
  50.         return 0;
  51. }
复制代码
 源代码:UDP服务器

  1. #include <stdio.h>
  2. #include <sys/types.h>
  3. #include <sys/socket.h>
  4. #include <string.h>
  5. #include <unistd.h>
  6. #include <stdlib.h>
  7. #include <arpa/inet.h>
  8. #include <netinet/in.h>
  9. int main(int argc,char *argv[])
  10. {
  11.         if(argc != 3)
  12.         {
  13.                 printf("Usage:%s <IP> <PORT>\n",argv[0]);
  14.                 return -1;
  15.         }
  16.         //1.创建套接字
  17.         int sockfd = socket(AF_INET,SOCK_DGRAM,0);
  18.         if(sockfd == -1)
  19.         {
  20.                 perror("socket failed!!");
  21.                 return -1;
  22.         }
  23.         //2.指定服务器的地址
  24.         struct sockaddr_in Serv;
  25.         Serv.sin_family = AF_INET;
  26.         Serv.sin_port=htons(atoi(argv[2]));
  27.         Serv.sin_addr.s_addr = inet_addr(argv[1]);
  28.        
  29.         int ret = bind(sockfd,(struct sockaddr *)&Serv,sizeof(Serv));
  30.         if(ret == -1)
  31.         {
  32.                 perror("bind failed!!");
  33.                 close(sockfd);
  34.                 return -1;
  35.         }
  36.         //3.数据传输
  37.         while(1)
  38.         {
  39.                 struct sockaddr_in clie;
  40.                 socklen_t len = sizeof(clie);
  41.                 char recv_buf[1024]={0};       
  42.                 int ret = recvfrom(sockfd,recv_buf,1023,0,(struct sockaddr *)&clie,&len);
  43.                 if(ret > 0)
  44.                 {
  45.                         printf("recv data[%s][port:%d]:%s\n",inet_ntoa(clie.sin_addr),ntohs(clie.sin_port),recv_buf);
  46.                         if(strncmp(recv_buf,"byebye",6) == 0)//接收到了退出指令
  47.                         {       
  48.                                 printf("[%s][port:%d] exit!!!\n",inet_ntoa(clie.sin_addr),ntohs(clie.sin_port));
  49.                                 continue;
  50.                         }
  51.                         char *buf = "OK!!\n";
  52.                         sendto(sockfd,buf,strlen(buf),0,(struct sockaddr *)&clie,len);
  53.                 }
  54.         }
  55.         //4.关闭套接字
  56.         close(sockfd);
  57. }
复制代码
UDP客户端:

  1. #include <stdio.h>
  2. #include <sys/types.h>
  3. #include <sys/socket.h>
  4. #include <string.h>
  5. #include <unistd.h>
  6. #include <stdlib.h>
  7. #include <arpa/inet.h>
  8. #include <netinet/in.h>
  9. int main(int argc,char *argv[])
  10. {
  11.         if(argc != 3)
  12.         {
  13.                 printf("Usage:%s <IP> <PORT>\n",argv[0]);
  14.                 return -1;
  15.         }
  16.         //1.创建套接字
  17.         int sockfd = socket(AF_INET,SOCK_DGRAM,0);
  18.         if(sockfd == -1)
  19.         {
  20.                 perror("socket failed!!");
  21.                 return -1;
  22.         }
  23.         //2.指定服务器的地址
  24.         struct sockaddr_in Serv;
  25.         Serv.sin_family = AF_INET;
  26.         Serv.sin_port=htons(atoi(argv[2]));
  27.         Serv.sin_addr.s_addr = inet_addr(argv[1]);
  28.        
  29.         //3.数据传输
  30.         while(1)
  31.         {
  32.                 struct sockaddr_in clie;        //保存服务器的ip地址和端口的
  33.                 socklen_t len = sizeof(clie);
  34.         char buf[1024];
  35.         fgets(buf,1023,stdin);
  36.             sendto(sockfd,buf,strlen(buf) - 1,0,(struct sockaddr *)&Serv,len);
  37.         if(strncmp(buf,"bye",3) == 0)
  38.         {
  39.             break;
  40.         }
  41.                 //收
  42.                 char recv_buf[1024]={0};       
  43.                 int ret = recvfrom(sockfd,recv_buf,1023,0,(struct sockaddr *)&clie,&len);
  44.                 if(ret > 0)
  45.                 {
  46.                         printf("recv data[%s][port:%d]:%s\n",inet_ntoa(clie.sin_addr),ntohs(clie.sin_port),recv_buf);
  47.                         if(strncmp(recv_buf,"byebye",6) == 0)//接收到了退出指令
  48.                         {       
  49.                                 printf("[%s][port:%d] exit!!!\n",inet_ntoa(clie.sin_addr),ntohs(clie.sin_port));
  50.                                 continue;
  51.                         }
  52.                        
  53.                 }
  54.         }
  55.         //4.关闭套接字
  56.         close(sockfd);
  57. }
复制代码
编程简要:

TCP服务器:
创建套接字,绑定服务器的ip和端口,进入监听模式,等待客户端连接获取客户端网络套接字,创建子历程和客户端通讯,设定通讯结束条件调用exit(0)让子历程退出。
TCP客户端:
创建套接字,指定必要通讯的服务器的ip和端口,向该服务器发送连接哀求,哀求成功后开始和服务器收发数据。
UDP服务器:
创建套接字,绑定ip和端口,创建空的网络地址结构体,等待客户端对自己发消息,然后将客户端的地址保存到结构体里,开始于客户端对话通讯。
UDP客户端:
创建套接字,指定服务器的ip和端口,直接发送数据给服务器。
(以上编程简要是小编随意梳理的思绪(并不美满),读者可根据源代码自行梳理便于自己理解的思绪)
留意事项:以上源代码仅针对同一局域网之间的设备通讯。TCP是基于双方确认连接后再通讯的传输协议(稳固),UDP是不基于连接的,直接通讯的传输协议(较不稳固,但效率高,以是普及比TCP协议广泛)。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

张国伟

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表