socket通信原理_tcp socket通信的基本过程

socket通信原理_tcp socket通信的基本过程,第1张

socket通信原理_tcp socket通信的基本过程 一个完整的HTTP请求的过程此举例为抛砖引玉,引导大家进入思考状态。

当你按输入www.baidu.com ,浏览器接收到这个消息之后,浏览器根据自己的算法识别出你要访问的URL,为您展示出来搜索页面和广告,那么这些经历了哪些过程呢?大致过程如下:(1)浏览器查询 DNS,获取域名对应的IP地址; 具体过程包括浏览器搜索自身的DNS缓存、搜索 *** 作系统的DNS缓存、读取本地的Host文件和向本地DNS服 务器进行查询等。

(2)浏览器获得域名对应的IP地址以后,浏览器向服务器请求建立链接,发起三次握手;(3)TCP/IP链接建立起来后,浏览器向服务器发送HTTP请求;(4)服务器接收到这个请求,并根据路径参数映射到特定的请求处理器进行处理,并将处理结果及相应的视图返回给浏览器;(5)浏览器解析并渲染视图,若遇到对js文件、css文件及图片等静态资源的引用,则重复上述步骤并向服务器请求这些资源;(6)浏览器根据其请求到的资源、数据渲染页面,最终向用户呈现一个完整的页面。

下面,我们从底到上来一层层理解这个问题。

网络参考模型开放式系统互联通信参考模型(英语:Open System Interconnection Reference Model,缩写:OSI;简称为OSI模型)是一种概念模型,由国际标准化组织提出,一个试图使各种计算机在世界范围内互连为网络的标准框架。

定义于ISO/IEC 7498-1。

(摘自维基百科)7应用层 application layer例如HTTP、SMTP、SNMP、FTP、Telnet、SIP、SSH、NFS、RTSP、XMPP、Whois、ENRP、TLS6表示层 presentation layer例如XDR、ASN.1、SMB、AFP、NCP5会话层 session layer例如ASAP、ISO 8327 / CCITT X.225、RPC、NetBIOS、ASP、IGMP、Winsock、BSD sockets4传输层 transport layer例如TCP、UDP、RTP、SCTP、SPX、ATP、IL3网络层 network layer例如IP、ICMP、IPX、BGP、OSPF、RIP、IGRP、EIGRP、ARP、RARP、X.252数据链路层 data link layer例如以太网、令牌环、HDLC、帧中继、ISDN、ATM、IEEE 802.11、FDDI、PPP1物理层 physical layer例如线路、无线电、光纤通常人们认为OSI模型的最上面三层(应用层、表示层和会话层)在TCP/IP组中是一个应用层。

由于TCP/IP有一个相对较弱的会话层,由TCP和RTP下的打开和关闭连接组成,并且在TCP和UDP下的各种应用提供不同的端口号,这些功能能够被单个的应用程序(或者那些应用程序所使用的库)增加。

与此相似的是,IP是按照将它下面的网络当作一个黑盒子的思想设计的,这样在讨论TCP/IP的时候就可以把它当作一个独立的层。

TCP/IP 参考模型4应用层 application layer例如HTTP、FTP、DNS (如BGP和RIP这样的路由协议,尽管由于各种各样的原因它们分别运行在TCP和UDP上,仍然可以将它们看作网络层的一部分)3传输层 transport layer例如TCP、UDP、RTP、SCTP (如OSPF这样的路由协议,尽管运行在IP上也可以看作是网络层的一部分)2网络互连层 internet layer对于TCP/IP来说这是因特网协议(IP) (如ICMP和IGMP这样的必须协议尽管运行在IP上,也仍然可以看作是网络互连层的一部分;ARP不运行在IP上)1网络访问(链接)层 Network Access(link) layer例如以太网、Wi-Fi、MPLS等。

下面一张图更有助于你的理解HTTP 协议与 TCP/IP 协议**HTTP 是 TCP/IP 参考模型中应用层的其中一种实现。

**HTTP 协议的网络层基于 IP 协议,传输层基于 TCP 协议:HTTP 协议是基于 TCP/IP 协议的应用层协议。

TCP/IP 协议需要向程序员提供可编程的 API,该 API 就是 Socket,它是对 TCP/IP 协议的一个重要的实现,几乎所有的计算机系统都提供了对 TCP/IP 协议族的 Socket 实现。

Socket是进程通讯的一种方式,即调用这个网络库的一些API函数实现分布在不同主机的相关进程之间的数据交换。

