网络编程就是如何在程序中实现两台计算机的通信。
一、IP地址、端口
1.IP地址
IP地址就像是我们的家庭住址一样,如果你要写信给一个人,你就要知道他(她)的地址,这样邮递员才能把信送到。计算机发送信息就好比是邮递员,它必须知道唯一的“家庭地址”才能不至于把信送错人家。只不过我们的地址是用文字来表示的,计算机的地址用二进制数字表示。 IP地址被用来给Internet上的电脑一个编号。
2.端口的概念
- IP地址好比每个人的地址(门牌号),端口好比是房间号。必须同时指定IP地址和端口号才能够正确的发送数据。
- IP地址好比为电话号码,而端口号就好比为分机号。
要注意:
端口是虚拟的概念,并不是说在主机上真的有若干个端口。通过端口,可以在一个主机上运行多个网络应用程序。
按端口号分类:
公认端口(Well Known Ports):从0到1023,它们紧密绑定(binding)于一些服务。通常这些端口的通讯明确表明了某种服务的协议。例如:80端口实际上总是HTTP通讯。
注册端口(Registered Ports):从1024到65535。它们松散地绑定于一些服务。也就是说有许多服务绑定于这些端口,这些端口同样用于许多其它目的。例如:许多系统处理动态端口从1024左右开始。
二、TCP、UDP协议
TCP 和 UDP 的优缺点无法简单地、绝对地去做比较:TCP 用于在传输层有必要实现可靠传输的情况;UDP 主要用于那些对高速传输和实时性有较高要求的通信或广播通信。TCP 和 UDP 应该根据应用的目的按需使用。
1.TCP
TCP方式就类似于拨打电话,使用该种方式进行网络通讯时,需要建立专门的虚拟连接,然后进行可靠的数据传输,如果数据发送失败,则客户端会自动重发该数据。
2.UDP
UDP方式就类似于发送短信,使用这种方式进行网络通讯时,不需要建立专门的虚拟连接,传输也不是很可靠,如果发送失败则客户端无法获得。
UDP 因为没有拥塞控制,一直会以恒定的速度发送数据。即使网络条件不好,也不会对发送速率进行调整。这样实现的弊端就是在网络条件不好的情况下可能会导致丢包,但是优点也很明显,在某些实时性要求高的场景(比如电话会议)就需要使用 UDP 而不是 TCP
3.总结
- TCP是面向连接的,传输数据安全,稳定,效率相对较低。
- UDP是面向无连接的,传输数据不安全,效率较高。
三、TCP的三次握手、四次挥手
1.三次握手
TCP 的三次握手:就像 “打电话确认接通”
想象你给朋友打电话,要确认双方都能正常沟通,步骤是这样的:
-
你打过去(第一次握手):
你拨号后说:“喂,能听到吗?”(发送连接请求:“我想连你,你准备好了吗?”)。 -
朋友回应(第二次握手):
朋友听到后说:“能听到!你能听到我吗?”(回应请求:“我准备好了,你能收到我的回复吗?”)。 -
你再确认(第三次握手):
你听到后说:“能听到!那我们开始聊吧~”(确认收到回应:“我收到了,现在可以正式通信了!”)。
三次握手结束后,你们俩就确认了 “双向都能收发消息”,通话(连接)正式建立。
2.四次挥手
TCP 的四次挥手:就像 “挂电话前说再见”
通话结束后,要礼貌地结束连接,确保双方都准备好断开,步骤是这样的:
-
你说 “我要挂了”(第一次挥手):
你说:“我没话说了,准备挂电话啦~”(发送断开请求:“我这边数据发完了,想断开连接。”)。 -
朋友说 “收到,我确认下”(第二次挥手):
朋友说:“好的,我知道你要挂了,我看看还有没有没说完的……”(回应请求:“我收到你的断开请求了,等我处理完剩余数据。”)。 -
朋友说 “我也说完了,挂吧”(第三次挥手):
朋友处理完后说:“我也没话说了,你可以挂了~”(发送断开通知:“我这边数据也发完了,准备断开。”)。 -
你说 “好的,挂了”(第四次挥手):
你说:“收到!那我挂啦~”(确认通知:“我收到你的断开通知了,断开连接吧。”)。
四次挥手结束后,双方确认 “都没有数据要发了”,通话(连接)正式断开。
四、socket()套接字编程
1.socket()介绍
Socket编程封装了常见的TCP、UDP操作,可以实现非常方便的网络编程。
socket套接字编程表示‘打开了一个网络’,socket 就是网络程序之间的 “通信通道”,语法为:
socket.socket(family[,type[,proto]])
① family:是套接字家族,可以理解为:通信方式的分类,可以用AF_UNIX或者AF_INET
AF_INET就像是发快递,用来处理‘跨网络通信’,需要‘IP+端口号’
AF_UNIX就像是和同桌传纸条,负责同一台电脑内部的程序之间通信
② type:是是套接字类型,分为SOCK_STREAM和SOCK_DGRAM
SOCK_STREAM:TCP--快但是不稳
SOCK_DGRAM:UDP--慢但是稍稳
③ protoclo:一般不填,默认为 0
2.服务器端的套接字函数
① s.bind()
绑定地址到套接字,用元组的形式
② s.listen()
开始TCP监听。backlog指定在拒绝连接之前,操作系统可以挂起的最大连接数量。该值至少为1,大部分应用程序设为5就可以了。
③ s.accept()
被动接受TCP客户端连接,(阻塞式)等待连接的到来
3.客户端套接字函数
s.connect()
主动初始化TCP服务器连接,。一般address的格式为元组
4.公共用途的常用套接字函数
(一)TCP用的
① s.recv()
接收TCP数据,数据以字符串形式返回
② s.send()
发送TCP数据
(二)UDP用的
① s.recvfrom()
接收UDP数据,与recv()类似
② s.sendto()
发送UDP数据,将数据发送到套接字
五、UDP编程
1.服务器接收数据最简单的代码例子
'''
UDP编程时,不需要建立连接,只需要知道对方的IP地址和端口号,就可以直接发送数据包
UDP编程数据传输不可靠,但是传输速度很快
socket套接字编程表示‘打开了一个网络’,socket 就是网络程序之间的 “通信通道”,语法为:
socket.socket(family[,type[,proto]])
family:是套接字家族,可以理解为:通信方式的分类,可以用AF_UNIX或者AF_INET
AF_INET就像是发快递,用来处理‘跨网络通信’,需要‘IP+端口号’
AF_UNIX就像是和同桌传纸条,负责同一台电脑内部的程序之间通信
type:是是套接字类型,分为SOCK_STREAM和SOCK_DGRAM
SOCK_STREAM:TCP--快但是不稳
SOCK_DGRAM:UDP--慢但是稍稳
protoclo:一般不填,默认为 0
'''
#UDP接受数据
from socket import *
s=socket(AF_INET,SOCK_DGRAM) #创建了套接字
#绑定接收信息的端口
s.bind(('127.0.0.1',8888)) #这是在绑定端口,IP地址和端口号,要用元组的形式
print("等待接收数据!")
redata=s.recvfrom(1024) #1024表示本次接受的最大字节数
#recvfrom()用于接收UDP数据,这个方法会阻塞程序执行,直到收到数据为止
#返回的是一个元组,包含收到的数据和发送方的地址信息
print(redata)
print(f"收到远程信息:{redata[0].decode("gbk")},from{redata[1]}")
#redata[0]是接收到的原始字节数据,redata[1]是发送方的地址信息,格式为(IP地址,端口号)
#decode('gbk')将字节数据解码为字符串
s.close() #操作完成后关闭套接字,释放资源
2.客户端发送数据最简单的例子
from socket import *
s=socket(AF_INET,SOCK_DGRAM) #创建套接字
addr=('127.0.0.1',8888) #准备接收方的地址
data=input("请输入...")
#发送数据
s.sendto(data.encode("gbk"),addr)
#发送UDP数据,将数据发送到套接字,address是形式为(ipaddr,port)的元组,指定远程地址。
s.close()
3.服务器持续接收数据的例子
from socket import *
#创建套接字
s=socket(AF_INET,SOCK_DGRAM)
#绑定接受数据的端口
s.bind(('127.0.0.1',8800)) #绑定端口要用元组的形式
print("等待接收数据!")
while True:
# 接收数据,返回元组
redata=s.recvfrom(1024)
recv_content=redata[0].decode('gbk')
print(f"收到远程信息:{recv_content},from{redata[1]}")
if recv_content=="88":
print("聊天结束!")
break
s.close()
4.客户端持续发送数据的例子
from socket import *
#创建套接字
s=socket(AF_INET,SOCK_DGRAM)
#创建接受的地址(IP地址,端口)
addr=('127.0.0.1',8800)
while True:
#创建发送的内容
data=input("请输入...")
s.sendto(data.encode('gbk'),addr)
if data == '88':
print('聊天结束!')
break
s.close()
5.多线程服务端自由通信
from socket import *
from threading import Thread
def recv_data():
while True:
# 接收数据,返回元组
redata = s.recvfrom(1024)
recv_content = redata[0].decode('gbk')
print(f"收到远程信息:{recv_content},from{redata[1]}")
if recv_content == "88":
print("聊天结束!")
break
def send_data():
# 创建接受的地址(IP地址,端口)
addr = ('127.0.0.1', 8800)
while True:
# 创建发送的内容
data = input("请输入...")
s.sendto(data.encode('gbk'), addr)
if data == '88':
print('聊天结束!')
break
if __name__=='__main__':
# 创建套接字
s = socket(AF_INET, SOCK_DGRAM)
# 绑定接受数据的端口
s.bind(('127.0.0.1', 9900)) # 绑定端口要用元组的形式
#创建线程
t1=Thread(target=recv_data)
t2=Thread(target=send_data)
t1.start()
t2.start()
t1.join()
t2.join()
6.多线程实现客户端自由通信
from socket import *
from threading import Thread
def recv_data():
while True:
# 接收数据,返回元组
redata = s.recvfrom(1024)
recv_content = redata[0].decode('gbk')
print(f"收到远程信息:{recv_content},from{redata[1]}")
if recv_content == "88":
print("聊天结束!")
break
def send_data():
# 创建接受的地址(IP地址,端口)
addr = ('127.0.0.1', 9900)
while True:
# 创建发送的内容
data = input("请输入...")
s.sendto(data.encode('gbk'), addr)
if data == '88':
print('聊天结束!')
break
if __name__=='__main__':
# 创建套接字
s = socket(AF_INET, SOCK_DGRAM)
# 绑定接受数据的端口
s.bind(('127.0.0.1', 8800)) # 绑定端口要用元组的形式
#创建线程
t1=Thread(target=recv_data)
t2=Thread(target=send_data)
t1.start()
t2.start()
t1.join()
t2.join()
六、TCP编程
1.服务端简化代码
from socket import *
#创建TCP套接字
server_socket=socket(AF_INET,SOCK_STREAM)
#绑定地址和端口
server_socket.bind(('127.0.0.1',8899)) #本机监听8899端口
server_socket.listen(5) #让套接字进入监听状态,等待客户端的连接请求,5表示最大挂起的连接数
print("等待客户端连接!")
client_socket,client_info=server_socket.accept()
'''
accept() 方法会返回两个值
client_socket是一个新创建的套接字对象,专门用于和刚刚连接到服务器的特定客户端进行通信
当服务器通过 server_socket.listen() 进入监听状态后,一旦有客户端发起连接请求,accept() 方法就会被触发,
并且创建一个新的套接字实例。这个新套接字与客户端建立了一对一的连接通道,之后服务器就可以通过 client_socket 使用诸如 recv() 方法来接收客户端发送的数据,使用 send() 方法向客户端发送数据 。
假如服务器是一个客服中心,server_socket 就像是客服中心的总接待窗口,负责接收所有客户的连接请求。
client_info是一个包含客户端地址信息的元组,这个元组包含两个元素,第一个元素是客户端的 IP 地址,第二个元素是客户端所使用的端口号
'''
recv_data=client_socket.recv(1024) #最大接受1024字节
print(f"收到信息:{recv_data.decode('gbk')},from{client_info}")
client_socket.close() #关闭与客户端通信的套接字,释放相关资源。
server_socket.close() #关闭服务器端的套接字,停止监听连接。
2.客户端简化代码
from socket import *
#创建TCP套接字
client_socket=socket(AF_INET,SOCK_STREAM)
#绑定地址和接口
client_socket.connect(('127.0.0.1',8899))
#发送消息
client_socket.send('hello'.encode('gbk'))
#关闭
client_socket.close()
3.服务端持续交互代码
from socket import *
#创建TCP套接字
server_socket=socket(AF_INET,SOCK_STREAM)
#绑定地址和端口
server_socket.bind(('127.0.0.1',9999)) #服务器中用bind()绑定窗口
server_socket.listen(5) #让套接字进入监听,最多同时挂五个
print("等待客户端连接...")
client_socket,client_info=server_socket.accept()
print("客户端连接成功")
while True:
recv_data=client_socket.recv(1024) #最大接收1024个字节
print(f"客户端发来信息:{recv_data.decode('gbk')},from{client_info}")
if recv_data.decode('gbk') == 'end':
print("会话结束!")
break
msg=input(">")
client_socket.send(msg.encode('gbk'))
client_socket.close()
server_socket.close()
4.客户端持续交互代码
from socket import *
#创建TCP套接字
client_socket=socket(AF_INET,SOCK_STREAM)
#绑定地址和接口
client_socket.connect(('127.0.0.1',9999))
while True:
client_socket.send(input(">").encode('gbk'))
recv_data=client_socket.recv(1024)
print(f"服务端发来信息:{recv_data.decode('gbk')}")
if recv_data.decode('gbk')=='end':
print("会话结束")
break
client_socket.close()
5.多线程服务端自由通信
from socket import *
from threading import Thread
def recv_data():
while True:
recv_data = client_socket.recv(1024) # 最大接收1024个字节
recv_content=recv_data.decode('gbk')
print(f"客户端发来信息:{recv_content},from{client_info}")
if recv_content == 'end':
print("会话结束!")
break
def send_data():
while True:
msg = input(">")
e_msg=msg.encode('gbk')
client_socket.send(e_msg)
if msg=='end':
break
if __name__=='__main__':
# 创建TCP套接字
server_socket = socket(AF_INET, SOCK_STREAM)
# 绑定地址和端口
server_socket.bind(('127.0.0.1', 9999)) # 服务器中用bind()绑定窗口
server_socket.listen(5) # 让套接字进入监听,最多同时挂五个
print("等待客户端连接...")
client_socket, client_info = server_socket.accept()
print("客户端连接成功")
t1=Thread(target=recv_data)
t2=Thread(target=send_data)
t1.start()
t2.start()
t1.join()
t2.join()
client_socket.close()
server_socket.close()
6.多线程客户端自由通信
from socket import *
from threading import Thread
def recv_data():
while True:
recv_data = client_socket.recv(1024)
recv_content=recv_data.decode('gbk') #decode是解码,之后就变成了字符串
print(f"服务端发来信息:{recv_content}")
if recv_content=='end':
break
def send_data():
while True:
msg=input(">")
e_msg=msg.encode('gbk')
client_socket.send(e_msg)
if msg == 'end': #只能进行字符串的比较,所以保留原始字符串,用于比较
print("会话结束")
break
if __name__ == '__main__':
#创建TCP套接字
client_socket=socket(AF_INET,SOCK_STREAM)
#绑定地址和接口
client_socket.connect(('127.0.0.1',9999))
t1=Thread(target=recv_data)
t2=Thread(target=send_data)
t1.start()
t2.start()
t1.join()
t2.join()
client_socket.close()
2万+

被折叠的 条评论
为什么被折叠?



