程序员社区

Socket是如何通信的

如果你学过编程语言,一定接触过socket套接字,即使你不懂tcp/ip协议,你也可以用socket通过网络发送和接收数据

Socket

socket

套接字,对网络中不同主机上的应用进程之间进行双方通信的端点的抽象,提供了应用层进程利用网络协议交换数据的机制

套接字向上连接应用程序,向下连接网络协议栈,是应用程序通过网络协议进行通信的接口,是应用程序与网络协议进行交互的接口

为什么要有socket?

如果没有socket,应用程序的程序员要精通底层硬件操作和网络通信底层的协议,应用程序需要和网络协议、硬件驱动直接打交道,要在程序里实现非常多的代码来驱动协议和硬件,这不是一个科学的流程

操作系统把协议和这些底层的东西打包起来,形成一个方便的接口,你只需要调用这个接口,就能实现协议栈以下的功能,这就是socket的好处

TcpSocket流程

Socket是如何通信的插图

  • Server端应用程序创建socket,调用Bind(ip, port)函数绑定自己的地址和端口号,随后调用Listen()函数监听在(ip, port)上,等待Client连接请求

  • Client端应用程序创建socket,调用Bind(ip, port)函数绑定自己的地址和端口号,然后调用Connect(dstIP, dstPort)驱动协议栈进行tcp的连接请求

  • Server端调用Accept()函数驱动协议栈对tcp连接响应,实现三次握手

  • Client和Server在连接成功后,通过Send(Data)和Recv()函数实现数据的发送和接收

  • 数据传输完后,Client调用Close()函数关闭连接,释放端口号等资源

Listen()函数是一直在监听,Accept()如果没有连接请求,会一直阻塞在这里,Connect()和Accept()是tcp连接建立后,才算是执行完毕

UdpSocket的流程没有握手连接,也就没有Connect()、Listen()和Accept()

socket选项

套接字是应用层用来驱动协议栈和驱动层工作的接口,有时可能需要对下层协议头部中的字段进行赋值或者获取,比如说IP层的TTL值,或者TCP层的MSS值,或者快速重用端口,这些都是通过对套接字对象进行选项设置或者获取来完成

socket对象如果想设置option,首先需要调用正确的方法,capl中有SetSocketOption(),python中有setsockopt(),传入的参数有三个level、option、value,level是定义的协议层或socket应用本身,option是定义的协议层的某个字段或某个功能,value是选项的值

socket对象也可以获取option的值,也需要调用正确的方法,capl中有GetSocketOption(), python中有getsockopt()

网络层

以capl为例,网络层可设置的选项有哪些

IPv4

Socket是如何通信的插图1

这里面最常用的应该是IP_TTL,设置或者获取网络层IPv4协议头部中的TTL的值

IPv6

Socket是如何通信的插图2

传输层

TCP

Socket是如何通信的插图3

这里面最常用的应该是TCP_MAXSEG,设置或者获取TCP协议的MSS的值

UDP

Socket是如何通信的插图4

可以把UDP checksum设置为0

socket

socket自己也可以设置选项

Socket是如何通信的插图5

SO_REUSEADDR

一个(ip, port)只允许一个socket绑定并使用的,如果别的socket想使用,原先的socket必须调用close()函数断开连接并释放端口资源。对于tcp来说,断开连接的最后一步发ACK后需要等待2MSL = 2TTL,可能这个时间还没到,脚本里就已经有socket重用这个端口了,对socket设置端口重用选项,就可以避免端口还处于上一个连接的2MSL阶段而无法使用的问题

SO_KEEPALIVE

实现在服务器侧,缺省超时时间120分钟,SO_KEEPALIVE实现在TCP协议栈,只要TCP连接没有任何数据、控制字传输,定时器就开始启动,超时后服务器会给客户端发SO_KEEPALIVE,来检测连接是否存活,如果N次重传keepalive,客户端没有响应,则服务器删除连接,释放资源。如果有数据传输,会不停刷新定时器,重新走表
这个选项是为了防止客户端没有发FIN主动断开而出现的客户端关机等,造成服务器会有很多不可用的TCP连接,从而占用资源

SO_BROADCAST

通过在socket层设置广播选项发送广播报文,只能用在UDP

SO_SNDBUF

设置发送缓存区大小

SO_RCVBUF

设置接收缓存区大小

附上python中要如何使用的代码

import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #ipv4, tcp
sock.bind(('127.0.0.1', 5001))
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) #端口重用  

python如何新建socket

客户端

import socket

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 建立连接:
s.connect(('127.0.0.1', 3333))
# 接收欢迎消息:
print(s.recv(1024).decode('utf-8'))
for data in [b'Michael', b'Tracy', b'Sarah']:
    # 发送数据:
    s.send(data)
    print(s.recv(1024).decode('utf-8'))         
s.send(b'exit')
s.close()

服务器

import socket
import time
import threading

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

s.bind(('127.0.0.1', 3333))

s.listen(6)
print('Waiting for connection...')

def tcplink(sock, addr):
    print('Accept new connection from %s:%s...' % addr)
    sock.send(b'Welcome!')
    while True:
        data = sock.recv(1024)
        time.sleep(1)
        if not data or data.decode('utf-8') == 'exit':
            break
        sock.send(('Hello, %s!' % data.decode('utf-8')).encode('utf-8'))
    sock.close()
    print('Connection from %s:%s closed.' % addr)
    
while True:
    # 接受一个新连接:
    sock, addr = s.accept()
    # 创建新线程来处理TCP连接:
    t = threading.Thread(target=tcplink, args=(sock, addr))
    t.start()  

以上代码来自廖雪峰官网


赞(0) 打赏
未经允许不得转载:IDEA激活码 » Socket是如何通信的

相关推荐

  • 暂无文章

一个分享Java & Python知识的社区