IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> 网络编程01-TCP协议(详) -> 正文阅读

[系统运维]网络编程01-TCP协议(详)

目录

一、网络编程学习大纲

二、回顾系统编程中进程的通信方式

1、 管道

2、信号

3、IPC对象

三、网络编程(套接字编程)

1、特点

2、协议

3、历史

四、网络应用程序设计模式

1、C/S模式

2、B/S模式

3、优缺点

五、计算机网络体系结构模型

1、概念

2、作用

3、分类

4、OSI参考模型(七层模型)

5、TCP/IP协议模型(四层)

六、TCP/IP网络模型通信过程

1、两台计算机通过TCP/IP协议通讯的过程

2、数据包封装

3、以太网帧格式

4、ARP数据报格式

5、IP段格式

6、TCP数据报格式

七、传输层协议

TCP协议(打电话)

UDP协议(信)

八、网络编程中几个重要概念

1、socket --> 插座,套接字 插座的种类繁多,就像很多个协议一样,必须提前设置好协议。

?2、IP地址

3、端口号(16位)

4、字节序

九、TCP通信

1、通信过程

2、客户端API

1、建立套接字

2、发起连接 -拨打电话

3、发送数据-------write send

4、关闭文件--close

服务器端API:

1、绑定 bind

2、设置铃声

3、等待客户端的连接 --accept

4、接收数据 recv

练习3: 修改代码,实现TCP客户端与TCP服务器互相可以收跟发。

练习4: 写一个回射服务器,这个服务器功能就是客户端发送什么数据给服务器,服务器都会将数据回发到客户端。


一、网络编程学习大纲

1、计算机网络体系结构模型、网络编程专业术语(socket/IP/端口号)、通信时序图
2、传输层协议: TCP协议/UDP协议。
3、多进程并发服务器、多线程并发服务器
4、网络编程IO模型:阻塞IO/非阻塞IO/多路复用/信号驱动
5、超时接收数据方法

二、回顾系统编程中进程的通信方式

1、 管道

无名管道(只能作用亲缘关系) -- pipe()
有名管道(任意两个进程) --- mkfifo()

2、信号

发送信号 -- kill()
捕捉信号 -- signal()

3、IPC对象

消息队列---接收特征类型的数据 -- ftok() msgget() msgsnd() msgrcv() msgctl()
共享内存 --> 双方进程可以同时对一片内存进行读写 shmget() shmat() shmdt() shmctl()
信号量 --> 不属于通信方式,只是一种互斥的量 semget() semop() semctl()
特点: 只能在同一台主机上内部通信,不能跨平台。

三、网络编程(套接字编程)

1、特点

既可以在同一台主机上内部通信,也可以在不同主机之间通信。
自己的ubuntu ----自己的ubuntu
自己的ubuntu -----同一个局域网内除了自己之外任意一台主机
总结一下: 网络通信前提: 只要你在某个局域网内,就可以与局域网任意一台主机通信。

2、协议

1)概念 应用的角度出发,在不同的主机之间通信,双方都必须遵循的同一种规则。协议可理解为“规则”, 是数据传输和数据的解释的规则。
假设,A、B双方欲传输文件。
规定:
第一次,传输文件名,接收方接收到文件名,应答OK给传输方;
第二次,发送文件的尺寸,接收方接收到该数据再次应答一个OK;
第三次,传输文件内容。同样,接收方接收数据完成后应答OK表示文件内容接收成功。
由此,无论A、B之间传递何种文件,都是通过三次数据传输来完成。A、B之间形成了一个最简单的数 据传输规则。双方都按此规则发送、接收数据。A、B之间达成的这个相互遵守的规则即为协议。
这种仅在A、B之间被遵守的协议称之为原始协议。当此协议被更多的人采用,不断的增加、改进、维 护、完善。最终形成一个稳定的、完整的文件传输协议,被广泛应用于各种文件传输过程中。该协议 就成为一个标准协议。最早的ftp协议就是由此衍生而来。
TCP协议注重数据的传输。http协议着重于数据的解释。
2)常见的协议
传输层 常见协议有TCP /UDP协议。
应用层 常见的协议有HTTP协议 FTP协议
网络层 常见协议有IP协议 ICMP协议 IGMP协议
网络接口层 常见协议有ARP协议 RARP协议
TCP传输控制协议(Transmission Control Protocol)是一种面向连接的、可靠的、基于字节流的传输层通信协议。
UDP用户数据报协议(User Datagram Protocol)是OSI参考模型中一种无连接的传输层协议,提供面向事务的简单不可靠信息传送服务。
HTTP超文本传输协议(Hyper Text Transfer Protocol)是互联网上应用最为广泛的一种网络协议
FTP文件传输协议(File Transfer Protocol)
IP协议是因特网互联协议 Internet Protocol
ICMP协议是Internet控制报文协议(Internet Control Message Protocol)它是TCP/IP协议族的一
个子协议, 用于在IP主机、路由器之间传递控制消息。
IGMP协议是 Internet 组管理协议(Internet Group Management Protocol),是因特网协议家族中的一个组 播协议。该协议运行在主机和组播路由器之间。
ARP协议是正向地址解析协议(Address Resolution Protocol),通过已知的IP,寻找对应主机的MAC地址。
RARP是反向地址 转换 协议,通过MAC地址确定IP地址。

3、历史