流格式套接字(SOCK_STREAM) 流格式套接字(Stream Sockets)也叫“面向连接的套接字”,它基于 TCP 协议,在代码中使用 SOCK_STREAM 表示。

数据报格式套接字(SOCK_DGRAM) 数据报格式套接字(Datagram Sockets)也叫“无连接的套接字”,基于 UDP 协议,在代码中使用 SOCK_DGRAM 表示。

TCP与UDP 协议区别与优劣势 TCP 是面向连接的传输协议,建立连接时要经过三次握手,断开连接时要经过四次握手,中间传输数据时也要回复 ACK 包确认,多种机制保证了数据能够正确到达,不会丢失或出错。

UDP 是非连接的传输协议,没有建立连接和断开连接的过程,它只是简单地把数据丢到网络中,也不需要 ACK 包确认。

如果只考虑可靠性,TCP 的确比 UDP 好。

但 UDP 在结构上比 TCP 更加简洁,不会发送 ACK 的应答消息,  也不 会给数据包分配 Seq 序号,所以 UDP 的传输效率有时会比 TCP 高出很多,编程中实现 UDP 也比   TCP 简单。

与 UDP 相比,TCP 的生命在于流控制,这保证了数据传输的正确性。

最后需要说明的是:TCP 的速度无法超越 UDP,但在收发某些类型的数据时有可能接近 UDP。

例如,每次交换的数据量越大,TCP 的传输速率就越接近于 UDP。

分享更多网络底层原理知识点,内容包括Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK等等。

后台私信【架构】获取TCP/IP 协议、HTTP 协议和 Socket 有什么区别?从包含范围来看,它们的继承关系是这样的:从横向来看,它们的继承关系是这样的:关于TCP/IP和HTTP协议的关系,有一段比较容易理解的介绍:  我们在传输数据时,可以只使用(传输层)TCP/IP协议,但是那样的话,如果没有应用层,便无法识别数据内容,如果想要使传输的数据有意义,则必须使用到应用层协议,应用层协议有很多,比如HTTP、FTP、TELNET等,也可以自己定义应用层协议。

WEB使用HTTP协议作应用层协议,以封装HTTP文本信息,然后使用TCP/IP做传输层协议将它发到网络上。

Socket是什么呢,实际上Socket是对TCP/IP协议的封装,Socket本身并不是协议,而是一个调用接口(API),通过Socket,我们才能使用TCP/IP协议。

TCP/IP只是一个协议栈,就像 *** 作系统的运行机制一样,必须要具体实现,同时还要提供对外的 *** 作接口。

这个就像 *** 作系统会提供标准的编程接口,比如win32编程接口一样,TCP/IP也要提供可供程序员做网络开发所用的接口,这就是Socket编程接口。

”TCP/IP 和 HTTP 的数据结构HTTP 作为 TCP/IP 参考模型的应用层,把 HTTP 放到 TCP/IP 参考模型中,它们的继承结构是这样的:在 TCP/IP 参考模型中它们的整体的数据结构是:IP 作为以太网的直接底层,IP 的头部和数据合起来作为以太网的数据,同样的 TCP/UDP 的头部和数据合起来作为 IP 的数据,HTTP 的头部和数据合起来作为 TCP/UDP 的数据。

IP 的数据结构和交互流程我们都知道在一个成功的 HTTP 请求中,服务端可以在一个请求中获取到客户端 IP 地址,也可以获取到客户端请求的主机的 IP 地址。

然而这是怎么做到的呢?这就有赖于 IP 协议了,在 IP 协议中规定了,IP 的头部必须包含源 IP 地址和目的 IP 地址,这也是为什么在 TCP/IP 参考模型中IP 处在网络互联层,其中一个原因就是可以定位服务端地址和客户端地址,我们来看一下 IP 的数据结构:可以很清晰的看到源 IP 地址和目的 IP 地址,在 IP 的头部各占 32 位,而 IPV4 的 IP 地址是用点式十进制表示的,例如:192.168.1.1,在 IP 头部用二进制表示的话,刚好是 4 个字节 32 位。

32 位可以表示的 IP 地址是有限的,使用了 IP 地址转换技术 NAT。

例如 ABC 三个小区的所有设备可能公用了一个公网 IP,通过 NAT 技术分给每一户一个私有 IP 地址,大家在小区内交流时可能使用的是私有 IP 地址,但是向外交流时就用公网 IP。

