Socket网络编程

Socket套接字编程

  1. 什么是网络编程
    网络编程从大的方面说就是对信息的发送与接收, 中间通过物理线路(网线)进行传输.
  2. 为什么要用网络编程
    我们将来写的程序很少是单机程序, 基本上都是要基于网络进行通讯完成客户端与服务端的数据交互. 我们学习网络编程的目的就是学会基于网络进行通讯, 完成数据交换.
  3. 怎么用网络编程
    下面会详细介绍socket套接字编程的用法.

架构模式

  • 硬件C/S架构(打印机)
  • 软件C(client)/S(server)架构
    互联网中处处是C/S架构, 如: 百度服务器是服务端,你的浏览器是客户端(B/S架构也是C/S架构的一种; 腾讯视频作为服务端为你提供视频,你得下个腾讯视频客户端才能看它的视频)
    C/S架构与socket的关系:
    我们学习socket就是为了完成C/S架构的开发

OSI七层协议

我们都知道: 一个完整的计算机系统是由 硬件 / 操作系统 / 应用程序 三部分组成, 具备了这三个条件, 一台计算机系统就可以自己跟自己玩了(打个单机游戏, 玩个蜘蛛纸牌啥的)但是, 如果你要跟别人一起玩, 那你就必须要上网了, 那么什么是互联网呢?
互联网的核心就是一堆协议, 协议就是规定, 就是标准, 如果不按照标准去执行就会出错, 就好比一个国家有法律, 如果你不按照法律去执行, 那么你就会受到处罚. 在互联网中, 如果你不按照标准去执行, 那么你受到的处罚就是你无法连接至互联网.

为何学习socket一定要先学习互联网协议:

  • 首先:我们的目标就是学会如何基于socket编程,来开发一款自己的C/S架构软件
  • 其次:C/S架构的软件(软件属于应用层)是基于网络进行通信的
  • 然后:网络的核心即一堆协议,协议即标准,你想开发一款基于网络通信的软件,就必须遵循这些标准。
    Socket网络编程_第1张图片
    互联网协议是我们使用网络编程必须遵循的一个标准, 我们应该严格按照标准去做. 我们可以通过下面这个链接去详细了解 / 学习互联网协议: 网络基础之网络协议篇(osi七层协议中,我们应该了解的是下三层(物理层 / 数据链路层(以太网协议) / 网络层(IP协议) ), 应该掌握的是传输层(TCP / UDP协议) ).

TCP协议介绍

我们前面说过, 网络编程架构模式: C / S架构也就是服务端与客户端进行通讯, 我们所写出来的是应用程序, 传输层的作用就是为了帮助我们实现应用程序与应用程序之间的通讯.

传输层的由来:网络层的ip帮我们区分子网,以太网层的mac帮我们找到主机,然后大家使用的都是应用程序,你的电脑上可能同时开启qq,暴风影音,等多个应用程序,
那么我们通过ip和mac找到了一台特定的主机,如何标识这台主机上的应用程序,答案就是端口,端口即应用程序与网卡关联的编号。
传输层功能:建立端口到端口的通信
补充:端口范围0-65535,0-1023为系统占用端口

TCP协议:
又称为可靠传输,流式协议,TCP数据包没有长度限制,理论上可以无限长,但是为了保证网络的效率,通常TCP数据包的长度不会超过IP数据包的长度,以确保单个TCP数据包不必再分割。
通过osi前四层协议, 我们可以定位到唯一的一款软件(ip和mac地址可以定位到唯一一台计算机, 端口号可以定位到计算机上的唯一一个应用程序), 那么TCP协议是如何实现应用程序与应用程序之间的连接去进行数据交互的呢?

重点:

1. TCP协议三次握手建立连接