ARPAnet(阿帕网):
1)历史:
1958年美国总统艾森豪威尔向美国国会提出建立DARPA (Defense Advanced Research Project
Agency),即国防部高级研究计划署,简称ARPA。1968年6月DARPA提出“资源共享计算机网络”
(Resource Sharing Computer Networks),目的在于让DARPA的所有电脑互连起来,这个网络就叫做ARPAnet。
2)使用的协议:网络控制协议(Network Control Protoco,NCP) 3) 缺点:不能互联不同类型的计算机 和 不同类型的操作系统,同时也没有纠错功能
Internet(因特网):
1)由于ARPAnet网络的局限性,引入了TCP/IP协议。TCP/IP是Internet上所有网络和主机之间进行交 流的标准连接协议。通常所说的TCP/IP协议实际上包含了大量的协议和应用,且由多个独立定义的协议组合在一起,因此,更确切地说,应该称之为TCP/IP协议簇。
TCP/IP协议: 传输控制协议/因特网互联协议
TCP协议: 用于检测网络中传输差错
IP协议: 负责不同的网络之间的通信
通俗讲: TCP负责发送传输问题,一旦有问题发出信号,要求重新传输,直到数据安全到达对方为止。
IP给每一台联网设备规定一个地址

四、网络应用程序设计模式

1、C/S模式

传统的网络应用设计模式,客户机(client)/服务器(server)模式。需要在通讯两端各自部署客户机和服务器来完成数据通信。

2、B/S模式

浏览器(browser)/服务器(server)模式。只需在一端部署服务器,而另外一端使用每台PC都默认配置的浏览器即可完成数据的传输。

3、优缺点

对于C/S模式来说,其优点明显。客户端位于目标主机上可以保证性能,将数据缓存至客户端本地,从而提高数据传输效率。且,一般来说客户端和服务器程序由一个开发团队创作,所以他们之间所采用 的协议相对灵活。可以在标准协议的基础上根据需求裁剪及定制。例如,腾讯公司所采用的通信协 议,即为ftp协议的修改剪裁版。 因此,传统的网络应用程序及较大型的网络应用程序都首选C/S模式进行开发。如,知名的网络游戏魔 兽世界。3D画面,数据量庞大,使用C/S模式可以提前在本地进行大量数据的缓存处理,从而提高观感。
C/S模式的缺点也较突出。由于客户端和服务器都需要有一个开发团队来完成开发。工作量将成倍提 升,开发周期较长。另外,从用户角度出发,需要将客户端安插至用户主机上,对用户主机的安全性构成威胁。这也是很多用户不愿使用C/S模式应用程序的重要原因。
B/S模式相比C/S模式而言,由于它没有独立的客户端,使用标准浏览器作为客户端,其工作开发量较小。只需开发服务器端即可。另外由于其采用浏览器显示数据,因此移植性非常好,不受平台限制。如早期的偷菜游戏,在各个平台上都可以完美运行。
B/S模式的缺点也较明显。由于使用第三方浏览器,因此网络应用支持受限。另外,没有客户端放到对方主机上,缓存数据不尽如人意,从而传输数据量受到限制。应用的观感大打折扣。第三,必须与浏览器一样,采用标准http协议进行通信,协议选择不灵活。因此在开发过程中,模式的选择由上述各自的特点决定。根据实际需求选择应用程序设计模式。

五、计算机网络体系结构模型

1、概念

指的是主机内部集成的结构和每层协议的集合。每台主机本身就存在一个相同的网络体系结构。

2、作用

封装数据和解析数据。

3、分类

OSI(Open System Interconnection开放系统互联)参考模型
TCP/IP参考模型

4、OSI参考模型(七层模型)

1.物理层:主要定义物理设备标准,如网线的接口类型、光纤的接口类型、各种传输介质的传输速率
等。它的主要作用是传输比特流(就是由1、0转化为电流强弱来进行传输,到达目的地后再转化为1、0,也就是我们常说的数模转换与模数转换)。这一层的数据叫做比特。
2.数据链路层:定义了如何让格式化数据以帧为单位进行传输,以及如何让控制对物理介质的访问。这 一层通常还提供错误检测和纠正,以确保数据的可靠传输。如:串口通信中使用到的115200、8、N、1
3.网络层:在位于不同地理位置的网络中的两个主机系统之间提供连接和路径选择。Internet的发展使
得从世界各站点访问信息的用户数大大增加,而网络层正是管理这种连接的层。
4.传输层:定义了一些传输数据的协议和端口号(WWW端口80等),如:TCP(传输控制协议,传输 效率低,可靠性强,用于传输可靠性要求高,数据量大的数据),UDP(用户数据报协议,与TCP特性恰恰相反,用于传输可靠性要求不高,数据量小的数据,如QQ聊天数据就是通过这种方式传输的)。 主要是将从下层接收的数据进行分段和传输,到达目的地址后再进行重组。常常把这一层数据叫做段。
5.会话层:通过传输层(端口号:传输端口与接收端口)建立数据传输的通路。主要在你的系统之间发起会话或者接受会话请求(设备之间需要互相认识可以是IP也可以是MAC或者是主机名)。
6.表示层:可确保一个系统的应用层所发送的信息可以被另一个系统的应用层读取。例如,PC程序与另一台计算机进行通信,其中一台计算机使用扩展二一十进制交换码(EBCDIC),而另一台则使用美国信息交换标准码(ASCII)来表示相同的字符。如有必要,表示层会通过使用一种通格式来实现多种数据格式之间的转换。
7.应用层:是最靠近用户的OSI层。这一层为用户的应用程序(例如电子邮件、文件传输和终端仿真)提供网络服务。
口诀:物数网传会表应
注意:OSI模型非常臃肿,处理数据效率非常低,这个模型已经被TCP/IP协议模型所取代

5、TCP/IP协议模型(四层)