TCP 的数据结构和交互流程我们通常说的 HTTP 的 3 次握手和 4 次挥手都是由 TCP 来完成的,其实这都没 HTTP 什么事,但是有不少人喜欢这么说,严格来说我们应该说 TCP 的 3 次握手 4 次挥手。

要搞清楚 TCP 的交互流程,首先要清楚 TCP 的数据结构,接下来我们来看一下 TCP 的数据结构:上述 TCP 的数据结构图对于后面理解 HTTP 的交互流程非常重要,我们要记住 5 个关键的位置:SYN:建立连接标识 ACK:响应标识 FIN:断开连接标识 seq:seq number,发送序号 ack:ack number,响应序号服务端应用启动后,会在指定端口监听客户端的连接请求,当客户端尝试创建一个到服务端指定端口的 TCP 连接,服务端收到请求后接受数据并处理完业务后,会向客户端作出响应,客户端收到响应后接受响应数据,然后断开连接,一个完整的请求流程就完成了。

这样的一个完整的 TCP 的生命周期会经历以下 4 个步骤:1,建立 TCP 连接,3 次握手客户端发送SYN, seq=x,进入 SYN_SEND 状态服务端回应SYN, ACK, seq=y, ack=x+1,进入 SYN_RCVD 状态客户端回应ACK, seq=x+1, ack=y+1,进入 ESTABLISHED 状态,服务端收到后进入 ESTABLISHED 状态 2,进行数据传输客户端发送ACK, seq=x+1, ack=y+1, len=m服务端回应ACK, seq=y+1, ack=x+m+1, len=n客户端回应ACK, seq=x+m+1, ack=y+n+13,断开 TCP 连接, 4 次挥手主机 A 发送FIN, ACK, seq=x+m+1, ack=y+n+1,进入 FNI_WAIT_1 状态主机 B 回应ACK, seq=y+n+1, ack=x+m+1,进入 CLOSE_WAIT 状态,主机 A 收到后 进入 FIN_WAIT_2 状态主机 B 发送FIN, ACK, seq=y+n+1, ack=x+m+1,进入 LAST_ACK 状态主机 A 回应ACk, seq=x+m+1, ack=y+n+1,进入 TIME_WAIT 状态,等待主机 B 可能要求重传 ACK 包,主机 B 收到后关闭连接,进入 CLOSED 状态或者要求主机 A 重传 ACK,客户端在一定的时间内没收到主机 B 重传 ACK 包的要求后,断开连接进入 CLOSED 状态为什么TIME_WAIT状态需要经过2MSL(最大报文段生存时间)才能返回到CLOSE状态?虽然按道理,四个报文都发送完毕,我们可以直接进入CLOSE状态了,但是我们必须假设网络是不可靠的,一切都可能发生,比如有可能最后一个ACK丢失。

所以TIME_WAIT状态是用来重发可能丢失的ACK报文。

客户端与服务端建立连接、传输数据和断开连接等全靠这几个标识,比如 SYN 也可以被用来作为 DOS 攻击的一个手段,FIN 可以用来扫描服务端指定端口。

HTTP 的数据结构Socket 是 TCP/IP 的可编程 API,HTTP 的可编程 API 的实现要依赖 Socket。

HTTP 是超文本传输协议,HTTP 的头和数据看起来更加直观,在大多数情况下,它们都是字符或者字符串,所以对于大多数人来说理解 HTTP 的头和数据格式显得很简单。

确实,HTTP 的数据格式理解起来非常容易,上部分是头,下部分是身体。

HTTP 的请求时的数据结构和响应时的数据结构整体上是一样的,但是有一些细微的区别,我们先来看一下 HTTP 请求时的数据结构:HTTP 响应时的数据结构:现在我们使用谷歌浏览器请求某度,按下F12,来对比理解上述结构图,下面是请求某度我们就可以简单的理解 HTTP 的数据结构了。

分享更多网络底层原理知识点,内容包括Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK等等。

后台私信【架构】获取Linux下的socket演示程序下面用最基础的Socket来进行服务端与客户端的交互,让你理解的更为清晰。

接口详解:方法名用途socket():创建socketbind():绑定socket到本地地址和端口,通常由服务端调用listen():TCP专用,开启监听模式accept():TCP专用,服务器等待客户端连接,一般是阻塞态connect():TCP专用,客户端主动连接服务器send():TCP专用,发送数据recv():TCP专用,接收数据sendto():UDP专用,发送数据到指定的IP地址和端口recvfrom():UDP专用,接收数据,返回数据远端的IP地址和端口close():关闭socket基于TCP协议实现CS端使用Socket进行网络通信的过程① 服务器程序将一个套接字绑定到一个特定的端口,并通过此套接字等待和监听客户的连接请求。

