总而言之、实现了简易的网络通信器,有群聊、单聊、控制、查看等的操作,完成了任务
不足:应用到了线程,在退出时并没有成功结束调度返回资源,而是将线程设为了守护线程,运行到了堵塞状态使程序不报错、后续学习了知识再来补充,也望大佬能评论赐教!
一、准备知识:
多套接字及多线程:**
进程与线程的区别:
(可理解为一条马路-进程和马路上的多条车道-线程)
-
线程是程序执行的最小单位,而进程是操作系统分配资源的最小单位;
-
一个进程由一个或多个线程组成,线程是一个进程中代码的不同执行路线
-
进程之间相互独立,但同一进程下的各个线程之间共享程序的内存空间(包括代码段,数据集,堆等)及一些进程级的资源(如打开文件和信
号等),某进程内的线程在其他进程不可见;
-
调度和切换:线程上下文切换比进程上下文切换要快得多
为何不使用多进程而是使用多线程?
线程廉价,线程启动比较快,退出比较快,对系统资源的冲击也比较小。而且线程彼此分享了大部分核心对象(File Handle)的拥有权
二、通信器功能分析:
tcp socket(通信质量保障)
server:
群聊天(群聊窗口、接收消息后泛洪)
一对一(私聊窗口、接收消息后转发)
行为管理(只做了查看当前线程和关闭所有服务、未实现单个控制、也不难咯)
信息提示(进入或退出会输出提示)
client:
群聊天(发给服务器让服务器去泛洪)
一对一(发给发服务器让服务器代发)
退出控制
三、编写代码+功能验证
- server信息提示:
- 行为管理:
- server群发:
- server单发:
- 代码
#欢迎大佬指正和修改
import socket
import threading
import logging
import datetime
FORMAT = "%(asctime)s %(threadName)s %(thread)d %(message)s"
logging.basicConfig(format=FORMAT, level=logging.INFO)
class socketS():
#初始化,固定ip和port,绑定,监听
def __init__(self):
address = ('127.0.0.1', 9999)
self.bufsize = 1024
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.bind(address)
self.sock.listen()
self.client_sock = {} #空字典放所有的client sock对象,{key:ip, value:sock}
self.thread_accept = threading.Thread(target=self.accept, name='accept', daemon=True)
self.thread_accept.start()
#建立连接、记录
def accept(self):
while True:
sock, raddr = self.sock.accept()
print("a new client connected!")
logging.info(sock)
logging.info(raddr)
self.client_sock[raddr] = sock
#线程传参、args=duple
self.thread_recv = threading.Thread(target=self.recv, name='recv', daemon=True, args=(sock,))
self.thread_recv.start()
#接收和群发
def recv(self, sock:socket.socket):
while True:
msg = sock.recv(self.bufsize).decode('gbk')
# 单发
single_msg = msg.split('%%%')
if len(single_msg)>1:
self.send(single_msg)
# self.thread_send = threading.Thread(target=self.send, name='send', daemon=True, args=(single_msg,))
# self.thread_send.start()
#退出、从dict中删除
elif msg == 'quit':
print("a client have exited!")
logging.info(sock.getpeername())
self.client_sock.pop(sock.getpeername())
else:
msg = "{}\n {}: {}".format(
datetime.datetime.now().strftime("%Y/%m/%d-%H:%M:%S"),
sock.getpeername(), #获取远端(ip, port)
msg).encode('gbk')
for value in self.client_sock.values():
value.send(msg)
#单发
def send(self, single_msg):
msg = single_msg[0]
l_ip = single_msg[1]
l_port = int(single_msg[2])
r_ip = single_msg[3]
r_port = single_msg[4]
for key, value in self.client_sock.items():
if l_ip == key[0] and l_port == key[1]:
msg = "{}\n {}: {}".format(
datetime.datetime.now().strftime("%Y/%m/%d-%H:%M:%S"),
(r_ip, r_port),
msg).encode('gbk')
value.send(msg)
#关闭服务、貌似还有很大的问题
def stop(self):
print("server已关闭")
self.thread_accept.join()
self.thread_recv.join()
# threading.Event.wait(1) #阻塞线程直到内部变量为true。
for value in self.client_sock.values():
value.close()
self.sock.close()
if __name__ == '__main__':
ss = socketS()
print("服务已启动,输入show查看当前线程,输入quit将关闭所有服务:")
while True:
cmd = input().strip()
if cmd == 'quit':
ss.stop()
elif cmd == 'show':
logging.info(threading.enumerate())
else:
print("a error cmd!")
- client基本功能
选择连接、选择通信方式
import socket
import threading
class socketC():
#初始化套接字
def __int__(self, ip, port):
address = (ip, port)
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.connect(address)
print("connect successful!")
self.thread_recv = threading.Thread(target=self.recv, name='recv', daemon=True)
#一对一
def chat(self):
self.thread_recv.start()
print("successful and input ip and port you need to chat")
r_ip = input("ip:\n").strip() #只需要输入一次
r_port = input("port:\n").strip()
l_address = self.sock.getsockname() #return 自己的(ip, port)
l_ip = str(l_address[0])
l_port = str(l_address[1])
while True:
data = input().strip()
if data == 'quit':
print("end!!!")
self.thread_recv.join()
self.sock.close()
return 0
msg = (data + '%%%' + r_ip + '%%%' + r_port + '%%%' + l_ip + '%%%' + l_port).encode('gbk')
self.sock.send(msg)
#一对多
def chat_group(self):
self.thread_recv.start()
print("chat_group start successful and you can chat now!")
while True:
msg = input().strip()
if msg == 'quit':
print("end!!!")
self.thread_recv.join()
self.sock.close()
return 0
msg = msg.encode('gbk')
self.sock.send(msg)
#公用接收
def recv(self):
bufsize = 1024
while True:
msg = self.sock.recv(bufsize).decode('gbk')
print(msg)
if __name__ == '__main__':
ip = input("please input server's ip:\n").strip()
port = int(input("please input server's port:\n").strip())
sc = socketC()
sc.__int__(ip, port)
while True:
print("""****** please choose your operations: ************
1、start a normal chat
2、start a group chat""")
print('*' * 50)
options = int(input().strip())
if options == 1:
sc.chat()
elif options == 2:
sc.chat_group()
else:
print("a error cmd!")