TCP/IP网络协议栈分为应用层(Application)、传输层(Transport)、网络层(Network)和链路
层(Link)四层。如下图所示:
一般在应用开发过程中,讨论最多的是TCP/IP模型。链路层 也可以称之为 网络接口层

六、TCP/IP网络模型通信过程

1、两台计算机通过TCP/IP协议通讯的过程

2、数据包封装

传输层及其以下的机制由内核提供,应用层由用户进程提供(后面将介绍如何使用socket API编写应用程序),应用程序对通讯数据的含义进行解释,而传输层及其以下处理通讯的细节,将数据从一台计算机通过一定的路径发送到另一台计算机。应用层数据通过协议栈发到网络上时,每层协议都要加上一个数据首部(header),称为封装(Encapsulation),如下图所示:

不同的协议层对数据包有不同的称谓,在传输层叫做段(segment),在网络层叫做数据报
(datagram),在链路层叫做帧(frame)。数据封装成帧后发到传输介质上,到达目的主机后每层 协议再剥掉相应的首部,最后将应用层数据交给应用程序处理。

3、以太网帧格式

1、以太网的帧格式如下所示:
其中的源地址和目的地址是指网卡的硬件地址(也叫MAC地址),长度是48位(6个字节),是在网卡出厂时固化的。可在shell中使用ifconfig命令查看,“HWaddr 00:15:F2:14:9E:3F”部分就是硬件地
址。协议字段有三种值,分别对应IP、ARP、RARP。帧尾是CRC校验码。以太网帧中的数据长度规定最小46字节,最大1500字节,ARP和RARP数据包的长度不够46字节,要在后面补填充位。最大值1500称为以太网的最大传输单元(MTU),不同的网络类型有不同的MTU,如果一个数据包从以太网路由到拨号链路上,数据包长度大于拨号链路的MTU,则需要对数据包进行分片
(fragmentation)。ifconfig命令输出中也有“MTU:1500”。注意,MTU这个概念指数据帧中有效
载荷的最大长度,不包括帧头长度。

4、ARP数据报格式

在网络通讯时,源主机的应用程序知道目的主机的IP地址和端口号,却不知道目的主机的硬件地址,而数据包首先是被网卡接收到再去处理上层协议的,如果接收到的数据包的硬件地址与本机不符,则直接丢弃。因此在通讯前必须获得目的主机的硬件地址。ARP协议就起到这个作用。源主机发出ARP请求,询问“IP地址是192.168.0.1的主机的硬件地址是多少”,并将这个请求广播到本地网段(以太网帧首部的硬件地址填FF:FF:FF:FF:FF:FF表示广播),目的主机接收到广播的ARP请求,发现其中的IP地址与本机相符,则发送一个ARP应答数据包给源主机,将自己的硬件地址填写在应答包中。
每台主机都维护一个ARP缓存表,可以用arp -a命令查看。缓存表中的表项有过期时间(一般为20分钟),如果20分钟内没有再次使用某个表项,则该表项失效,下次还要发ARP请求来获得目的主机的硬件地址。
ARP数据报的格式如下所示:
源MAC地址、目的MAC地址在以太网首部和ARP请求中各出现一次,对于链路层为以太网的情况是多余的,但如果链路层是其它类型的网络则有可能是必要的。硬件类型指链路层网络类型,1为以太网,协议类型指要转换的地址类型,0x0800为IP地址,后面两个地址长度对于以太网地址和IP地址分别为6和4(字节),op字段为1表示ARP请求,op字段为2表示ARP应答。

5、IP段格式

IP数据报的首部长度和数据长度都是可变长的,但总是4字节的整数倍。对于IPv4,4位版本字段是4。4位首部长度的数值是以4字节为单位的,最小值为5,也就是说首部长度最小是4x5=20字节,也就是不带任何选项的IP首部,4位能表示的最大值是15,也就是说首部长度最大是60字节。8位TOS字段有3个位用来指定IP数据报的优先级(目前已经废弃不用),还有4个位表示可选的服务类型(最小延迟、最大?吐量、最大可靠性、最小成本),还有一个位总是0。总长度是整个数据报(包括IP首部和IP层payload)的字节数。每传一个IP数据报,16位的标识加1,可用于分片和重新组装数据报。3位标志和13位片偏移用于分片。TTL(Time to live)是这样用的:源主机为数据包设定一个生存时间,比如64,每过一个路由器就把该值减1,如果减到0就表示路由已经太长了仍然找不到目的主机的网络,就丢弃该包,因此这个生存时间的单位不是秒,而是跳(hop)。协议字段指示上层协议是TCP、UDP、ICMP还是IGMP。然后是校验和,只校验IP首部,数据的校验由更高层协议负责。IPv4的IP地址长度为32位。
思考题:如果源主机和目的主机不在同一网段,ARP请求的广播帧无法穿过路由器,源主机如何与目的主机通信?
参考: https://blog.csdn.net/weixin_43166958/article/details/86503506

6、TCP数据报格式

有源端口号和目的端口号,通讯的双方由IP地址和端口号标识。32位序号、32位确认序号、窗口大小。4位首部长度和IP协议头类似,表示TCP协议头的长度,以4字节为单位,因此TCP协议头最长可以是4x15=60字节,如果没有选项字段,TCP协议头最短20字节。URG、ACK、PSH、RST、SYN、FIN是六个控制位。16位检验和将TCP协议头和数据都计算在内。

七、传输层协议

TCP协议(打电话)

