1 概念
网络编程基于TCP和UDP
IP
用来标识网络中的计算机
主流IPV4,32位整数,每8位分组
C类地址:110+网络号21位+主机号8位
主机号0表示不存在 共可表示254台
IPV6 128位整数
127.0.0.1 本机地址
端口
0-65535
0-1023 被保留
网络程序都有自己的端口
端口用于标识进程
网络通信协议
协议即约定
OSI模型七层模型:
物理层-数据链路层-网络层-传输层-会话层-表示层-应用层
互联网通信使用最多的时TCP/IP协议族
TCP/IP协议族四层:
应用层-传输层-互联网络层-网络接口层
TCP/UDP协议
传输层协议
Socket是传输层给应用层的编程接口
TCP类似拨打电话,需要建立虚拟连接保证可靠连接
UDP类似发送短信,不可靠
TCP安全,稳定,效率低
socket
python
tcp SOCK_STREAM 面向连接
udp SOCK_DGRAM 面向无连接
可借助网络调试助手进行调试

2 UDP发送数据
# coding=utf-8
from socket import socket, AF_INET, SOCK_DGRAM
# 创建UDP套接字
udp_socket = socket(AF_INET, SOCK_DGRAM)
# 键盘接收发送的信息
data = input('请输入要发送的信息:')
# 创建接收信息的地址
addr = ('192.168.0.110', 8080)
# 发送信息
udp_socket.sendto(data.encode('gb2312'), addr)
udp_socket.close()
控制台输入要发送的信息,网络调试助手可接收到结果
多次启动python程序,会随机分配端口

3 UDP接收数据
# coding=utf-8
from socket import *
# 创建UDP套接字
udp_socket = socket(AF_INET, SOCK_DGRAM)
# 键盘接收发送的信息
data = input('请输入要发送的信息:')
# 创建接收信息的地址
addr = ('192.168.0.110', 8080)
# 绑定一个端口 ''表示本机 8989端口
udp_socket.bind(('', 8989))
# 发送信息
udp_socket.sendto(data.encode('gb2312'), addr)
# 本次接收最大字节数1024
recv_data = udp_socket.recvfrom(1024)
print('接收到%s的消息:%s'%(recv_data[1], recv_data[0].decode('gb2312')))
# 关闭套接字
udp_socket.close()
执行结果:
请输入要发送的信息:你好
接收到('192.168.0.110', 8080)的消息:收到
4 UDP多线程聊天
# coding=utf-8
from socket import *
from threading import Thread
# 创建UDP套接字
udp_socket = socket(AF_INET, SOCK_DGRAM)
# 绑定一个端口 本机8989端口
udp_socket.bind(('', 8989))
# 接收
def recv_fun():
while True:
recv_data = udp_socket.recvfrom(1024)
print('>>%s:%s'%(recv_data[1], recv_data[0].decode('gb2312')))
# 发送
def send_fun():
while True:
addr = ("192.168.0.110", 8080)
data = input('<<:')
udp_socket.sendto(data.encode('gb2312'), addr)
if __name__ == '__main__':
# 创建两个线程
t1 = Thread(target=send_fun)
t2 = Thread(target=recv_fun)
t1.start()
t2.start()
t1.join()
t2.join()
5 TFTP下载器
简单文件下载协议,使用端口69
tftp服务器使用ftpd32软件进行模拟

