Day5
今日学习:
1.go语言学习
学习Golang的意外收获:我写的代码执行效率超过了所有用户,哈哈哈
2.回顾Tcp
在刚开始学习JAVA的时候,最让人头痛的就是流,因为当时初学者刚学习,还没有养成编程思维,无法形象将文
件传输的操作比作在一个管道里传文件;但是现在写了很多行代码,对文件的传输比喻成流也能理解了。TCP,它的传输文件
也有recv和send:当服务器做send操作,那么客户端recv,就像管子的一段发送,另一端只能接收一样。
那么问题来了,当客户端有多台的时候怎么办?不可能只靠一根管子,一个一个传给客户端吧?就在这里我们学习了线程池,
一个服务器和每一台客户机都有一根管子;可是,就算是服务器和每个客户端之间都多了一根管子,有的客户端它老是不接收(或
发送)消息,我们不能老等着那些不去收发消息的客户端吧?所以,又在这时引入了阻塞/非阻塞的概念,服务端不用去等客户端到
底什么时候发消息过来,客户端要发消息过来叫一声,服务器就过去了(非阻塞)
但服务端不知道客户端每次发过来的消息是多少,一下长一下短的,其中就出现了将两次消息当作一次发过来的现象,粘包。
为了解决粘包现象,我们使用了包头[header],包头信息主要做的就是告诉服务端每一次接收多少消息。
然而之前说的一台服务器和多个客户端之间有多个管子(多线程),在python里其实是不大可行的,因为python具有GIL锁,
如果你的电脑是双核CPU,就算开了多线程,它最多也就只能利用到一个核,多线程并没有什么帮助,虽然有很多管子,但是每次
只能用一个。然而回到最开始,服务器和多个客户端就算只有一个管子,那也足够了,当你还没准备好发送消息的时候(遇到IO阻塞),
那我就把管子拿到别人那里去,最大限度的将管子的作用发挥到极限的方式,这种方式叫做协程,遇到IO跳过
综上,TCP最终版可以成型了:
1.tcp服务端:
1 import socket 2 import threading 3 import time 4 5 6 class tcpServer: 7 def __init__(self): 8 self.tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 9 self.tcp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 10 self.client_socket_list = list() 11 self.port = 9527 12 13 def tcp_server_start(self): 14 """ 15 功能函数,TCP服务端开启的方法 16 :return: None 17 """ 18 self.tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 19 self.tcp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 20 self.tcp_socket.setblocking(False) 21 try: 22 23 self.tcp_socket.bind(('', self.port)) 24 except Exception as ret: 25 self.msg = '请检查端口号\n' 26 print(self.msg) 27 print(ret) 28 else: 29 self.tcp_socket.listen() 30 self.sever_th = threading.Thread(target=self.tcp_server_concurrency) 31 self.sever_th.start() 32 self.msg = 'TCP服务端正在监听端口:%s\n' % str(self.port) 33 print(self.msg) 34 35 36 def tcp_server_concurrency(self): 37 """ 38 功能函数,供创建线程的方法; 39 使用子线程用于监听并创建连接,使主线程可以继续运行,以免无响应 40 使用非阻塞式并发用于接收客户端消息,减少系统资源浪费,使软件轻量化 41 :return:None 42 """ 43 while True: 44 try: 45 self.client_socket, self.client_address = self.tcp_socket.accept() 46 47 except Exception as ret: 48 time.sleep(0.001) 49 50 else: 51 self.client_socket.setblocking(False) 52 # 将创建的客户端套接字存入列表 53 self.client_socket_list.append((self.client_socket, self.client_address)) 54 self.msg = 'TCP服务端已连接IP:%s端口:%s\n' % self.client_address 55 print(self.msg) 56 # 轮询客户端套接字列表,接收数据 57 for client, address in self.client_socket_list: 58 try: 59 recv_msg = client.recv(1024) 60 except Exception as ret: 61 pass 62 else: 63 if recv_msg: 64 msg = recv_msg.decode('utf-8') 65 self.msg = '来自IP:{}端口:{}:\n{}\n'.format(address[0], address[1], msg) 66 print(self.msg) 67 else: 68 client.close() 69 self.client_socket_list.remove((client, address)) 70 def tcp_server_send(self): 71 send_msg = input('') 72 for client, address in self.client_socket_list: 73 client.send(send_msg.encode('utf-8')) 74 self.msg = 'TCP服务端已发送\n' 75 print(self.msg) 76 77 if __name__ == '__main__': 78 tcpS=tcpServer() 79 tcpS.tcp_server_start() 80 while True: 81 tcpS.tcp_server_send()
2.tcp客户端:
import socket import threading class TcpClient: def __init__(self): self.tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.tcp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.ip="127.0.0.1" self.port=9527 def tcp_client_start(self): """ 功能函数,TCP客户端连接其他服务端的方法 :return: """ self.tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.tcp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) try: self.address = (str(self.ip), int(self.port)) except Exception as ret: self.msg = '请检查目标IP,目标端口\n' print(self.msg) else: try: self.msg = '正在连接目标服务器\n' print(self.msg) self.tcp_socket.connect(self.address) except Exception as ret: self.msg = '无法连接目标服务器\n' print(self.msg) print(ret) else: self.client_th = threading.Thread(target=self.tcp_client_concurrency) self.client_th.start() self.msg = 'TCP客户端已连接IP:%s端口:%s\n' % self.address print(self.msg) def tcp_client_concurrency(self): """ 功能函数,用于TCP客户端创建子线程的方法,阻塞式接收 :return: """ while True: recv_msg = self.tcp_socket.recv(1024) if recv_msg: msg = recv_msg.decode('utf-8') self.msg = '来自IP:{}端口:{}:\n{}\n'.format(self.address[0], self.address[1], msg) print(self.msg) else: self.tcp_socket.close() self.msg = '从服务器断开连接\n' print(self.msg) break def tcp_client_send(self): send_msg = input('') self.tcp_socket.send(send_msg.encode('utf-8')) self.msg = 'TCP客户端已发送\n' if __name__ == '__main__': tcpC = TcpClient() tcpC.tcp_client_start() while True: tcpC.tcp_client_send()
功能如下:
-两端可以同时收发消息
-非阻塞方式减少服务器压力
-子线程用于监听并创建连接,使主线程可以继续运行,以免无响应
-非阻塞式并发用于接收客户端消息,减少系统资源浪费
3.防优酷项目业务逻辑层学习:
管理员业务需求抽取:
-管理员:1.上传电影
2.删除电影
3.更新公告
3.1 上传电影功能
获取一个从客户端接受来的字典back_dict,
1.从字典获取属性:名字,大小,是否免费,电影MD5码,上传者id
2.根据电影名,使用tcp从客户端管理员获取电影,并存放于设定的好目录文件夹下
3.对数据库进行更新,把刚才下好的电影信息进行保存
3.2删除电影功能
获取一个从客户端接受来的字典back_dict,
1.从字典获取属性:需要删除的电影id
2.从数据库中搜索出该电影,并返回一个相对应的电影对象
3.对该电影的is_delete设置为1,1为已经删除
4.对数据库进行更新,把刚才做好删除标记的电影信息进行保存
3.3 更新公告功能
获取一个从客户端接受来的字典back_dict,
1.从字典获取属性:标题名称,内容,时间,管理员的id
2.根据属性,获得对应的notice对象
3.对内容进行保存
4.对数据库进行更新,把刚才做好删除标记的电影信息进行保存
业务层学习总结:
1.业务层做的事是,向数据层获取数据,向界面层提交,实现需求。
2.当业务层需要ORM做复杂一点的操作,例如多表查询,按需输出这,为了实现这些操作,需要向ORM里写一些新的获取数据层的手段方法,反而不利于开发效率吧?
3.在细节上,例如判断上传的电影是否存在,那么根据select()返回一个对象,如果存在就返回一个对象;这样的操作会不会太臃肿?因为毕竟我只是需要知道它存在就行了
4.在业务层上,conn始终作为一个参数参与进行业务实现,这样做会不会有什么风险问题?我可不可以将收发信息的功能写到lib里,使得业务逻辑层不直接与客户连接混在一起?
业务层管理员代码实现:
import datetime import os from conf import settings from lib import common from orm import models #上传电影功能 # @common.login def upload_movie_interface(back_dic): movie_size = back_dic.get('movie_size') movie_name = back_dic.get('movie_name') # movie_new_name = common.get_session(movie_name) + movie_name movie_new_name = movie_name movie_path = os.path.join(settings.DOWNLOAD_MOVIES_PATH, movie_new_name) # recv_data = 0 # with open(movie_path, 'wb') as f: # while recv_data < movie_size: # data = conn.reader.read(100) # f.write(data) # recv_data += len(data) # 2.存储电影信息于电影表中 movie_obj = models.Movie( name=movie_new_name, is_free=back_dic.get('is_free'), is_delete=0, file_md5=back_dic.get('movie_md5'), path=movie_path, create_time=str(datetime.datetime.now()), user_id=back_dic.get('user_id') ) movie_obj = movie_obj.save() print(movie_obj.__dict__) # data = {'flag': True, 'msg': '电影上传成功!'} # conn.writer.write(data) #删除电影 def delete_movie_interface(back_dic, ): movie_id = back_dic.get('movie_id') movie_obj = models.Movie.select(models.Movie(),whereStr="id={0}".format(movie_id))[0] print(movie_obj.is_delete) movie_obj.is_delete = 1 # 0 --> 1 movie_obj.update() send_dic = { 'flag': True, 'msg': '电影删除成功!' } # common.send_msg(send_dic, conn) #发送公告 def send_notice_interface(back_dic, ): notice_obj = models.Notice( title=back_dic.get('title'), content=back_dic.get('content'), create_time=str(datetime.datetime.now()), user_id=back_dic.get('user_id') ) notice_obj = notice_obj.select(whereStr="movie_id={0}".format(notice_obj.user_id))[0] # 插入公告 notice_obj.save() send_dic = { 'msg': '公告发布成功!' } # common.send_msg(send_dic, conn) back_dict={'movie_size':10, 'movie_name':'盗梦空间', 'is_free':'0', 'movie_md5': '123456', "movie_id":2, 'user_id':5 } # upload_movie_interface(back_dict) delete_movie_interface(back_dict) # send_notice_interface(back_dict)