1、概念:
用来检测网络传输中差错的传输控制协议 transmission control protocol 。 是一种面向连接的传
输层协议,它能提供高可靠性通信(即数据无误、数据无丢失、数据无失序、数据无重复到达的通信)
2、适用场合:
1)、对传输质量要求较高,以及传输大量数据的通信
2)、在需要可靠数据传输的场合,通常使用TCP协议
3)、QQ等即时通讯软件的用户登录账户管理相关的功能,通常采用TCP协议

UDP协议(信)

1、概念
UDP( User Datagram Protocol )用户数据报协议。是不可靠的无连接的协议。在数据发送前,
因为不需要进行连接,所以可以进行高效率的数据传输。
2、适用场合:
1)发送小尺寸数据
2)适用于广播/组播式通信
3) QQ等即时通讯软件的点对点文本通讯以及音视频通讯 常采用 UDP协议
4)网络多媒体服务中通常采用UDP方式进行实时数据传输

八、网络编程中几个重要概念

1、socket --> 插座,套接字 插座的种类繁多,就像很多个协议一样,必须提前设置好协议。

1)概念
Socket本身有“插座”的意思,在Linux环境下,用于表示进程间网络通信的特殊文件类型。本质为内
核借助缓冲区形成的伪文件。既然是文件,那么理所当然的,我们可以使用文件描述符引用套接字。与管道类似的,Linux系统将其封装成文件的目的是为了统一接口,使得读写套接字和读写文件的操作一致。区别是管道主要应用于本地进程间通信,而套接字多应用于网络进程间数据的传递。套接字的内核实现较为复杂,不宜在学习初期深入学习。
在TCP/IP协议中,“IP地址+TCP或UDP端口号”唯一标识网络通讯中的一个进程。“IP地址+端口
号”就对应一个socket。欲建立连接的两个进程各自有一个socket来标识,那么这两个socket组成的
socket pair就唯一标识一个连接。因此可以用Socket来描述网络连接的一对一关系。
2)套接字通信原理如下图所示:
在网络通信中,套接字一定是成对出现的。一端的发送缓冲区对应对端的接收缓冲区。我们使用同一个文件描述符指向发送缓冲区和接收缓冲区。
TCP/IP协议最早在BSD UNIX上实现,为TCP/IP协议设计的应用层编程接口称为socket API。
3)特点
是一套编程的函数接口,目的是 建立套接字
无论是 TCP 协议,还是 UDP 协议,都必须使用 socket
socket 在网络模型中处于应用层与传输层之间
4)例子
使用TCP协议,那么socket建立出来的套接字就是TCP套接字
1 套接字 : int sockfd = socket ( TCP 协议 ); --> 得到 TCP 协议套接字
2 普通文件: int fd = open ( 普通文件 ); --> fd 就是文件描述符

?2、IP地址

1)概念
用来标识网络中不同的主机。通信必须要具有一个IP地址
2)分类
IPV4地址---32位 IPV6地址 ---128位
3)如何表示
常常以点分制"192.168.8.108"
4)数据包中都必须要包含目的IP地址,源IP地址。路由器依赖于此信息作为数据选择路由

3、端口号(16位)

标识同一台主机内 不同的应用程序。
                             网络 1
        主机1 -----------------------------------主机2

         qq                                       qq 

         微信                                     微信 
             ......
        Jack.c       ------------>               Rose.c 
IP地址 192.168.8.100                         192.168.8.108 ---> 双方必须在相同的局域网

端口号    50001                                   50001    ---> 双方都必须使用相同的端口号
端口号占用:
1)系统占用端口号: 1 - 1023 (用户不能再次使用端口号进行通信)
2)可用端口: 1024 - 65535
端口号选择错误 --> 连接错误

4、字节序

1)概念
一个多字节存储单位的低地址存储数据的高有效位 还是 低有效 位,说白了,也就是数据在计算机
内存中以什么样的方式存储
2)分类
小端字节序:数据的低有效位 存储 在 内存中的 低地址
大端字节序:数据的低有效位 存储 在 内存中的 高地址
3)为了避免不同类别主机之间在数据交换时由于对于字节序的不同而导致的差错,引入了网络字节序。也就是统一规定所有主机通过网络发送数据包时转为大端序,也就是网络字节序。
内存地址: 低地址 -------------------------> 高地址
"hello"
         x86平台 -----> ARM

     小端字节序: 低字节存放在低地址 1 2 3 4
     大端字节序: 高字节存放在低地址 4 3 2 1

    本地字节序: 取决于主机本身 x86是小端 ARM是大端
    网络字节序: 一定是大端字节序

            高         低 
     例子: 192.168.1.120

    x86: 021.1.861.291 ---> 网络: 192.168.1.120 
    ARM: 192.168.1.120 ---> 网络: 192.168.1.120 
总结一下: 无论本身是大端还是小端,都需要把自身的字节序转换为网络字节序,即大端,才能在网络中传输数据。

九、TCP通信

1、通信过程

2、客户端API

1、建立套接字

1 #include <sys/types.h> /* See NOTES */
2 #include <sys/socket.h>
3 int socket ( int domain , int type , int protocol );
函数作用
建立套接字,返回套接字文件描述符
函数参数
????????domain : 你要选择 哪一种 地址族
????????????????????????PF_INET / AF_INET Ipv4 网络协议
????????????????????????PF_INET6/AF_INET6 Ipv6 网络协议
????????type: 你 要选择 哪一种 协议
????????????????????????SOCK_STREAM 选择TCP ----流式 套接字
????????????????????????SOCK_DGRAM 选择UDP ----数据报套接字
????????protocol : 传0表示使用默认协议
返回值
????????成功返回 套接字文件描述符 sockfd
????????失败返回 -1

