0. 大纲:
网络编程三要素: IP地址、端口、协议、3次握手、4次挥手;
Socket套接字与TCP开发流程 :服务器端程序流程、客户端程序流程;
TCP编程:服务器端程序、客户端程序编写;
1. 网络编程三要素: IP、端口、协议
1.1 TCP协议 和 UDP协议:
TCP提供一种面向连接的、可靠的字节流服务。
① TCP是面向连接的 ;UDP是无连接的即发送数据前不需要建立连接;
② TCP提供可靠的服务 ,也就是说,通过 TCP连接传输的数据不会丢失、没有重复,并且按顺序到达;UDP没有可靠性;
③ TCP是面向字节流的,实际上是 TCP把数据看成一连串无结构的字节流;UDP是面向报文的。
1.2 TCP建立连接三次握手:
三次握手 就是指建立TCP连接 时,需要客户端和服务端总共发送3个包以确认连接的建立。
第一次握手 : 客户端向服务端发送请求,等待服务端确认;
第二次握手 : 服务端收到请求后知道客户端请求建立连接,回复给客户端以确认连接请求 ;
第三次握手: 客户端收到确认后 ,再次发送请求给服务端,服务端收到正确请求后,如果正确则连接建立成功,完成三次握手,随后客户端与服务端之间可以开始传输数据了。
(详述 :第一次握手 : 建立连接时,客户端发送 syn包(syn=j)到服务器,并进入 SYN=SENT状态,等待服务器确认;(SYN:同步序列编号 Synchronize Sequence Numbers)
第二次握手 : 服务器收到 syn包,必须确认客户的 SYN(ack=j+1),同时自己也发送一个 SYN包(SYN=k),即 SYN+ACK 包,此时服务器进入 SYN_RECV状态;
第三次握手 : 客户端收到服务器的 SYN+ACK 包,向服务器发送确认包 ACK(ack=k+1),此时包发送完毕,客户端和服务器进入 ESTABLISHED(TCP连接成功)状态,完成第三次握手;
完成三次握手,客户端与服务器开始传送数据。)(下图红线指的是三次握手建立连接)
1.3 TCP断开连接四次挥手:
四次挥手 是指断开TCP链接 时需要经过4次确认。TCP连接是双向,A连接B、B连接A都要断开 。
第一次挥手 : 当主机A(可以是客户端也可以是服务端)完成数据传输后,提出停止TCP连接的请求;
第二次挥手 : 主机B收到请求后对其作出响应,确认这一方向上的TCP连接 将关闭;
第三次挥手 : 主机B端再提出反方向的连接 关闭请求;
第四次挥手 : 主机A对主机B的请求进行确认,双方向的关闭结束。(黑色线指四次挥手断开链接)

2. Socket套接字 与 TCP开发流程
2.1 Socket套接字:
网络编程 :也叫网络通信,Socket通信,即:通信 双方都独有自己的Socket对象 ,数据在Socket之间通过 数据报包(UDP协议) 或者 字节流(TCP协议) 的形式进行传输。
①定义:socket(简称套接字)是进程之间通信一个工具 ,好比现实生活中的插座,所有的家用电器要想工作都是基于插座进行,而进程之间进行网络通信需要基于socket 。
②语法:socket(套接字)能实现不同主机之间的进程间通信。Python中有专门的socket类:import socket;要使用socket,则通常要使用到socket模块下的socket类创建socket对象:tcp_socket= socket.socket(socket.AF_INET, socket.SOCK_STREAM)
| 函数名/参数 | 含义 |
|---|---|
socket(AddressFamily, Type) |
用于创建一个socket对象。其中,参数AddressFamily(地址族)可以选择AF_INET 用于Internet进程间通信,实际工作中常用它; 参数Type表示套接字类型,可以是SOCK_STREAM流式套接字,主要用于TCP协议。 |
参数1:AddressFamily |
地址族 。即:Ipv4 还是 Ipv6 ;默认值:AF_INET(Ipv4);AF_INET6(Ipv6) |
参数2:Type |
通信方式 。socket类型;即TCP 还是 UDP ;默认值:SOCK_STREAM(TCP);SOCK_DGRAM(UDP) |
③示例:
python
#演示socket对象的创建
import socket #导入模块
#创建socket对象
tcp_socket= socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#输出内容
print(tcp_socket)
#运行结果:
# <socket.socket fd=3, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('0.0.0.0', 0)>
2.2 TCP开发流程:
服务器端程序流程、客户端程序流程图:

详述: socket()服务器端有socket,客户端也有socket;多个客户端对应一个服务器端,bind()绑定服务端的IP和端口号(以元组的形式),即服务器端指定了IP和端口;客户端connect()时根据此IP和端口连接;listen()最大监听数:允许最多有多少人等待即多个客户端同时申请的情况下,服务器端最多只能服务几个,如单线程的则服务一个,如5个则最多5个客户端连接,第6个过来需要等待连接;accept()等待监听即如果没有客户端与服务端连接则等待(如 开店老板没有顾客时等待),会返回一个元组:(元组的第一个参数是 负责和客户端交互的socket , 第二个参数是 客户端信息 ),(和客户端交互的socket 的理解:服务端socket()的socket是店主,当客户端socket()的socket来交互时,由accept()返回的元组的第一个参数 和客户端交互的socket 的socket与客户端的socket进行交互的,并不是由服务端socket()的socket与之交互的,因为(店主)它的职责是负责等待,真正交互的不是它);一直等到connect()连接;send()发送数据;recv()接收数据:客户端发服务端收,以及服务端发客户端收;客户端close()关闭客户端,服务端close()关闭的是与客户端交互的socket、不是socket()的socket (关闭接待完的店员,不是关店);
TCP服务器端: 
TCP客户端:

代码:

server_socket.py
python
"""
网编案例:服务端给客户端发送消息,客户端给服务端回执信息
服务器端开发流程:
1.创建服务端socket对象
2.绑定IP和端口号
3.设置最大监听数
4.等待客户端申请建立连接
5.给客户端发送消息
6.接收客户端的信息并打印
7.释放资源
注意:客户端与服务端是通过 字节流(bytes) 的形式实现的!
"""
#导包
import socket
# 1.创建服务端socket对象:ipv4,字节流(TCP)
server_socket=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2.绑定IP和端口号
server_socket.bind(('127.0.0.1',8080))
# 3.设置最大监听数
server_socket.listen(5)
# 4.等待客户端申请建立连接
print(111)
accept_socket,client_info=server_socket.accept()
# 5.给客户端发送消息
accept_socket.send(b'Welcome to Python')
# 6.接收客户端的信息并打印
data=accept_socket.recv(1024).decode('utf-8')
print(f"服务器端收到来自{client_info}的数据:"+data)
# 7.释放资源
accept_socket.close()
# server_socket.close() # 服务器端一般不关闭
client_socket.py
python
"""
网编案例:服务端给客户端发送消息,客户端给服务端回执信息
客户端开发流程:
1.创建客户端socket对象
2.连接服务器端,指定:服务器端IP、端口号
3.接收服务端的信息并打印
4.给服务器端发送消息
5.释放资源
注意:客户端与服务端是通过 字节流(bytes) 的形式实现的!
"""
#导包
import socket
# 1.创建客户端socket对象:ipv4,字节流(TCP)
client_socket=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2.连接服务器端,指定:服务器端IP、端口号
client_socket.connect(('127.0.0.1', 8080))
# 3.接收服务端的信息并打印
data=client_socket.recv(1024).decode('utf-8')
print(f"客户端收到数据:"+data)
# 4.给服务器端发送消息
client_socket.send("Socket很有趣,很喜欢!".encode('utf-8'))
# 5.释放资源
client_socket.close()
2.3 字符串 str 与二进制 bytes 类型转换:
在网络中,数据是以二进制数据类型bytes的形式进行传递 的,所以在我们向网络传输数据的时候需要把数据转化成二进制,从网络中接受到的数据默认也是二进制类型的数据,想要正常使用 这些数据也需要 把这些数据从二进制类型数据转为字符串str型 。
①字符串str数据转换为二进制bytes类型 时,可使用如下函数:
| encode(encoding) | 用于把字符串编码转换为二进制数据,其中encoding表示编码格式,常设置为utf-8 |
|---|
②二进制bytes数据转换为字符串str类型 时,可使用如下函数:
| decode(encoding) | 用于把二进制数据解码转换为宇符串,其中encoding表示编码格式,常设置为utf-8 |
|---|
③说明:
1.编码(encode) 是把我们看懂的转成看不懂的:字符串.encode(码表);(不写默认utf-8编码)
2.解码(decode) 是把我们看不懂的转成看懂的:二进制.decode(码表);(不写默认utf-8编码)
3.只要是乱码了,原因一定是编解码不同;
4.数字、字母、特殊符号无论什么码表都只占一个字节,中文在gbk中占两个字节、utf-8中占三个字节;
5.二进制特殊写法:b'数字/ 字母/ 特殊符号',但该方式针对中文无效;
④代码示例:
python
# 1.编码
s1="aB啦2@#"
print(s1.encode()) #运行结果:b'aB\xe5\x95\xa62@#'
print(s1.encode('utf-8'))#运行结果:b'aB\xe5\x95\xa62@#'
print(s1.encode('gbk')) #运行结果:b'aB\xc0\xb22@#'
# 2.解码
bys=b'aB\xe5\x95\xa62@#'
print(type(bys)) #运行结果:<class 'bytes'>
print(bys.decode()) #运行结果:aB啦2@#
print(bys.decode('utf-8')) #运行结果:aB啦2@#
print(bys.decode('gbk')) #运行结果:报错