② 客户程序根据服务器程序所在的主机和端口号发出连接请求。

③ 如果一切正常,服务器接受连接请求。

并获得一个新的绑定到不同端口地址的套接字。

④ 客户和服务器通过读、写套接字进行通讯。

客户机/服务器模式在TCP/IP网络应用中,通信的两个进程间相互作用的主要模式是客户机/服务器模式*(client/server),即客户像服务其提出请求,服务器接受到请求后,提供相应的服务。

服务器:(1)首先服务器方要先启动,打开一个通信通道并告知本机,它愿意在某一地址和端口上接收客户请求(2)等待客户请求到达该端口(3)接收服务请求,处理该客户请求,服务完成后,关闭此新进程与客户的通信链路,并终止(4)返回第二步,等待另一个客户请求(5)关闭服务器客户方:(1)打开一个通信通道,并连接到服务器所在的主机特定的端口(2)向服务器发送请求,等待并接收应答,继续提出请求(3)请求结束后关闭通信信道并终止具体实现,新建服务端socket_server_tcp.c具体代码如下: socket_server_tcp.c//// Created by android on 19-8-9.//#include <stdio.h>#include <string.h>#include <arpa/inet.h>#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <netinet/ip.h>#define PORT 3040 //端口号#define BACKLOG 5 //最大监听数int main() { int iSocketFD = 0; //socket句柄 int iRecvLen = 0; //接收成功后的返回值 int new_fd = 0; //建立连接后的句柄 char buf[4096] = {0}; // struct sockaddr_in stLocalAddr = {0}; //本地地址信息结构图,下面有具体的属性赋值 struct sockaddr_in stRemoteAddr = {0}; //对方地址信息 socklen_t socklen = 0; iSocketFD = socket(AF_INET, SOCK_STREAM, 0); //建立socket SOCK_STREAM代表以tcp方式进行连接 if (0 > iSocketFD) { printf("创建socket失败!n"); return 0; } stLocalAddr.sin_family = AF_INET; /*该属性表示接收本机或其他机器传输*/ stLocalAddr.sin_port = htons(PORT); /*端口号*/ stLocalAddr.sin_addr.s_addr = htonl(INADDR_ANY); /*IP,括号内容表示本机IP*/ //绑定地址结构体和socket if (0 > bind(iSocketFD, (void *) &stLocalAddr, sizeof(stLocalAddr))) { printf("绑定失败!n"); return 0; } //开启监听 ,第二个参数是最大监听数 if (0 > listen(iSocketFD, BACKLOG)) { printf("监听失败!n"); return 0; } printf("iSocketFD: %dn", iSocketFD); //在这里阻塞知道接收到消息,参数分别是socket句柄,接收到的地址信息以及大小 new_fd = accept(iSocketFD, (void *) &stRemoteAddr, &socklen); if (0 > new_fd) { printf("接收失败!n"); return 0; } else { printf("接收成功!n"); //发送内容,参数分别是连接句柄,内容,大小,其他信息(设为0即可) send(new_fd, "这是服务器接收成功后发回的信息!", sizeof("这是服务器接收成功后发回的信息!"), 0); } printf("new_fd: %dn", new_fd); iRecvLen = recv(new_fd, buf, sizeof(buf), 0); if (0 >= iRecvLen) //对端关闭连接 返回0 { printf("对端关闭连接或者接收失败!n"); } else { printf("buf: %sn", buf); } close(new_fd); close(iSocketFD); return 0;}新建客户端端socket_client_tcp.c socket_client_tcp.c//// Created by android on 19-8-9.//#include <stdio.h>#include <string.h>#include <arpa/inet.h>#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <netinet/ip.h>#define PORT 3040 //目标地址端口号#define ADDR "10.6.191.177" //目标地址IPint main() { int iSocketFD = 0; //socket句柄 unsigned int iRemoteAddr = 0; struct sockaddr_in stRemoteAddr = {0}; //对端,即目标地址信息 socklen_t socklen = 0; char buf[4096] = {0}; //存储接收到的数据 iSocketFD = socket(AF_INET, SOCK_STREAM, 0); //建立socket if (0 > iSocketFD) { printf("创建socket失败!n"); return 0; } stRemoteAddr.sin_family = AF_INET; stRemoteAddr.sin_port = htons(PORT); inet_pton(AF_INET, ADDR, &iRemoteAddr); stRemoteAddr.sin_addr.s_addr = iRemoteAddr; //连接方法: 传入句柄,目标地址,和大小 if (0 > connect(iSocketFD, (void *) &stRemoteAddr, sizeof(stRemoteAddr))) { printf("连接失败!n"); //printf("connect failed:%d",errno);//失败时也可打印errno } else { printf("连接成功!n"); recv(iSocketFD, buf, sizeof(buf), 0); ////将接收数据打入buf,参数分别是句柄,储存处,最大长度,其他信息(设为0即可)。