2、发起连接 -拨打电话

1 #include <sys/types.h> /* See NOTES */
2 #include <sys/socket.h>
3 int connect ( int sockfd , const struct sockaddr * addr , socklen_t addrlen );
函数参数
????????sockfd --> 套接字文件描述符
????????addr ---对方的IP地址 和 端口号
????????addrlen ---地址的长度 sizeof(struct sockaddr_in )
1 使用 man 7 ip 可以查看以下结构体
2 struct sockaddr --- 旧的结构体
3 {
4 unsigned short int sa_family ; --- IP 地址 端口号 协议
5 unsigned char sa_data [ 14 ];
6 };
7 IPV4 结构体
8 struct sockaddr_in
9 {
10 short int sin_family ; / 地址族 IPV4 IPV6 /
11 unsigned short int sin_port ; / 端口号 /
12 struct in_addr sin_addr ; / IP 地址 /
13 };
14 struct in_addr {
15 in_addr_t s_addr ; / in_addr_t 32 位的 unsigned int ,该无符号整数采用大端字节序。 /
16 };

比如:初始化 IP地址 和端口号 --IPV4
1 struct sockaddr_in serverAddr ;
2 serverAddr . sin_family = PF_INET ;
3 serverAddr . sin_port = htons ( 5000 ) ; // host to network short
4 serverAddr . sin_addr . s_addr = inet_addr ( "192.168.1.120" ); // 主机 IP 地址 --- 》网络 IP
inet_addr 5 connect ( sockfd , ( struct sockaddr * ) & serverAddr , sizeof ( struct sockaddr_in ) );
扩展函数:
1 #include <arpa/inet.h>
2 uint16_t htons ( uint16_t hostshort ); // 将主机端口号 转成 网络端口号
3 uint16_t ntohs ( uint16_t netshort ); // 将网络端口号 转成 主机端口号
4
5 #include <sys/socket.h>
6 #include <netinet/in.h>
7 #include <arpa/inet.h>
8 in_addr_t inet_addr ( const char * cp ); // 将主机 IP 转成 网络 IP
9 char * inet_ntoa ( struct in_addr in ); // 将网络 IP 转成 主机 IP
参数:
????????cp ---》主机IP
返回值 :
????????返回 网络IP

3、发送数据-------write send

1 #include <sys/types.h>
2 #include <sys/socket.h>
3 ssize_t send ( int sockfd , const void * buf , size_t len , int flags );
作用
????????用于网络中发送数据
参数
????????sockfd: 套接字文件描述符
????????buf : 你要发送的数据
????????len : 你要发送数据的大小 ,以 字节为单位
????????flags : 一般默认为 0
返回值
????????成功返回发送的字节数
????????失败返回 -1

4、关闭文件--close

服务器端API:

1、绑定 bind

1 #include <sys/types.h> /* See NOTES */
2 #include <sys/socket.h>
3 int bind ( int sockfd , const struct sockaddr * addr , socklen_t addrlen );
作用:
????????绑定自己的IP地址 和 端口号
参数
????????sockfd : 套接字文件描述符
????????addr : 自己的IP地址和端口号
????????addrlen : 地址的大小长度
返回值

2、设置铃声

1 #include <sys/types.h> /* See NOTES */
2 #include <sys/socket.h>
3 int listen ( int sockfd , int backlog );
作用
????????创建 一个 未连接 队列 ,同时最多连接的客户端 总数 backlog
参数
????????sockfd : 套接字文件描述符
????????backlog :同时 最多支持连接上来的客户端总数
返回值
????????成功返回 0
????????失败返回 -1
说明
典型的服务器程序可以同时服务于多个客户端,当有客户端发起连接时,服务器调用的accept()返
回并接受这个连接,如果有大量的客户端发起连接而服务器来不及处理,尚未accept的客户端就处于连接等待状态,listen()声明sockfd处于监听状态,并且最多允许有backlog个客户端处于连接待状态,如果接收到更多的连接请求就忽略。
1 查看系统默认 backlog
2 gec @ubuntu : / mnt / hgfs / 2 $ cat / proc / sys / net / ipv4 / tcp_max_syn_backlog
3 128

3、等待客户端的连接 --accept

1 #include <sys/types.h> /* See NOTES */
2 #include <sys/socket.h>
3 int accept ( int sockfd , struct sockaddr * addr , socklen_t * addrlen );
作用
????????等待客户端的连接
参数
????????sockfd: 套接字文件描述符
????????addr : 连接上来的客户端的IP地址和端口号
????????addrlen : 长度
//如果客户端连接上来了,可以获取到连接上来的客户端的IP地址和端口号
返回值:
????????成功返回 已连接的套接字文件描述符

4、接收数据 recv

