socket通信原理(Socket通信原理)

精英怪
广告

什么是Socket套接字?

socket通信原理(Socket通信原理)

socket的中文翻译就是套接字,套接字在英文中的含义是插座。

socket就像是一个电话插座,负责连接两端的童话,进行点对点通信,让电话可以进行通信,端口就是插座上的孔,端口不能同时被其他进程占用,而我们建立连接就像是把插头插在这个插座上,创建一个socket实例开始监听后,这个电话插座就时刻坚挺着消息的传入,谁拨通这个IP地址和端口,我就接通谁。

实际上,socket是在应用层和传输层之间得空一个抽象层,他把tcp/ip层复杂的操作抽象为几个简单的接口,供应用层调用实现进程在网络中的通信,socket起源于UNIX,在UNIX中一切皆文件的思想下,进程间通信就被冠名为文件描述符(file descriptor).socket是一种“打开->读->写->关闭”,模式的实现,服务器和客户端各自维护一个“文件”,在建立连接打开后,可以向文件写入内容供对方读取或者读取对方的内容,通讯结束时关闭文件。

socket的位置

我们可以看出socket其实是一层抽象层,将协议抽象层一层接口对外提供服务。

socket通信过程

socket保证了不同计算机之间的通信,也就是网络通信,对于网站,通信模型是服务器与客户端之间的通信,两端都建立了一个socket对象,然后通过socket对象对数据进行传输,通常服务器处于一个无限循环,等待客户端的链接。

下面是tcp的时序图

客户端过程

客户端的过程比较简单,创建socket,链接服务器,将socket与原厂主机链接(注意:只有tcp才有链接的概念,一些socket比如udp,icmp,arp没有链接的概念),发送数据,读取响应数据,知道数据交换完毕,关闭链接,结束tcp对话。

import socket import sys if __name__ == '__main__': sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 创建 Socket 连接 sock.connect(('127.0.0.1', 8001)) # 连接服务器 while True: data = input('Please input data:') if not data: break try: sock.sendall(data) except socket.error as e: print('Send Failed...', e) sys.exit(0) print('Send Successfully') res = sock.recv(4096) # 获取服务器返回的数据,还可以用 recvfrom()、recv_into() 等 print(res) sock.close()

服务端过程

服务端先初始化socket,建立流式套接字,与本机地址以及端口进行绑定,然后通知TCP,准备好接收链接,调用accept阻塞,等待来自客户端的链接,如果这个时候客户端与服务器建立了链接,客户端发送数据请求,服务器接收请求并处理请求,然后把响应数据发送给客户端,客户端读取数据,知道数据交换完毕,最后关闭连接,交互结束。

import socket import sys if __name__ == '__main__': sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 创建 Socket 连接(TCP) print('Socket Created') try: sock.bind(('127.0.0.1', 8001)) # 配置 Socket,绑定 IP 地址和端口号 except socket.error as e: print('Bind Failed...', e) sys.exit(0) sock.listen(5) # 设置最大允许连接数,各连接和 Server 的通信遵循 FIFO 原则 while True: # 循环轮询 Socket 状态,等待访问 conn, addr = sock.accept() try: conn.settimeout(10) # 如果请求超过 10 秒没有完成,就终止操作 # 如果要同时处理多个连接,则下面的语句块应该用多线程来处理 while True: # 获得一个连接,然后开始循环处理这个连接发送的信息 data = conn.recv(1024) print('Get value ' + data, end='\n\n') if not data: print('Exit Server', end='\n\n') break conn.sendall('OK') # 返回数据 except socket.timeout: # 建立连接后,该连接在设定的时间内没有数据发来,就会引发超时 print('Time out') conn.close() # 当一个连接监听循环退出后,连接可以关掉 sock.close()

conn, addr = sock.accept() 调用 accept() 时,Socket 会进入waiting状态。客户端请求连接时,方法建立连接并返回服务器。accept() 返回一个含有两个元素的元组 (conn, addr)。第一个元素 conn 是新的 Socket 对象,服务器必须通过它与客户端通信;第二个元素 addr 是客户端的 IP 地址及端口。 data = conn.recv(1024) 接下来是处理阶段,服务器和客户端通过 send() 和 recv() 通信(传输数据)。 服务器调用 send(),并采用字符串形式向客户端发送信息,send() 返回已发送的字符个数。 服务器调用 recv() 从客户端接收信息。调用 recv() 时,服务器必须指定一个整数,它对应于可通过本次方法调用来接收的最大数据量。recv() 在接收数据时会进入blocked状态,最后返回一个字符串,用它表示收到的数据。如果发送的数据量超过了 recv() 所允许的,数据会被截短。多余的数据将缓冲于接收端,以后调用 recv() 时,会继续读剩余的字节,如果有多余的数据会从缓冲区删除(以及自上次调用 recv() 以来,客户端可能发送的其它任何数据)。传输结束,服务器调用 Socket 的 close() 关闭连接。

从tcp角度看socket通信过程

三次握手socket过程

1 服务器调用socket(),bind(),listen()完成初始化后,调用accept()阻塞等待;

2 客户端socket对象调用connect()向服务器发送一个SYN并阻塞;

3 服务器完成第一次握手,即发送SYN和ACK应答;

4 客户端收到服务端发送的应答之后,从connect()返回,再发送一个ACK给服务器了;

5 服务器socket对象接收到客户端的第三次握手ACK确认,此时服务器从accept()返回,建立连接;

总结:tcp的三次握手是通过socket的客户端的connect()发送首次ACK,服务端通过accept()接收到第一次握手并发送SYN+ACK应答,此时等待客户端的ACK,接收到accept()就返回了,客户端也在接收到syn 之后发送ack之后connect()函数返回。

接下来就是两个端的连接对象互相收发数据

tcp的四次挥手过程

1 某个应用进程调用close()主动关闭,发送一个FIN;

2 另一端接收到FIN后被动执行关闭,并发送ACK确认;

3 之后被动执行关闭的应用进程调用close()关闭socket,并也发送一个FIN;

4 接收到FIN的一段向另一端发送ACK确认。

总结

上面的代码简单地演示了 Socket 的基本函数使用,其实不管有多复杂的网络程序,这些基本函数都会用到。上面的服务端代码只有处理完一个客户端请求才会去处理下一个客户端的请求,这样的服务器处理能力很弱,而实际中服务器都需要有并发处理能力,为了达到并发处理,服务器就需要 fork 一个新的进程或者线程去处理请求

参考如下文章

https://segmentfault.com/a/1190000013712747

发表评论

快捷回复: 表情:
AddoilApplauseBadlaughBombCoffeeFabulousFacepalmFecesFrownHeyhaInsidiousKeepFightingNoProbPigHeadShockedSinistersmileSlapSocialSweatTolaughWatermelonWittyWowYeahYellowdog
评论列表 (暂无评论,320人围观)

还没有评论,来说两句吧...