printf("Received:%sn", buf); } close(iSocketFD);//关闭socket return 0;}下面是我的编译及运行效果:编译命令如下: gcc -o server socket_server_tcp.c gcc -o client socket_client_tcp.c #运行命令 ./server  #首先启动 ./client #次之启动基于UDP协议实现CS端**基于UDP(面向无连接)的socket编程——**数据报式套接字(SOCK_DGRAM) 网络间通信AF_INET,典型的TCP/IP四型模型的通信过程服务器:(多线程的【每10秒会打印一行#号】 与 循环监听) socket_server_udp.c//// Created by android on 19-8-9.//#include <stdio.h>#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <netinet/ip.h>#include <pthread.h>void * test(void *pvData){ while(1) { sleep(5); printf("################################n"); } return NULL;}int main(void){ pthread_t stPid = 0; int iRecvLen = 0; int iSocketFD = 0; char acBuf[4096] = {0}; struct sockaddr_in stLocalAddr = {0}; struct sockaddr_in stRemoteAddr = {0}; socklen_t iRemoteAddrLen = 0; /* 创建socket */ iSocketFD = socket(AF_INET, SOCK_DGRAM, 0); if(iSocketFD < 0) { printf("创建socket失败!n"); return 0; } /* 填写地址 */ stLocalAddr.sin_family = AF_INET; stLocalAddr.sin_port = htons(12345); stLocalAddr.sin_addr.s_addr = 0; /* 绑定地址 */ if(0 > bind(iSocketFD, (void *)&stLocalAddr, sizeof(stLocalAddr))) { printf("绑定地址失败!n"); close(iSocketFD); return 0; } pthread_create(&stPid, NULL, test, NULL); //实现了多线程 while(1) //实现了循环监听 { iRecvLen = recvfrom(iSocketFD, acBuf, sizeof(acBuf), 0, (void *)&stRemoteAddr, &iRemoteAddrLen); printf("iRecvLen: %dn", iRecvLen); printf("acBuf:%sn", acBuf); } close(iSocketFD); return 0;}客户端: socket_client_udp.c//// Created by android on 19-8-9.//#include <stdio.h>#include <string.h>#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <netinet/ip.h>#include <arpa/inet.h>int main(void){ int iRecvLen = 0; int iSocketFD = 0; int iRemotAddr = 0; char acBuf[4096] = {0}; struct sockaddr_in stLocalAddr = {0}; struct sockaddr_in stRemoteAddr = {0}; socklen_t iRemoteAddrLen = 0; /* 创建socket */ iSocketFD = socket(AF_INET, SOCK_DGRAM, 0); if(iSocketFD < 0) { printf("创建socket失败!n"); return 0; } /* 填写服务端地址 */ stLocalAddr.sin_family = AF_INET; stLocalAddr.sin_port = htons(12345); inet_pton(AF_INET, "10.6.191.177", (void *)&iRemotAddr); stLocalAddr.sin_addr.s_addr = iRemotAddr; iRecvLen = sendto(iSocketFD, "这是一个测试字符串", strlen("这是一个测试字符串"), 0, (void *)&stLocalAddr, sizeof(stLocalAddr)); close(iSocketFD); return 0;}测试:1、编译服务器:因为有多线程,所以服务器端进程要进行pthread编译gcc socket_server_udp.c -pthread -g -o server_udp #客户端和上方相同复制代码执行结果如下:右下为客户端重复执行服务器端有主线程和辅线程,主线程,打印客户端发送的请求;辅线程每隔5秒钟打印一排#号。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/tougao/668404.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-19
下一篇 2022-04-19

发表评论

登录后才能评论

评论列表(0条)

保存