1 #include <sys/types.h>
2 #include <sys/socket.h>
3 ssize_t recv ( int sockfd , void * buf , size_t len , int flags );
参数
????????sockfd: 已连接的套接字文件描述符 connectfd
????????buf : 接收到的数据存储到这里
????????len : 接收数据的大小
????????flags : 一般设置为 0
返回值:
????????成功返回 接收到的字节数
????????失败返回 -1
????????返回 0 表示 客户端断开连接
服务器代码:
1 #include <stdio.h>
2 #include <sys/types.h> /* See NOTES */
3 #include <sys/socket.h>
4 #include <sys/socket.h>
5 #include <arpa/inet.h> 6 #include <unistd.h>
7 #include <stdlib.h>
8
9
10 #define SERVER_ADDR "192.168.63.25" // 服务器的 IP 地址
11 #define SERVER_PORT 20000 //port 服务器的端口号
12
13
14 int main ()
15 {
16 int ret ;
17 //1 、买手机 ( 建立套接字 )
18 int socketFd = socket ( AF_INET , SOCK_STREAM , 0 );
19 if ( socketFd == - 1 ){
20 perror ( "socket error" );
21 exit ( 0 );
22 }
23 //2 、绑定自己的电话号码(绑定自己的 IP 地址和端口号)
24 // 定义一个 IPV4 结构体变量 , 存储 IP 地址和端口号
25 struct sockaddr_in serverAddr ;
26 serverAddr . sin_family = AF_INET ; //IPv4
27 serverAddr . sin_port = htons ( SERVER_PORT ); //16 端口号 -- 本地端口号 -- 网络端口号
28 serverAddr . sin_addr . s_addr = inet_addr ( SERVER_ADDR ); //32IP 地址 -- 本地 IP-- 网络 IP
29
30 ret = bind ( socketFd , ( struct sockaddr * ) & serverAddr , sizeof ( struct sockaddr_in ));
31 if ( ret == - 1 ){
32 perror ( "bind error" );
33 exit ( 0 );
34 }
35 //3 、设置铃声(设置监听)
36 ret = listen ( socketFd , 20 ); //20 表示同时连接上来的客户端的最大数量
37 if ( ret == - 1 ){
38 perror ( "listen error" );
39 exit ( 0 );
40 }
41 printf ( " 阻塞等待新的客户端连接 ......\n" );
42 //4 、坐等电话 ( 阻塞等待客户端的连接 )
43 int newClientFd = accept ( socketFd , NULL , NULL );
44 if ( ret == - 1 ){ 45 perror ( "accept error" );
46 exit ( 0 );
47 }
48 // 注意 accept 函数的返回值 表示 新的客户端的文件描述符 ,后面与客户端通信 必须使用该文件描述
49 printf ( " 有新的客户端连接上来 ....\n" );
50
51 //5 、聊天 接收 数据
52 char buf [ 1024 ] = { 0 };
53 read ( newClientFd , buf , sizeof ( buf ));
54
55 printf ( "buf:%s\n" , buf );
56
57
58 //6 、关闭 断开连接
59 close ( socketFd );
60 close ( newClientFd );
61
62 return 0 ;
63 }
客户端代码:
1 #include <stdio.h>
2 #include <sys/socket.h>
3 #include <sys/types.h> /* See NOTES */
4 #include <netinet/in.h>
5 #include <arpa/inet.h>
6 #include <string.h>
7 #include <unistd.h>
8
9 #define OWNADDR "192.168.112.109" // 我自己电脑的 ip 地址
10 #define OWNPORT 10000 // 我自己电脑的该程序的端口号
11
12 #define SERVERADDR "192.168.112.109" // 对方的 服务器的 IP 地址
13 #define SERVERPORT 11111 // 对方的 服务器的端口号
14
15 int main ()
16 {
17 //1 、买手机(建立套接字) 18 int socketfd = socket ( AF_INET , SOCK_STREAM , 0 );
19 if ( socketfd == - 1 )
20 {
21 printf ( " 没钱了 ...., 失败 \n" );
22 return - 1 ;
23 }
24 //2 、绑定自己的电话号码(绑定自己的 IP 地址 和端口号)
25 // 定义一个 IPV4 结构体变量,初始化自己的 IP 地址和端口号
26 struct sockaddr_in ownAddr ;
27 ownAddr . sin_family = AF_INET ; /* 地址族 IPV4*/
28 ownAddr . sin_port = htons ( OWNPORT ); //htons 将本地端口号转为网络端口号
29 ownAddr . sin_addr . s_addr = inet_addr ( OWNADDR ); // 将本地 IP 地址转为网络 IP 地址
30
31 bind ( socketfd , ( struct sockaddr * ) & ownAddr , sizeof ( struct sockaddr_in ));
32
33 //3 、开始打电话(发起连接)
34 struct sockaddr_in serverAddr ;
35 serverAddr . sin_family = AF_INET ; /* 地址族 IPV4*/
36 serverAddr . sin_port = htons ( SERVERPORT ); //htons 将本地端口号转为网络端口号
37 serverAddr . sin_addr . s_addr = inet_addr ( SERVERADDR ); // 将本地 IP 地址转为网络 IP 地址
38
39 connect ( socketfd ,( struct sockaddr * ) & serverAddr , sizeof ( struct sockaddr_in ));
40
41 //4 、聊天
42 while ( 1 )
43 {
44 printf ( "data " );
45 char buf [ 1024 ] = { 0 };
46 scanf ( "%s" , buf );
47 // 发送数据
48 send ( socketfd , buf , strlen ( buf ), 0 );
49 }
50 //5 、关闭
51 close ( socketfd );
52
53 return 0 ;
54 }
练习2: 自己ubuntu与自己的开发板通信。 自己的ubuntu与同桌的ubuntu通信。
自己的开发板与同桌的ubuntu通信。
自己的开发板与同桌的开发板的通信。

练习3: 修改代码,实现TCP客户端与TCP服务器互相可以收跟发。

服务器
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

//服务器的IP地址
#define SERVER_IP	"192.168.11.2"
#define SERVER_PORT	60000

//用来给客户端发送数据
void *send_buf(void *arg)
{
	//设置自己为分离属性
	pthread_detach(pthread_self());
	int ret;
	
	char buf[1024] = {0};
	while(1)
	{
		scanf("%s",buf); 	
		//给客户端发送数据
		send(*(int *)arg,buf,strlen(buf),0);  		
	}
	
}