tftp文件服务器将文件切分,然后随机创建一个新端口进行发送,69端口可继续接收其他用户的读写请求
服务器发送一个完包,客户端会向服务器发送确认信息
数据包长度小于516表示发送完毕,如果最后一个包刚好为516,则再发送一个长度为0的数据包
# coding=utf-8
import struct
from socket import *
filename = 'test.jpg'
server_ip = '127.0.0.1'
# 封装读请求
send_data = struct.pack('!H%dsb5sb'%len(filename), 1, filename.encode(), 0, 'octet'.encode(), 0)
# 创建套接字
udp_socket = socket(AF_INET, SOCK_DGRAM)
udp_socket.sendto(send_data, (server_ip, 69))
# 本地创建文件 a追加模式 b二进制
f = open(filename, 'ab')
while True:
recv_data = udp_socket.recvfrom(1024)
# 获取操作码及数据块编号
caozuoma, ack_num = struct.unpack('!HH', recv_data[0][:4])
# 判断操作码是否是5,如果是5则为错误信息
if caozuoma == 5:
print('文件不存在')
break
# 将接收到的数据写入到文件中
f.write(recv_data[0][4:])
# 如果读取完
if len(recv_data[0]) < 516:
break
# 发送确认包
ack_data = struct.pack('!HH', 4, ack_num)
# 获取服务器发送数据的随机端口
rand_port = recv_data[1][1]
udp_socket.sendto(ack_data, (server_ip, rand_port))
6 TCP通信
三次握手
1.客户端发送包含同步标志(SYN)的TCP报文
报文指明客户端端口及TCP序号
2.服务端接收到SYN报文,返回SYN+ACK报文,TCP序号加1,ACK即确认
3.客户端返回一个确认报文ACK给服务器端,TCP序号加1,至此一个TCP连接完成,然后开始数据处理
TCP服务器端代码
客户端使用网络助手,协议类型选择TCP Client
# coding=utf-8
from socket import *
# 创建服务端套接字
server_socket = socket(AF_INET, SOCK_STREAM)
# 绑定端口
server_socket.bind(('', 8989))
# 监听
server_socket.listen()
# 接收客户端的连接
client_socket, client_info = server_socket.accept()
# 接收客户端发送的信息
recv_data = client_socket.recv(1024)
print('接收到%s的消息%s'%(client_info, recv_data.decode('gb2312')))
# 关闭连接
client_socket.close()
server_socket.close()
运行代码,网络调试助手连接,发送消息
控制台会打印服务端接收的消息
7 TCP模拟QQ
服务器端
# coding=utf-8
from socket import *
# 创建服务器端套接字
server_socket = socket(AF_INET, SOCK_STREAM)
# 绑定端口
server_socket.bind(('', 9999))
# 监听
server_socket.listen()
# 等待客户端的连接
client_socket, client_info = server_socket.accept()
while True:
# 接收客户端的消息
recv_data = client_socket.recv((1024))
print('客户端说:', recv_data.decode('utf-8'))
if recv_data.decode('utf-8') == 'bye':
break
# 发送消息
msg = input('>')
client_socket.send(msg.encode('utf-8'))
server_socket.close()
客户端
# coding=utf-8
from socket import *
# 创建客户端套接字
client_socket = socket(AF_INET, SOCK_STREAM)
# 调用connect方法与服务器建立连接
client_socket.connect(('127.0.0.1', 9999))
while True:
# 客户端发送消息
msg = input('>')
client_socket.send(msg.encode('utf-8'))
if msg == 'bye':
break
# 客户端接收消息
recv_data = client_socket.recv(1024)
print('服务端说:', recv_data.decode('utf-8'))
client_socket.close()
启动服务端和客户端,可以互相发送消息
8 TCP多线程聊天
服务器端
# coding=utf-8
from socket import *
from threading import Thread
sockets = []
def main():
# 创建server端套接字
server_socket = socket(AF_INET, SOCK_STREAM)
server_socket.bind(('', 8888))
# 监听
server_socket.listen()
while True:
# 接收客户端请求
client_socket, client_info = server_socket.accept()
sockets.append(client_socket)
# 开启线程处理当前客户端的请求
t = Thread(target=readMsg, args=(client_socket,))
t.start()
def readMsg(client_socket):
# 读取客户端的消息
while True:
recv_data = client_socket.recv(1024)
# 将消息发送给所有在线的客户端
for socket in sockets:
socket.send(recv_data)
if __name__ == '__main__':
main()
客户端
# coding=utf-8
from socket import *
from threading import Thread
def readMsg(client_socket):
while True:
recv_data = client_socket.recv(1024)
print('收到:', recv_data.decode('utf-8'))
def writeMsg(client_socket):
while True:
msg = input('>')
client_socket.send(msg.encode('utf-8'))
def main():
# 创建客户端套接字
client_socket = socket(AF_INET, SOCK_STREAM)
# 调用connect连接服务器
client_socket.connect(('127.0.0.1', 8888))
# 开启线程处理客户端读消息
t = Thread(target=readMsg, args=(client_socket,))
t.start()
# 开启线程处理客户端写消息
t = Thread(target=writeMsg, args=(client_socket,))
t.start()
if __name__ == '__main__':
main()
9 TCP多线程聊天优化
*1.客户端发送指定字符串退出客户端,服务端移除该线程
*2.显示客户端用户名
服务端
# coding=utf-8
from socket import *
from threading import Thread
sockets = []
def main():
# 创建server端套接字
server_socket = socket(AF_INET, SOCK_STREAM)
server_socket.bind(('', 8888))
# 监听
server_socket.listen()
while True:
# 接收客户端请求
client_socket, client_info = server_socket.accept()
sockets.append(client_socket)
# 开启线程处理当前客户端的请求
t = Thread(target=readMsg, args=(client_socket,))
t.start()
def readMsg(client_socket):
# 读取客户端的消息
while True:
recv_data = client_socket.recv(1024)
# 如果接收到的消息结尾是bye,则移除该客户端 *1
if recv_data.decode('utf-8').endswith('utf-8'):
sockets.remove(client_socket)
client_socket.close()
break
# 将消息发送给所有在线的客户端
for socket in sockets:
socket.send(recv_data)
if __name__ == '__main__':
main()
客户端
# coding=utf-8
from socket import *
from threading import Thread
# 下线标识
flag = True
def readMsg(client_socket):
global flag
while flag:
recv_data = client_socket.recv(1024)
print('收到:', recv_data.decode('utf-8'))
def writeMsg(client_socket, user_name):
global flag
while flag:
msg = input('>')
# *2
msg = user_name + '说:' + msg
client_socket.send(msg.encode('utf-8'))
# 如果输入bye下线 *1
if msg.endswith('bye'):
flag = False
def main():
# 创建客户端套接字
client_socket = socket(AF_INET, SOCK_STREAM)
# 调用connect连接服务器
client_socket.connect(('127.0.0.1', 8888))
user_name = input('请输入用户名:')
# 开启线程处理客户端读消息
t1 = Thread(target=readMsg, args=(client_socket,))
t1.start()
# 开启线程处理客户端写消息
t2 = Thread(target=writeMsg, args=(client_socket, user_name))
t2.start()
t1.join()
t2.join()
client_socket.close()
if __name__ == '__main__':
main()
网友评论