1.基本概念
网络通信目的:
IP地址
目的:用来标记网络通信中的一台计算机
window查看ip地址: CMD窗口输入命令 ipconfig
IP地址的分类:
ip v4: 每一个ip地址包括两部分:网络地址和主机地址 常用ip地址:192:168:1~254.1~254 ip v6(正在发展) 单播:一对一 多播:一对多
端口:就是一个数字,标记进程,好比就是一个房子的门,是出入这间房子的必经之路
端口分类:知名端口 动态端口
端口是通过端口号来标记的,端口号只有整数,范围是从0到65535 知名端口:是众所周知的端口号,范围从0到1023,不能随便用 80端口分配给HTTP服务 21端口分配给FTP服务 动态端口:范围1023~65535,一般不固定分配某种服务,而是动态分配的,可以随便用 进程:是运行起来的程序。
socket(套接字,类似于插口,插排)
socket是网络通信必备的,如常见的网络通信工具,qq,微信
1.创建一个tcp sokect(tcp套接字) import socket s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) s.close() 2.创建一个udp socket(udp套接字) s = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) s.close() # 不用的时候,关闭套接字
import socket def main(): # 创建一个udp套接字 udp_socket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) udp_socket.sendto(b'hahaha',("192.168.11.189",8080)) #192.168.11.189本机ip地址 # 关闭套接字 udp_socket.close() if __name__ == "__main__": main() ''' b'100' 表示字节类型 ''' ========================================================================================= import socket def main(): # 创建一个udp套接字 udp_socket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # 从键盘中获取数据 send_data = input("请输入要发送的数据: ") udp_socket.sendto(send_data.encode("utf-8"),("192.168.11.189",8080))# 发送数据 # 关闭套接字 udp_socket.close() if __name__ == "__main__": main() ========================================================================================= import socket def main(): udp_socket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) while True:#循环不间断发送数据 send_data = input("请输入要发送的数据:") if send_data == "exit": break udp_socket.sendto(send_data.encode("utf-8"),(("192.168.11.189",8080)))# 发送数据 udp_socket.close() if __name__ == "__main__": main()
接收数据
''' 要想接收数据,必须要有一个固定的端口 ''' import socket # 创建套接字 udp_socket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # 绑定本地的相关信息,如果一个网络程序不绑定,系统就会随机分配 local_addr = ('',7788) # ip地址和端口号,ip一般不写,表示本机的任何一个ip udp_socket.bind(local_addr) # 等待接收对方发送的数据 recv_data = udp_socket.recvfrom(1024) # 1024表示本次接收的最大字节数 # 显示接收到的数据 print(recv_data[0].decode("gbk")) udp_socket.close()
import socket def main(): udp_socket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # 绑定本地的相关信息,如果一个网络程序不绑定,系统就会随机分配 local_addr = ('',7788) # ip地址和端口号,ip一般不写,表示本机的任何一个ip udp_socket.bind(local_addr) # 等待接收对方发送的数据 recv_data = udp_socket.recvfrom(1024) # 1024表示本次接收的最大字节数 # 显示接收到的数据 print(recv_data[0].decode("gbk")) # window 默认为"gbk" print(recv_data[1]) #显示发送方的地址信息 udp_socket.close() if __name__ == "__main__": main() =========================================================================================# 循环接收数据 import socket def main(): udp_socket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # 绑定本地的相关信息,如果一个网络程序不绑定,系统就会随机分配 local_addr = ('',7788) # ip地址和端口号,ip一般不写,表示本机的任何一个ip udp_socket.bind(local_addr) # 等待接收对方发送的数据 while True: recv_data = udp_socket.recvfrom(1024) # 1024表示本次接收的最大字节数 print(recv_data[0].decode("gbk")) # window 默认为"gbk",# 显示接收到的数据 print(recv_data[1]) # 打印发送方的地址信息 udp_socket.close() if __name__ == "__main__": main()
总结:
发送数据的流程: 接收数据的流程:
1、创建套接字 1、创建套接字
2、发送数据 2、绑定本地自己的信息(ip 和端口)
3、关闭 3、接收数据
4、关闭
端口绑定
t同一台电脑 ip地址不相同
''' 单工:百分之一百只能走向一个方向 (如收音机) 半双工: 发送时不能接收,接收时不能发送 (如收音机) 全双工: 同一个时刻既可以发送也可以接收 (打电话) ''' socket套接字是全双工
import socket def main(): '''''' udp_socket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) #获取对方的ip/port dest_ip = input("请输入对方的IP:") dest_port = int(input("请输入对方的port:")) #从键盘获取数据 send_data = input("请输入要发送的数据:") udp_socket.sendto(send_data.encode("utf-8"),(dest_ip,dest_port)) recv_data = udp_socket.recvfrom(1024) # 接收回送过来的数据 print(recv_data) udp_socket.close() '''套接字是一个同时收发数据''' if __name = "__main__": main()
UDP聊天案列
import socket def main(): #创建套接字 udp_socket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) udp_socket.bind(("",7788)) # 循环循环来进行处理事情 while True: dest_ip = (input("请输入对方的ip:")) dest_port = int(input("请输入对方port:")) send_data = input("请输入要发送的消息:") udp_socket.sendto(send_data.encode("utf-8"),(dest_ip,dest_port)) # 接收并显示 recv_data = udp_socket.recvfrom(1024) print(f'接收内容:{recv_data[0].decode("utf-8")}') print(f'对方ip地址:{str(recv_data[1])}') if __name__ == "__main__": main()
TCP
TCP协议:传输控制协议(Transmission Control Protocol) 一种面向连接的,可靠的
UDP协议: 用户数据报协议
二者区别:
udp通信模型中,在通信开始之前,不需要建立相关的连接,只需要发送数据即可,类似于生活中的"写信"。(不安全)
tcp(打电话) :更稳定 可靠。TCP通信之前 必须 创建连接 数据传送 终止连接。
TCP比UDP稳定,应用更广泛。UDP很少用,通常用来发送数据,接收数据
TCP严格区分客户端和服务端
tcp客户端
import socket def main(): # 创建tcp的套接字 tcp_client_socket = socket.socket(AF_INET,SOCK_STREAM) # 连接服务器 server_ip = input("请输入要连接的服务器的IP:") server_port = int(input("请输入要连接的服务器的port:")) tcp_client_socket.connect((server_ip,server_port)) #输入参数元组 # 提示用户输入数据 send_data = input("请输入要发送的数据:") # 发送数据/接收数据 tcp_socket.send(send_data.encode("utf-8")) # 关闭套接字 tcp_socket.close() if __name == "__main__": main()
tcp服务器
''' socket创建一个套接字 bind绑定ip和port listen使套接字变为可以被动连接 accept等待客户端的连接 recv/send接收发送数据 ''' import socket def main(): tcp_server_socket = socket.socket(AF_INET,SOCK_STREAM) tcp_server_socket.bind("",8080) # 让默认的套接字由主动变成被动 tcp_server_socket.listen(128) print('----1----') # 等待别人的电话到来(等待客户端的连接) new_client_socket,client_addr = tcp_server_socket.accept() # accept()返回的是元组 print('----2----') print(client_addr) # 接收客户端发送过来的请求 recv_data = new_client_socket.recv(1024) print(recv_data) # 回送一部分数据给客户端 new_client_socket.send("hahahaha...ok....".encode("utf-8")) # 关闭套接字 new_client_socket.close() tcp_server_socket.close() if __name__ == "__main__": main()
tcp 服务器(循环为多个客户端服务)
import socket def main(): tcp_server_socket = socket.socket(SOCK.AF_INET,sock.SOCK_STREAM)#创建套接字socket tcp_server_socket.bind(("",7000)) #绑定本地信息 tcp_server_socket.listen(128) #让默认的套接字由主动变成被动 listen while True: print("等待一个新的客户端的到来") new_client_socket,client_addr = tcp_server_socket.accept() #等待客户端的连接accept() print(f'一个新的客户端已经到来:{str(client_addr)}') recv_data = new_client_socket.recv(1024) #接收客户端发送过来的请求 print(f'客户端发送过来的请求是:{recv_data.decode("utf-8")}') new_client_socket.send("hahaha...".encode("utf-8")) #回送一部分数据给客户端 new_client_socket.close() # 关闭套接字 '''关闭accept返回的套接字 意味着 不会再为这个客户端服务''' print("已经服务完毕") tcp_server_socket.close() '''如果将监听套接字关闭了,那么会导致 不能再次等待新客户的到来 ,即xxx.accept就会失败 不会有新的客户端到来 ''' if __name__ == "__main__": main()
tcp 服务端 (循环为多个客户端服务并且多次服务一个客户端)
import socket def main(): tcp_server_socket = socket.socket(SOCK.AF_INET,sock.SOCK_STREAM)#创建套接字socket tcp_server_socket.bind(("",7000)) #绑定本地信息 tcp_server_socket.listen(128) #让默认的套接字由主动变成被动 listen while True: # 外层while True 循环为多个客户端服务 print("等待一个新的客户端的到来") new_client_socket,client_addr = tcp_server_socket.accept() #等待客户端的连接accept() print(f'一个新的客户端已经到来:{str(client_addr)}') while True: #内层 while True 循环多次为同一个客户端服务多次 recv_data = new_client_socket.recv(1024) #接收客户端发送过来的请求 print(f'客户端发送过来的请求是:{recv_data.decode("utf-8")}') ''' 若recv解堵塞,那么有两种方式: a.客户端发送过来的数据 b.客户端调用close导致了 ''' if recv_data: new_client_socket.send("hhahhhh".encode("utf-8")) else: break new_client_socket.close() # 关闭套接字 '''关闭accept返回的套接字 意味着 不会再为这个客户端服务''' print("已经服务完毕") tcp_server_socket.close() '''如果将监听套接字关闭了,那么会导致 不能再次等待新客户的到来 ,即xxx.accept就会失败 不会有新的客户端到来 ''' if __name__ == "__main__": main()
总结
1 tcp服务器一般情况下都需要绑定,否则客户端找不到这个服务器 2 tcp客户端一般不绑定,,因为是主动连接服务器,所以只需要确定好服务器的IP 、port 等信息就好了,本地客户端可以随机 3 tcp服务器中通过listen可以将socket创建出来的主动套接字变为被动的。这是作tcp服务器时必须要做的。 4 当客户端需要连接服务器时,就需要使用connect进行连接,UDP是不需要连接的而是直接发送的,但是tcp必须先连接,只有连接成功才能通信。 5 当一个tcp客户端连接服务器时,服务器端就会有一个1新的套接字,这个套接字用来标记这个客户端,单独为这个客户端服务。 6 listen 后的套接字是被动套接字,用来接收新的客户端的连接请求的,而accpet返回的新套接字是标记这个新客户端的 7 关闭listen后的套接字意味着被动套接字关闭了,会导致新的客户端不能够连接服务器,但是之前已经连接成功的客户端正常通信 8 关闭accept返回的套接字意味着这个客户端已经服务完毕 9 当客户端套接字调用close、服务器就会recv解堵塞,并且返回的长度为0.因此服务器可以通过返回数据的长度来区别客户端是否已经下线。