int main(int argc,char **argv)
{
	//建立套接字
	int socket_fd;
	//AF_INET-->ipv4  SOCK_STREAM-->tcp
	socket_fd = socket(AF_INET,SOCK_STREAM,0);
	if(socket_fd < 0)
	{
		perror("socket fail");
		return -1;
	}	
		
	//重复绑定本机IP可能会出现失败,这个时候要设置端口号复用。(笔记2)
	int optval = 1;
 	setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR,&optval, sizeof(optval));

	//填充本机IP地址和端口
	struct sockaddr_in server_addr;
	server_addr.sin_family = AF_INET;
	server_addr.sin_port = htons(SERVER_PORT);//本机端口转化为网络端口
	server_addr.sin_addr.s_addr = inet_addr(SERVER_IP);//将本机IP转换为网络IP
	
	//绑定本机IP和端口
	int ret;
	ret = bind(socket_fd,(struct sockaddr *)&server_addr,sizeof(server_addr));
	if(ret < 0)
	{
		perror("bind fail");
		return -1;
	}	
	printf("bind success [%s %d]\n",SERVER_IP,SERVER_PORT);
	
	//监听
	ret = listen(socket_fd,20);
	if(ret < 0)
	{
		perror("listen fail");
		return -1;
	}	
	
	//等待客户端的连接
	int socket_client;//客户端的套接字
	struct sockaddr_in client_addr;//用来接受客户端的ip地址和端口
	socklen_t addrlen = sizeof(client_addr);
	//socket_client = accept(socket_fd,NULL,NULL); //如果不解析客户端的地址和端口可以设置地址参数为NULL
	socket_client = accept(socket_fd,(struct sockaddr *)&client_addr,&addrlen);
	if(socket_client < 0)
	{
		perror("accept fail");
		return -1;	
	}
	//解析客户端的ip和端口号
	char *ip = inet_ntoa(client_addr.sin_addr);//将网络ip转换为本机ip
	int port = ntohs(client_addr.sin_port);//将网络端口号转换为本机端口号
	printf("new client [ip:%s port:%d]\n",ip,port);
	//server_addr.sin_addr.s_addr
	
	//单独开一个线程用来给客户端发送数据
	pthread_t tid;
	ret = pthread_create(&tid,NULL,send_buf,&socket_client);//传参客户端的socket套接字
	if(ret != 0)
	{
		perror("pthread_create fail");
		return -1;
	}
	
	//接受客户端发送的数据(接受的是客户端的套接字)
	char buf[1024] = {0};
	while(1)
	{	
		memset(buf,0,sizeof(buf));
		ret = recv(socket_client,buf,sizeof(buf),0);
		//ret = read(socket_client,buf,sizeof(buf));//和recv函数的功能一样
		if(ret == 0)
		{
			printf("client down\n"); //客户端掉线
			break;
		}
		printf("[ip:%s port:%d] buf:%s ret:%d \n",ip,port,buf,ret);
		

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

客户端

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>

/*socket*/
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
/*ip*/
#include <netinet/in.h> //man 3 inet_addr
#include <arpa/inet.h>

//服务器的IP地址
#define SERVER_IP	"192.168.11.2"
#define SERVER_PORT	60000

//用来单独接收的线程
void *recv_buf(void *arg)
{
	//设置自己为分离属性
	pthread_detach(pthread_self());
	
	int ret;
	char buf[1024] = {0};
	
	while(1)
	{	
		bzero(buf,1024);
		//接受服务器回射的数据
		ret = recv(*(int *)arg,buf,sizeof(buf),0);
		if(ret == 0)
		{
			printf("server down\n"); //服务器掉线
			break;
		}	
		printf("recv buf:%s ret:%d \n",buf,ret);	
	}
}

int main(int argc,char **argv)
{
	//ret用来判断返回值
	int ret;
	
	//建立套接字--socket
	int socket_fd;
	//AF_INET-->ipv4  SOCK_STREAM-->tcp
	socket_fd = socket(AF_INET,SOCK_STREAM,0);
	if(socket_fd < 0)
	{
		perror("socket fail");
		return -1;
	}
	
	#if 0
	//绑定本机ip和端口(可以绑定自己的端口,如果不绑定系统随机分配)
	struct sockaddr_in my_addr;
	my_addr.sin_family = AF_INET; //ipv4
	my_addr.sin_port = htons(60001);//host to net(本机端口号转网络端口号)
	my_addr.sin_addr.s_addr = inet_addr("192.168.11.2");//将本机IP转换为网络IP	
	bind(socket_fd,(struct sockaddr *)&my_addr,sizeof(my_addr));
	#endif
	
	//创建一个线程用来接受服务器发送过来的数据
	pthread_t tid;
	ret = pthread_create(&tid,NULL,recv_buf,&socket_fd);
	if(ret != 0)
	{
		perror("pthread_create fail");
		return -1;
	}
	
	//填充IP地址(服务器)--新结构体
	struct sockaddr_in server_addr;
	server_addr.sin_family = AF_INET; //ipv4
	server_addr.sin_port = htons(SERVER_PORT);//host to net(本机端口号转网络端口号)
	server_addr.sin_addr.s_addr = inet_addr(SERVER_IP);//将本机IP转换为网络IP
	
	//连接服务器
	ret = connect(socket_fd,(struct sockaddr *)&server_addr,sizeof(server_addr));
	if(ret < 0)
	{
		printf("connect fail\n");
		return -1;
	}
	
	//给服务器发送数据
	char buf[1024] = {0};
	while(1)
	{	
		memset(buf,0,sizeof(buf));
		scanf("%s",buf);
		ret = send(socket_fd,buf,strlen(buf),0);  //如果服务器掉线,客户端继续发送,就会导致进程退出
		//ret = write(socket_fd,buf,strlen(buf));
		printf("send success ret:%d\n",ret);
	}
	
	//关闭套接字
	close(socket_fd);
	return 0;
}

练习4: 写一个回射服务器,这个服务器功能就是客户端发送什么数据给服务器,服务器都会将数据回发到客户端。

服务器

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

//服务器的IP地址
#define SERVER_IP	"192.168.11.2"
#define SERVER_PORT	60000

int main(int argc,char **argv)
{
	//建立套接字
	int socket_fd;
	//AF_INET-->ipv4  SOCK_STREAM-->tcp
	socket_fd = socket(AF_INET,SOCK_STREAM,0);
	if(socket_fd < 0)
	{
		perror("socket fail");
		return -1;
	}	
		
	//重复绑定本机IP可能会出现失败,这个时候要设置端口号复用。
	int optval = 1;
 	setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR,&optval, sizeof(optval));

	//填充本机IP地址和端口
	struct sockaddr_in server_addr;
	server_addr.sin_family = AF_INET;
	server_addr.sin_port = htons(SERVER_PORT);//本机端口转化为网络端口
	server_addr.sin_addr.s_addr = inet_addr(SERVER_IP);//将本机IP转换为网络IP
	
	//绑定本机IP和端口
	int ret;
	ret = bind(socket_fd,(struct sockaddr *)&server_addr,sizeof(server_addr));
	if(ret < 0)
	{
		perror("bind fail");
		return -1;
	}	
	printf("bind success [%s %d]\n",SERVER_IP,SERVER_PORT);
	
	//监听
	ret = listen(socket_fd,20);
	if(ret < 0)
	{
		perror("listen fail");
		return -1;
	}	
	
	//等待客户端的连接
	int socket_client;//客户端的套接字
	struct sockaddr_in client_addr;//用来接受客户端的ip地址和端口
	socklen_t addrlen = sizeof(client_addr);
	socket_client = accept(socket_fd,(struct sockaddr *)&client_addr,&addrlen);
	if(socket_client < 0)
	{
		perror("accept fail");
		return -1;	
	}
	//解析客户端的ip和端口号
	char *ip = inet_ntoa(client_addr.sin_addr);//将网络ip转换为本机ip
	int port = ntohs(client_addr.sin_port);//将网络端口号转换为本机端口号
	printf("[ip:%s port:%d]\n",ip,port);
	
	//接受客户端发送的数据(接受的是客户端的套接字)
	char buf[1024] = {0};
	while(1)
	{	
		memset(buf,0,sizeof(buf));
		ret = recv(socket_client,buf,sizeof(buf),0);
		//ret = read(socket_client,buf,sizeof(buf));//和recv函数的功能一样
		if(ret == 0)
		{
			printf("client down\n");
			break;
		}
		printf("[ip:%s port:%d] buf:%s ret:%d \n",ip,port,buf,ret);
		
		//将接收到的数据回射给客户端
		send(socket_client,buf,strlen(buf),0); 
	}
	
	//关闭套接字
	close(socket_fd);
	
	return 0;
}	

