网络编程
网络:
要解决哪些问题? 首先需要通信协议:
tcp udp http ftp pop3 smtp
tcp:可靠,有状态,长连接的协议
主叫方 被叫方
服务套接字 =>接受客户套接字的请求
客户套接字 == 客户套接字
udp:不可靠,无连接
Socket
Socket又称”套接字”,应用程序通常通过”套接字”向网络发出请求或者应答网络请求,使主机间或者一台计算机上的进程间可以通讯。
Python 提供了两个级别访问的网络服务:
低级别的网络服务支持基本的 Socket,它提供了标准的 BSD Sockets API,可以访问底层操作系统Socket接口的全部方法。
高级别的网络服务模块 SocketServer, 它提供了服务器中心类,可以简化网络服务器的开发。
使用Socket实现一对一聊天(TCP)
服务端
import socket
import threading
ss = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
ss.bind(("localhost",9999))
ss.listen(5)
print('开始等待客户的请求')
c = ss.accept()
print('某个客户连接到我了')
def myrecv(c):
while True:
msg=c[0].recv(1024)
print(msg.decode())
threading._start_new_thread(myrecv,(c,))
while True:
msg = input()
c[0].send(msg.encode())
除了socket模块以外,还使用了threading用一个新线程来做消息接受。
ss = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
首先我们使用 socket 模块的 socket 函数来创建一个 socket 对象。socket 对象可以通过调用其他函数来设置一个 socket 服务。
ss.bind(("localhost",9999))
ss.listen(5)
然后用bind()方法来绑定ip和端口号(参数类型为元组),再用listen()来设置最大连接数,超过最大连接数后排队。
c = ss.accept()
accept()方法来等待客户端请求,这个方法回返回一个元组,里面包含了一个套接字对象和客户端的地址。
def myrecv(c):
while True:
msg=c[0].recv(1024)
print(msg.decode())
threading._start_new_thread(myrecv,(c,))
while True:
msg = input()
c[0].send(msg.encode())
方法myrecv(c)是用来循环接收数据并打印,我们用一个线程来接管他。用c[0]也就是之前接收到的套接字对象来调用recv(指定最大数据量)方法接收数据,这个方法是返回的字符串类型,我们要打印需要用decode()方法来解码不然会乱码。
底下的循环是接收控制台输入的字符串并用send()方法发送,需要先用encode()方法进行编码。
客户端
import socket
import threading
c = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
c.connect(('localhost',9999)) #连接对方,不成功抛出异常
def myrecv(c):
while True:
msg=c.recv(1024)
print(msg.decode())
threading._start_new_thread(myrecv,(c,))
while True:
msg = input()
c.send(msg.encode())
比服务端相对简单,同样先创建一个socket对象,只不过我们只需要使用connect()方法连接到我们开启的服务器上就可以了。
多人同时聊天
实现理念:改造服务端,两个线程一个线程接受数据,一个线程把收到的数据发送出去
服务端
import socket
import threading
ss = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
ss.bind(('0.0.0.0',9999))
ss.listen(10)
msg=None
lock = threading.Lock()
cond = threading.Condition(lock)
def server_recv(c,a):
global msg
while True:
str1=c.recv(1024)
cond.acquire()
msg=str1
msg=str(a)+str1.decode()
cond.notify_all()
cond.release()
def server_send(c,a):
global msg
while True:
cond.acquire()
cond.wait()
cond.release()
c.send(msg.encode())
while True:
c,a = ss.accept()
threading._start_new_thread(server_recv,(c,a))
threading._start_new_thread(server_send,(c,a))
与之前不同
msg=None
lock = threading.Lock()
cond = threading.Condition(lock)
msg是用来储存信息的变量,因为暂时只缓存一条数据,所以为了保证线程安全,每条信息都被发送出去,我们需要一个线程锁来控制msg这个变量让它在被发送出去之前不会被改写。
def server_recv(c,a):
global msg
while True:
str1=c.recv(1024)
cond.acquire()
msg=str1
msg=str(a)+str1.decode()
cond.notify_all()
cond.release()
接受方法,比之前多了一个参数a,是在accept的时候接受到的地址,以便辨识身份。在方法内调用全局变量msg,首先用str1一个临时变量保证接受到数据,然后线程上锁,把msg赋值后唤醒发送方法,等待发送完成之后再进行接受数据。
def server_send(c,a):
global msg
while True:
cond.acquire()
cond.wait()
cond.release()
c.send(msg.encode())
接受方法,首先线程上锁之后使用wait()方法等待接受消息的线程来唤醒本线程,在把信息发送出去后继续锁定线程等待唤醒。
连接数据库
import pymysql
db = pymysql.connect('localhost','root','root','ssh')
cursor = db.cursor()
# # 使用 execute() 方法执行 SQL 查询
# cursor.execute("SELECT VERSION()")
# # 使用 fetchone() 方法获取单条数据.
# data = cursor.fetchone()
# print ("Database version : %s " % data)
sql = '''
insert into stu(id,name) values(1,'zs')
'''
try:
# 执行sql语句
cursor.execute(sql)
# 提交到数据库执行
db.commit()
except:
# 如果发生错误则回滚
db.rollback()
# 关闭数据库连接
print('success')
db.close()