一、socket基础知识
在网络编程中的一个基本组件就是套接字(socket).
1.有阻塞(同步网络编程)
2.有非阻塞(异步网络编程)
套接字基本上是两个端点的程序之间的"信息通道".程序可能分布在不同计算机上,通过套接字相互发送信息.
套接字包括两个:
1.服务套接字
创建一个服务套接字后:
1.让它等待连接,这样它就在某个网络地址处(ip地址和端口号的组合)监听,直到有客户端套接字连接.
连接完成后,两者就可以进行交互了.
2.使用bind(host,port)方法后,再调用listen(只有一个参数,服务器未处理连接的长度(即允许排队等待的连接数目,
在这些连接禁用之前))去监听某个特定位置的地址.
3.服务端开始监听后,便可以接受客户端连接.这个步骤使用accept方法来完成.这个方法会阻塞(等待)
直到客户连接,然后该方法返回一个格式为(client,address)的元组,
4.服务器处理完一个与客户端连接后,再次调用accept方法开始等待下一个连接.这个过程通常是个无限循环
2.客户机套接字
1.客户机简单的连接,完成事务,断开连接.
2.客户端使用connect(host,port)方法连接到服务器,
3.client是客户端套接字,address是一个前面解释过的地址.
一个套接字就是socket模块中socket类的一个实例.
这个实例需要三个参数
1.地址族(默认是socket.AF_INET)
2.流(socket.SOCK_STREAM,默认值)或数据报(socket.SOCK_DGRAM)套接字
3.使用协议(默认是0,使用默认值即可)
套接字有两个方法
1.send发送
2.revc接收
用于接收,用于传输数据,revc参数用来选择最大字节数来接收1024是个好选择。
二、Socket 类型
socket参数的详解
socket.socket(family=AF_INET,type=SOCK_STREAM,proto=0,fileno=None)
创建socket对象的参数说明:
套接字格式:
socket(family,type[,protocal]) 使用给定的地址族、套接字类型、协议编号(默认为0)来创建套接字。
三、Socket 函数
注意点:
1)TCP发送数据时,已建立好TCP连接,所以不需要指定地址。UDP是面向无连接的,每次发送要指定是发给谁。
2)服务端与客户端不能直接发送列表,元组,字典。需要字符串化repr(data)。
四、socket编程思路
1.基于TCP协议的socket
注 :启动时应该先启动 server端 ,再启动 client端。
TCP服务端:
1 创建套接字,绑定套接字到本地IP与端口
# socket.socket(socket.AF_INET,socket.SOCK_STREAM) , s.bind()
2 开始监听连接 #s.listen()
3 进入循环,不断接受客户端的连接请求 #s.accept()
4 然后接收传来的数据,并发送给对方数据 #s.recv() , s.sendall()
5 传输完毕后,关闭套接字 #s.close()
import socket sk = socket.socket() # 创建了一个socket对象 sk.bind(('127.0.0.1',8080)) # 绑定一台机器的(ip,端口) # 回环地址 - 指向自己这台机器 sk.listen() # 建立监听等待别人连接 conn,addr = sk.accept() # 阻塞:在这里等待直到接到一个连接 # conn是连接 # addr是对方的地址 print(conn) print(addr) conn.send(b'hello') # 和对方打招呼 msg = conn.recv(1024) #接收数据,并把接收的数据实例化 # 有发必有收 收发必相等 print(msg) conn.close() # 挂电话 sk.close() # 关机
TCP客户端:
1 创建套接字,连接远端地址
# socket.socket(socket.AF_INET,socket.SOCK_STREAM) , s.connect()
2 连接后发送数据和接收数据 # s.sendall(), s.recv()
3 传输完毕后,关闭套接字 #s.close()
import socket sk = socket.socket() # 买个手机 sk.connect(('127.0.0.1',8080)) # 拨号 ret = sk.recv(1024) print(ret) sk.send(b'byebye!') sk.close()
问题:在启动服务端时可能会遇到 端口报错 这时是因为此端口可能已被占用,无法重复使用,那该怎么解决呢?
#加入一条socket配置,重用ip和端口 import socket from socket import SOL_SOCKET,SO_REUSEADDR sk = socket.socket() sk.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #就是它,在bind前加 sk.bind(('127.0.0.1',8898)) #把地址绑定到套接字 sk.listen() #监听链接 conn,addr = sk.accept() #接受客户端链接 ret = conn.recv(1024) #接收客户端信息 print(ret) #打印客户端信息 conn.send(b'hi') #向客户端发送信息 conn.close() #关闭客户端套接字 sk.close() #关闭服务器套接字(可选)
2.基于UDP协议的socket
使用UDP协议时,不需要建立连接,只需要知道对方的IP地址和端口号,就可以直接发数据包。但是,能不能到达就不知道了。
虽然用UDP传输数据不可靠,但它的优点是和TCP比,速度快,对于不要求可靠到达的数据,就可以使用UDP协议。
server端:
import socket sk = socket.socket(type=socket.SOCK_DGRAM) # 建立一个socket对象, # 指定以UDP协议的形式来连接 sk.bind(('127.0.0.1',8080)) # 指定服务的地址 msg,addr = sk.recvfrom(1024) # 接收消息,发送端的地址 print(msg,addr) sk.sendto(b'HELLO',addr) # 给发送端回复消息 sk.close() # 关闭socket连接
client端:
import socket sk = socket.socket(type=socket.SOCK_DGRAM) sk.sendto(b'hello',('127.0.0.1',8080)) # 直接给服务器发送一段消息 msg,addr = sk.recvfrom(1024) # 接收对面的回信 print(msg) sk.close()
聊天工具应用:
TCP server端
import socket sk = socket.socket() sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) sk.bind(('127.0.0.1',9000)) sk.listen() while True: conn,addr = sk.accept() # 接收连接 三次握手conn while True: inp = input('>>>') if inp == 'q': conn.send(inp.encode('utf-8')) break conn.send(inp.encode('utf-8')) msg = conn.recv(1024) if msg == b'q':break print(msg.decode('utf-8')) conn.close() # 四次挥手 sk.close()
TCP client端
import socket sk = socket.socket() sk.connect(('127.0.0.1',9000)) while True: msg = sk.recv(1024) print(msg.decode('utf-8')) if msg == b'q':break inp = input('>>>') if inp == 'q': sk.send(inp.encode('utf-8')) break sk.send(inp.encode('utf-8')) sk.close()
UDP server端
import socket sk = socket.socket(type=socket.SOCK_DGRAM) sk.bind(('127.0.0.1',9090)) while True: msg,addr = sk.recvfrom(1024) print('来自[%s:%s]的消息--%s'%(addr[0],addr[1],msg.decode('utf-8'))) inp = input('>>>') sk.sendto(inp.encode('utf-8'),addr) sk.close()
UDP client端
import socket sk = socket.socket(type=socket.SOCK_DGRAM) addr = ('127.0.0.1',9090) while True: msg = input('>>>') sk.sendto(msg.encode('utf-8'),addr) msg_recv,addr = sk.recvfrom(1024) print(msg_recv.decode('utf-8')) sk.close()
时间同步服务器:(适合使用UDP协议)
# 需求 # 写一个时间同步的服务器 # 服务端接收请求 # 按照client端发送的时间格式,将服务器时间转换成对应格式 # 发送给客户端 import time import socket sk = socket.socket(type=socket.SOCK_DGRAM) sk.bind(('127.0.0.1',9000)) while True: msg,addr = sk.recvfrom(1024) # msg 客户端发送给server端的时间格式 "%Y-%m-%d %H:%M-%S" time_format = msg.decode('utf-8') time_str = time.strftime(time_format) sk.sendto(time_str.encode('utf-8'),addr) sk.close()
# client端每隔一段时间发送请求到服务端 # 发送时间的格式 import time import socket sk = socket.socket(type = socket.SOCK_DGRAM) sk.sendto('%Y-%m-%d %H:%M:%S'.encode('utf-8'),('127.0.0.1',9000)) msg,addr = sk.recvfrom(1024) print(msg.decode('utf-8')) sk.close() # 方式一 # 操作系统的定时任务 + python代码的形式 # 方式二 # while True + time.sleep的形式