客户端

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
/*socket*/
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
/*ip*/
#include <netinet/in.h> //man 3 inet_addr
#include <arpa/inet.h>

//服务器的IP地址
#define SERVER_IP	"192.168.11.2"
#define SERVER_PORT	60000

int main(int argc,char **argv)
{
	//建立套接字--socket
	int socket_fd;
	//AF_INET-->ipv4  SOCK_STREAM-->tcp
	socket_fd = socket(AF_INET,SOCK_STREAM,0);
	if(socket_fd < 0)
	{
		perror("socket fail");
		return -1;
	}
	
	#if 0
	//绑定本机ip和端口(可以绑定自己的端口,如果不绑定系统随机分配)
	struct sockaddr_in my_addr;
	my_addr.sin_family = AF_INET; //ipv4
	my_addr.sin_port = htons(60001);//host to net(本机端口号转网络端口号)
	my_addr.sin_addr.s_addr = inet_addr("192.168.11.2");//将本机IP转换为网络IP	
	bind(socket_fd,(struct sockaddr *)&my_addr,sizeof(my_addr));
	#endif
	
	//填充IP地址(服务器)--新结构体
	struct sockaddr_in server_addr;
	server_addr.sin_family = AF_INET; //ipv4
	server_addr.sin_port = htons(SERVER_PORT);//host to net(本机端口号转网络端口号)
	server_addr.sin_addr.s_addr = inet_addr(SERVER_IP);//将本机IP转换为网络IP
	
	//连接服务器
	int ret;
	ret = connect(socket_fd,(struct sockaddr *)&server_addr,sizeof(server_addr));
	if(ret < 0)
	{
		printf("connect fail\n");
		return -1;
	}
	
	//给服务器发送数据
	char buf[1024] = {0};
	while(1)
	{	
		memset(buf,0,sizeof(buf));
		scanf("%s",buf);
		ret = send(socket_fd,buf,strlen(buf),0); 
		//ret = write(socket_fd,buf,strlen(buf));
		//printf("send success ret:%d\n",ret);
		
		//接受服务器回射的数据
		ret = recv(socket_fd,buf,sizeof(buf),0);
		printf("recv buf:%s ret:%d \n",buf,ret);
	}
	
	//关闭套接字
	close(socket_fd);
	return 0;
}

  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2022-10-31 12:39:35  更:2022-10-31 12:42:24 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年9日历 -2024/9/19 9:33:22-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码