Socket网络编程_第2张图片

  • 连接准备:
    前提: 服务端进入listen状态
  • 第一次握手:
    客户端向服务端发起连接请求(syn)顺带会发送一个序列(seq = x),这个序列的用处咱们暂时不要管它, 下面我们会进行详细的介绍. 这时客户端会进入一个syn_sent状态.
    服务器收到客户端的连接请求, 进入syn_rcvd状态.
    至此, 第一次握手完成.
  • 第二次握手:
    服务端会给客户端发送响应消息(ack = 1 + x)同意客户端的连接请求. 与此同时, 服务端会给客户端发送一个连接请求(syn)以及一个序列(seq = y).
    客户端收到服务端的响应(ack = 1 + x), 这时客户端会进入established状态, , 客户端通向服务端的连接成功建立.
    第二次握手完成.
  • 第三次握手:
    客户端收到服务器响应的同时, 收到了服务端发来的请求连接的消息, 这时, 客户端会给服务端发送响应消息(ack = 1 + y).
    服务器收到客户端的响应(ack = 1 + y), 服务端进入established状态, 服务端通向客户端的连接成功建立.
    三次握手结束, 连接建立成功.
  • 发送syn的同时为何要发送一个序列
    发送序列的目的是保证数据的统一性. 如果没有这个序列, 我无法知道你给我发送的响应消息是响应我哪个请求, 就好比我们说话有时候我说了两句, 对方回了一句, 我们可以从字面意思去理解到他回应我的是我说的第一句话还是第二句话, 如果我们对这个消息加一个限制条件(序列), 我们就可以清楚的知道对方回应的是我说的哪句话. 我们可以看到每次回应ack时都是序列+1, 拿ack = x + 1来说这就代表着我回应的是你序列为x的那条消息.
    (如果osi七层协议有不明白的地方可以加我QQ: 526772254详细了解.)
    连接建立成功以后就可以进行通讯了, 当结束通讯要断开连接时, 需要经历四次挥手断开连接

2. TCP协议四次挥手断开连接

前提: 一般是由服务端发起断开连接请求. (原因: 服务端需要承载大量的客户端, 所以事情一干完就要断开连接释放自己的压力. )服务端干完事情的时候, 客户端不一定干完事情了!

  • 第一次挥手
    服务端向客户端发起断开连接请求(fin)顺带着发送序列(seq = x), 此时服务端进入fin_wait_1阶段.
    客户端接收到服务端发来的断开连接请求, 进入close_wait阶段.
    第一次挥手完成
  • 第二次挥手
    客户端接收到服务端发来的请求以后, 向服务端发送确认信息(ack = 1 + x)
    服务端收到客户端发来的确认信息, 进入fin_wait_2阶段.
    第二次挥手完成
  • 第三次挥手
    等待客户端将数据传输完毕后, 发送断开连接请求(fin)同时发送序列(seq = y)进入last_ack阶段
    服务端收到客户端发来的断开连接请求, 进入time_wait阶段.
    第三次挥手完成
  • 第四次挥手
    服务器向客户端发送确认信息(ack = 1 + y)
    客户端收到信息
    四次挥手完成, 连接断开

UDP协议介绍

UDP协议:
又称为不可靠传输,”报头”部分一共只有8个字节,总长度不超过65,535字节,正好放进一个IP数据包。
UDP协议客户端与服务端无须建立连接, 数据发送全靠缘分(开玩笑), 因为其独特的无连接模式, 其发送数据以后无须等待对方发送响应信息, 这样引出了一个好处就是它的传输速度特别快, 但是相应的数据也容易丢失.

TCP与UDP的区别:

  1. UDP协议
    数据报协议
    特点:无连接, 一发对应一收
    优点:发送效率高,但有效传输的数据量最多为500bytes
    缺点:不可靠:发送数据,无需对确认,容易丢包
  2. TCP协议
    可靠协议/ 流式协议
    特点: 有连接, 收发之间无直接对应关系
    优点: 数据安全可靠
    缺点: 传输数据效率低

为什么TCP可靠而UDP不可靠呢?
有些人上来就会说, 因为TCP协议有连接. 这是不对的, TCP协议每次发送数据以后, 都会等待对方回应ack, 如果一段时间没有收到对方回应的ack, TCP协议会尝试再次发送数据, 直到尝试多次对方都没有响应ack(对方已下线)或者收到ack确认信息以后, TCP才会停止发送这份数据. 这就是TCP协议是可靠协议的原因

Socket是什么

Socket是应用层与TCP/IP协议层通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。
所以,我们无需深入理解tcp/udp协议,socket已经为我们封装好了,我们只需要遵循socket的规定去编程,写出的程序自然就是遵循tcp/udp标准的。
工作流程:
Socket网络编程_第3张图片

先从服务器端说起。服务器端先初始化Socket,然后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端连接。在这时如果有个客户端初始化一个Socket,然后连接服务器(connect),如果连接成功,这时客户端与服务器端的连接就建立了。客户端发送数据请求,服务器端接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据,最后关闭连接,一次交互结束

# 基于TCP协议的C/S架构模式通讯
# 服务端
import socket

phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # 买电话
phone.bind(('127.0.0.1', 8081))  # 插手机卡,补充:0-65535 0-1024给系统用的. 这里传入的必须是一个元组, 第一个元素为ip地址, 第二个元素为端口号
phone.listen(5)  # 开机

print('start...')
conn, client_addr = phone.accept()  # 等电话连接
print('连接来了:', conn, client_addr)

# 收发消息
msg = conn.recv(1024)  # 收消息,1024是一个最大的限制
print('客户端的消息: ', msg)
conn.send(msg + b'SB')

# 挂电话
conn.close()
# 关机
phone.close()

#############################################################

# 客户端
import socket

phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # 买电话
phone.connect(('127.0.0.1', 8081))  # 拨电话,地址为服务端的ip和端口

phone.send('你好'.encode('utf-8'))  # 发消息b'hello'
data = phone.recv(1024)  # 收消息

print(data.decode('utf-8'))

phone.close()

# 基于UDP的C/S架构通讯
# 服务端
import socket

server=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # 数据报协议
server.bind(('127.0.0.1',8080))


client_data,client_addr=server.recvfrom(1024)
msg=input('回复%s:%s>>>:' %(client_addr[0],client_addr[1]))
server.sendto(msg.encode('utf-8'),client_addr)

#############################################################

# 客户端
import socket

client=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # 数据报协议


msg=input('>>>: ').strip()
client.sendto(msg.encode('utf-8'),('127.0.0.1',8080))
res,server_addr=client.recvfrom(1024)
print(res.decode('utf-8'))

socket主要函数

  • 服务端套接字函数
    s.bind() 绑定(主机,端口号)到套接字
    s.listen() 开始TCP监听
    s.accept() 被动接受TCP客户的连接,(阻塞式)等待连接的到来

  • 客户端套接字函数
    s.connect() 主动初始化TCP服务器连接
    s.connect_ex() connect()函数的扩展版本,出错时返回出错码,而不是抛出异常

  • 公共用途的套接字函数
    s.recv() 接收TCP数据
    s.send() 发送TCP数据(send在待发送数据量大于己端缓存区剩余空间时,数据丢失,不会发完)
    s.sendall() 发送完整的TCP数据(本质就是循环调用send,sendall在待发送数据量大于己端缓存区剩余空间时,数据不丢失,循环调用send直到发完)
    s.recvfrom() 接收UDP数据
    s.sendto() 发送UDP数据
    s.getpeername() 连接到当前套接字的远端的地址
    s.getsockname() 当前套接字的地址
    s.getsockopt() 返回指定套接字的参数
    s.setsockopt() 设置指定套接字的参数
    s.close() 关闭套接字

  • 面向锁的套接字方法
    s.setblocking() 设置套接字的阻塞与非阻塞模式
    s.settimeout() 设置阻塞套接字操作的超时时间
    s.gettimeout() 得到阻塞套接字操作的超时时间

  • 面向文件的套接字的函数
    s.fileno() 套接字的文件描述符
    s.makefile() 创建一个与该套接字相关的文件

关于listen()函数
在TCP协议中:
listen函数接收一个参数, 该参数的目的是限制最大的半连接数.
注意: 半连接数和可以接收的最大连接数是两个概念!
半连接的意思是指: 在TCP协议中, 服务端与客户端要经过三次握手建立两个通道, 当只建立了客户端通向服务端的通道时, 叫做半连接状态, 我们的listen限制的就是这种状态的最大连接数.

你可能感兴趣的:(python,socket,网络编程,osi七层协议,tcp协议)