Python3 网络通信 网络聊天室 文件传输

python

功能描述

该项目将实现一个文字和文件传输的客户端和服务器程序通信应用程序。它将传输和接收视频文件。

文本消息必须通过TCP与服务器通信,而客户端自己用UDP传输视频文件。

程序将支持一下功能:

  • 用户身份验证
  • 向服务器发布消息
  • 编辑或删除消息、读取消息
  • 从服务器发送消息
  • 读取活动用户的信息
  • 上传视频文件从一个用户到另一个用户

简要流程

  • 该项目将实现各种应用程序协议来实现以上功能。
  • 服务器将监听指定为命令行参数的端口,并等待客户端监听连接。
  • 客户端程序将发起一个与服务器的TCP连接。
  • 在连接建立后,用户将启动身份验证过程。客户端将与用户交互通过命令行界面。
  • 身份验证成功后,用户可以执行一系列命令
  • 最终登出
  • 客户端和服务器都必须打印有意义的提示消息

简要设计

  • Server: 使用一个类来保存每个客户端信息,包括用户名和IP地址的映射,并多线程开始接收每个客户端的处理程序。
  • Client: 三个线程:主线程为获取用户输入和发送到服务器,和一个线程为接收服务器响应,一个线程UDP传输文件

功能协议

  • Login: flag("Login") + username + password

  • MSG: flag("MSG") + message

  • EDT: flag("EDT") + msg_index + msg_time + new_msg

  • DLT: flag("DLT") + msg_index + msg_time

  • RDM: flag("RDM") + msg_time

  • ATU: flag("ATU")

  • OUT: flag("OUT")

  • UPD: flag("UDP") + username + filename

Usage

  • Server:python server.py 端口号 最大密码尝试次数

  • Client: python client.py 服务器IP 服务器端口 客户端UDP端口

效果展示


代码

import socket

import threading

import time

import sys

import os

UDP_port = 6666

LoginStatus = False

TCP_running = True

clientSocket = socket.socket()

login_Event = threading.Event()

response_Event = threading.Event()

user_list = []

USERNAME = ""

# get IP address and port after ATU command

def get_address_port_by_username(name):

if len(user_list) == 0:

print("[error] need run ATU command first")

else:

for i in user_list:

user_info = i.split(", ")

username = user_info[0]

address = user_info[1]

port = user_info[2]

if username == name:

return address, port

return None

# LOGIN

def log_in(clientSocket,udp_port):

global USERNAME

print("Username: ", end="")

name = input('')

print("Password: ", end="")

pwd = input('')

FLAG = "LOGIN"

USERNAME = name

PWD = pwd

login_msg = FLAG + "%%" + name + "%%" + PWD + "%%" + str(udp_port)

clientSocket.send(login_msg.encode('utf-8'))

def reponse_to_client(recv_msg):

global TCP_running

global LoginStatus

global login_Event

send_msg = ""

recv_msg_list = []

recv_msg_list = recv_msg.split("%%")

res = True

if len(recv_msg_list) != 0:

flag = recv_msg_list[0]

if (flag == "LOGIN"):

TYPE = recv_msg_list[1]

if(TYPE == "0"):

LoginStatus = True

else:

LoginStatus = False

# inform the main loop the result from server

login_Event.set()

print(recv_msg_list[2])

elif (flag == "MSG"):

print(recv_msg_list[1])

elif flag == "EDT":

TYPE = recv_msg_list[1]

if (TYPE == "0"):

edit_res = True

else:

edit_res = False

print(recv_msg_list[2])

elif (flag == "RDM"):

txt = recv_msg_list[1:]

for i in txt:

print(i)

pass

elif (flag == "DLT"):

TYPE = recv_msg_list[1]

if (TYPE == "0"):

del_res = True

else:

del_res = False

print(recv_msg_list[2])

pass

elif (flag == "ATU"):

NUM = recv_msg_list[1]

if NUM == "0":

print(recv_msg_list[2])

else:

txt = recv_msg_list[2:]

for i in txt:

print(i)

user_list.append(i)

elif (flag == "OUT"):

TCP_running = False

print(recv_msg_list[1])

def read_server(s):

global response_Event

global TCP_running

while TCP_running:

# the thread always receive from server

content = s.recv(2048).decode('utf-8')

reponse_to_client(content)

# inform the mian loop, resume it

response_Event.set()

# s.close()

os._exit(1)

def execute_command(clientSocket):

response_Event.clear()

while TCP_running:

print("Enter one of the following commands (MSG, DLT, EDT, RDM, ATU, OUT, UPD): ", end="")

msg = input('')

if not msg:

continue

command = msg.split(" ")[0]

if command == "MSG":

if len(msg.split(" ")) < 2:

print("need arguments")

continue

txt = msg.split(" ")[1:]

msg = command + "%%" + ' '.join(txt)

elif command == "EDT":

if len(msg.split(" ")) < 2:

print("[error] need arguments")

continue

args = msg.split(" ")[1:]

index = args[0][1:]

dt = ' '.join(args[1:5])

txt = ' '.join(args[5:])

msg = command + "%%" + index + "%%" + dt + "%%" + txt

elif command == "DLT":

if len(msg.split(" ")) < 2:

print("[error] need arguments")

continue

args = msg.split(" ")[1:]

index = args[0][1:]

dt = ' '.join(args[1:])

msg = command + "%%" + index + "%%" + dt

pass

elif command == "RDM":

if len(msg.split(" ")) < 2:

print("[error] need arguments")

continue

dt = ' '.join(msg.split(" ")[1:])

msg = command + "%%" + dt

elif command == "ATU":

if len(msg.split(" ")) < 2:

pass

else:

print("[error] too many arguments")

continue

pass

elif command == "OUT":

if len(msg.split(" ")) < 2:

pass

else:

print("[error] too many arguments")

continue

pass

elif command == "UPD":

if len(msg.split(" ")) < 3:

print("[error] need more arguments")

continue

else:

UDP_send(msg.split(" ")[1], msg.split(" ")[2])

continue

else:

print("[error] unknown command")

continue

clientSocket.send(msg.encode('utf-8'))

# block, waiting for server response

time.sleep(0.3)

response_Event.wait(30)

response_Event.clear()

# mian func

def tcp_start(server_IP, server_port, udp_port):

global clientSocket

clientSocket = socket.socket()

clientSocket.connect((server_IP, server_port))

TCP_t = threading.Thread(target=read_server, args=(clientSocket,)).start()

# login

while LoginStatus == False:

log_in(clientSocket, udp_port)

# waiting for server ...

time.sleep(0.3)

# block, waiting for server response

login_Event.wait(30)

login_Event.clear()

if LoginStatus:

# print("Debug: log in success")

pass

else:

# print("Debug: log in fail")

pass

# commands

execute_command(clientSocket)

def UDP_send(username, filename):

global USERNAME

address, port = get_address_port_by_username(username)

BUF_SIZE = 1024

# print(address, port)

server_addr = (address, int(port))

client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

count = 0

f = open(filename, 'rb')

while True:

if count == 0:

# send the first message inculding filename

data = "UDP%%"+USERNAME+"_"+filename

client.sendto(data.encode('utf-8'), server_addr)

data = f.read(BUF_SIZE)

if str(data) != "b''":

client.sendto(data, server_addr)

else:

client.sendto('end'.encode('utf-8'), server_addr) # end for the file and send a speical "end" flag

# lecture1.mp4 has been uploaded

print(filename + " has been uploaded.")

execute_command(clientSocket)

break

count += 1

time.sleep(0.001)

f.close()

client.close()

def UDP_recv(udp_port):

BUF_SIZE = 1024

server_addr = ('127.0.0.1', udp_port)

# UDP: socket.SOCK_DGRAM

server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

server.bind(server_addr)

count = 0

f = None

filename = ""

while True:

data, addr = server.recvfrom(BUF_SIZE)

if count == 0:

# recive the starting message inculding filename

recv_data_array = data.decode('utf-8').split("%%")

if len(recv_data_array) > 1:

if recv_data_array[0] == "UDP":

filename = recv_data_array[1]

f = open(filename, 'wb')

count += 1

elif str(data) != "b'end'":

try:

f.write(data)

# print(count)

except:

print("[erroe] can not write")

count += 1

else:

f.close()

# print(count)

count = 0

print("\nReceived " + filename.split("_")[1] + " from " + filename.split("_")[0])

if __name__ == '__main__':

# python client.py server_IP server_port client_udp_server_port

if len(sys.argv) > 3:

server_IP = sys.argv[1]

server_port = int(sys.argv[2])

client_udp_server_port = int(sys.argv[3])

udp_recv_t = threading.Thread(target=UDP_recv, args=(client_udp_server_port,)).start()

tcp_start(server_IP, server_port, client_udp_server_port)

else:

print("[error] Usage: python client.py server_IP server_port client_udp_server_port ")

# just for test

# server_IP = "127.0.0.1"

# server_port = 9000

# client_udp_server_port = 7777

# udp_recv_t = threading.Thread(target=UDP_recv, args=(client_udp_server_port,)).start()

# tcp_start(server_IP, server_port, client_udp_server_port)

# Python 3

# coding: utf-8

from socket import *

import time

import sys

import threading

import traceback

# If true, print debug information

__DEBUG = False

# max number of consecutive failed attempts

MAX_ERR_COUNT = 0

# server IP

serverIP = '127.0.0.1'

# record clients who try to connect to server

client_connect_list = []

# ClientInfo class

# manager client infomation

class ClientInfo():

m_IP=""

m_loginTimestamp = -1

m_preLoginTime = -1

m_username=""

m_UDPServerPort = -1

m_errorCount = 0

def __init__(self, IP):

self.m_IP = IP

def add_logInfo(self, name, timestamp, UDPport):

self.m_loginTimestamp = timestamp

self.m_username = name

self.m_UDPServerPort = UDPport

if self.m_errorCount < MAX_ERR_COUNT:

self.m_preLoginTime = self.m_loginTimestamp

# print("Debug: self.m_errorCount: " + str(self.m_errorCount))

return True

else:

if (int(self.m_loginTimestamp) - int(self.m_preLoginTime) > 30):

self.m_errorCount = 0

self.m_errorCount = self.m_errorCount + 1

return True

else:

return False

# record number_of_consecutive_failed_attempts

def error_password(self):

self.m_errorCount = self.m_errorCount + 1

# check name and password

def check_credential(username, password):

res = False

with open('credentials.txt', 'r') as file:

credential_list = file.readlines()

for credential in credential_list:

name = credential.strip().split(" ")[0]

pwd = credential.strip().split(" ")[1]

if (name==username and pwd == password):

res = True

break

if __DEBUG: print("check_credential: res: " , res)

return res

# Get client infomation by IP

def get_client_from_connect_list(IP):

for c in client_connect_list:

if c.m_IP == IP:

return c

return None

# Add a new client to cse_userlog.txt

def add_user_log(name, timestamp, IP, UDPport):

dt = time.strftime("%d %b %Y %H:%M:%S", time.localtime(int(timestamp)))

address = IP[0]

txt = dt + "; " + name + "; " + address + "; " + str(UDPport)

index = 0

# read old file to get log index

with open('cse_userlog.txt', mode='r', encoding="utf-8") as user_file:

log_list = user_file.readlines()

if log_list:

last_line = log_list[-1]

index = int(last_line.strip().split("; ")[0])

else:

index = 0

# write new log in file

with open('cse_userlog.txt', mode='a+', encoding="utf-8") as user_file:

msg = str(index+1) + '; ' + txt

user_file.write(msg + "\n")

# OUT: delete a client from cse_userlog.txt and adjust other client's index

def del_user_log(name):

new_log_list=[]

index = 1

with open('cse_userlog.txt', mode='r', encoding="utf-8") as user_file:

log_list = user_file.readlines()

for log in log_list:

log_arr = log.split("; ")

log_txt = log_arr[1:]

log_prot = log_arr[4]

log_addr = log_arr[3]

log_name = log_arr[2]

if log_name == name:

if __DEBUG: print("Debug: find same IP need to delete: " + str(log_arr[0]))

pass

else:

txt = "; ".join(i for i in log_txt)

msg = str(index) + "; " + txt

index = index + 1

new_log_list.append(msg)

with open('cse_userlog.txt', mode='w', encoding="utf-8") as user_file:

user_file.writelines(new_log_list)

# ATU: read other user log from cse_userlog.txt and return

def show_user_log_exclude_self(name):

new_log_list=[]

with open('cse_userlog.txt', mode='r', encoding="utf-8") as user_file:

log_list = user_file.readlines()

for log in log_list:

log_arr = log.split("; ")

log_txt = log_arr[1:]

log_name = log_arr[2]

if log_name == name :

if __DEBUG: print("Debug: find same name no need to show: " + str(log_arr[0]))

pass

else:

txt = "; ".join(i for i in log_txt)

msg = txt

new_log_list.append(msg)

return new_log_list

# MSG: When someone posts a new message, add it to the messagelog.txt

def add_msg_log(name, timestamp, txt):

dt = time.strftime("%d %b %Y %H:%M:%S", time.localtime(int(timestamp)))

log_txt = dt + "; " + name + "; " + txt

index = 0

with open('messagelog.txt', mode='r', encoding="utf-8") as msg_file:

log_list = msg_file.readlines()

if log_list:

last_line = log_list[-1]

index = int(last_line.strip().split("; ")[0])

else:

index = 0

with open('messagelog.txt', mode='a+', encoding="utf-8") as msg_file:

msg = str(index + 1) + '; ' + log_txt + '; ' + 'no'

msg_file.write(msg + "\n")

return index + 1

# convert formatted date time to timestamp

def dt_to_timestamp(dt):

timeArray = time.strptime(dt, "%d %b %Y %H:%M:%S")

timeStamp = int(time.mktime(timeArray))

return timeStamp

# RDM: read message log by different time

def read_msg_log(timestamp):

new_log_list=[]

with open('messagelog.txt', mode='r', encoding="utf-8") as msg_file:

log_list = msg_file.readlines()

for log in log_list:

log_arr = log.split("; ")

log_dt = log_arr[1]

log_timestamp = dt_to_timestamp(log_dt)

log_name = log_arr[2]

if log_timestamp < timestamp:

pass

else:

new_log_list.append(log)

return new_log_list

# DLT: delete a client's own message by time

def del_msg_log(name, del_index, del_dt):

del_res = False

del_txt = ""

new_log_list=[]

index = 1

with open('messagelog.txt', mode='r', encoding="utf-8") as msg_file:

log_list = msg_file.readlines()

for log in log_list:

log_arr = log.split("; ")

log_txt = log_arr[1:]

log_dt = log_arr[1]

log_name = log_arr[2]

if log_dt == del_dt and log_name == name and index == del_index:

del_res = True

del_txt = log_arr[3]

if __DEBUG: print("Debug: find same name and index and time need to delete: " + str(log_arr[0]))

pass

else:

txt = "; ".join(i for i in log_txt)

msg = str(index) + "; " + txt

index = index + 1

new_log_list.append(msg)

with open('messagelog.txt', mode='w', encoding="utf-8") as msg_file:

msg_file.writelines(new_log_list)

return del_res, del_txt

# EDT: edit a client's own message by time

def edit_msg_log(name, edit_index, edit_dt, edit_txt):

edit_res = False

new_log_list=[]

edit_time = ''

with open('messagelog.txt', mode='r', encoding="utf-8") as msg_file:

log_list = msg_file.readlines()

for log in log_list:

log_arr = log.split("; ")

log_index = log_arr[0]

log_txt = log_arr[3]

log_dt = log_arr[1]

log_name = log_arr[2]

if log_dt == edit_dt and log_name == name and log_index == str(edit_index):

edit_res = True

edit_time = time.strftime("%d %b %Y %H:%M:%S", time.localtime(int(time.time())))

log_arr[3] = edit_txt

log_arr[1] = edit_time

log_arr[4] = "yes\n"

if __DEBUG: print("Debug: find same name and index and time need to edit: " + str(log_arr[0]))

log = "; ".join(log_arr)

new_log_list.append(log)

if __DEBUG: print("Debug: new log ", log)

else:

new_log_list.append(log)

with open('messagelog.txt', mode='w', encoding="utf-8") as msg_file:

msg_file.writelines(new_log_list)

return edit_res, edit_time

# parse the msg from client and make different responses based on different client commands

def reponse_to_client(clientSocket, IP, recv_msg):

socket_status = True

send_msg = ""

recv_msg_list = []

recv_msg_list = recv_msg.split("%%")

pwd_status = True

if len(recv_msg_list) != 0:

flag = recv_msg_list[0]

else:

print("[error] parse error")

pass

if __DEBUG: print("Debug: flag is " + flag)

client = get_client_from_connect_list(IP)

# response to login

if (flag == "LOGIN"):

name = recv_msg_list[1]

pwd = recv_msg_list[2]

UDPPort = recv_msg_list[3]

pwd_status = check_credential(name, pwd)

# if wrong name and password, number of consecutive failed attempts adds 1

if not pwd_status:

client.error_password()

if client is None:

client = ClientInfo(IP)

client_connect_list.append(client)

# save user information

add_status = client.add_logInfo(name, time.time(), UDPPort)

if add_status == True:

if pwd_status == False:

TYPE = "1"

if client.m_errorCount == MAX_ERR_COUNT:

DATA = "Invalid Password. Your account has been blocked. Please try again later"

else:

DATA = "Invalid Password. Please try again!"

elif pwd_status == True:

TYPE = "0"

DATA = "Log in sucessfully!"

add_user_log(name, client.m_loginTimestamp, IP, client.m_UDPServerPort)

server_output = client.m_username + " log in sucessfully"

print(server_output)

else:

TYPE = "2"

DATA = "Your account is blocked due to multiple login failures. Please try again later"

FLAG = flag

send_msg = FLAG + "%%" + TYPE + "%%" + DATA

# response to "MSG" command

elif (flag == "MSG"):

txt = recv_msg_list[1]

if __DEBUG: print("Debug: " + txt)

timestamp = time.time()

msg_index = add_msg_log(client.m_username, timestamp, txt)

dt = time.strftime("%d %b %Y %H:%M:%S", time.localtime(int(timestamp)))

FLAG = flag

DATA = "Message #" + str(msg_index) + " posted at " + dt

send_msg = FLAG + "%%" + DATA

server_output = client.m_username + " posted MSG #" + str(msg_index) + " \"" + txt + "\" at " + dt + "."

print(server_output)

# response to "RDM" command

elif (flag == "RDM"):

FLAG = flag

DATA = ""

dt = recv_msg_list[1]

try:

timestamp = dt_to_timestamp(dt)

except Exception as e:

print(e)

send_msg = FLAG + "%%" + "unknown time, please check time format"

clientSocket.send(send_msg.encode('utf-8'))

return

log_list = read_msg_log(timestamp)

if len(log_list) == 0:

DATA = "no new message"

else:

# print(log_list)

for log in log_list:

log_arr = log.split("; ")

log_edit_status = log_arr[4]

log_index = log_arr[0]

log_name = log_arr[2]

log_txt = log_arr[3]

log_dt = log_arr[1]

if log_edit_status.strip() == "no":

log_edit_status = "posted"

elif log_edit_status.strip() == "yes":

log_edit_status = "edited"

m = "#" + log_index + " " + log_name + ", " + log_txt + ", " + log_edit_status + " at " + log_dt + "%%"

DATA = DATA + m

send_msg = FLAG + "%%" + DATA

#Yoda issued RDM command

server_output = client.m_username + " issued RDM command."

print(server_output)

# response to "EDT" command

elif (flag == "EDT"):

FLAG = flag

edit_index = int(recv_msg_list[1])

edit_dt = recv_msg_list[2]

edit_txt = recv_msg_list[3]

edit_res, edit_time = edit_msg_log(client.m_username, edit_index, edit_dt, edit_txt)

if edit_res:

TYPE = "0" # del success

DATA = "Message #" + str(edit_index) + " edited at " + edit_time + "."

server_output = client.m_username + " edited MSG #" + str(edit_index) + " \"" + edit_txt + "\" at " + edit_time + "."

print(server_output)

else:

TYPE = "1" # del fail

DATA = "Edit message fail, can not find such message"

server_output = client.m_username + " " + DATA

print(server_output)

send_msg = FLAG + "%%" + TYPE + "%%" + DATA

pass

# response to "DLT" command

elif (flag == "DLT"):

FLAG = flag

del_index = int(recv_msg_list[1])

del_dt = recv_msg_list[2]

del_res, del_txt = del_msg_log(client.m_username, del_index, del_dt)

del_time = time.strftime("%d %b %Y %H:%M:%S", time.localtime(int(time.time())))

if del_res:

TYPE = "0" # del success

DATA = "Message #" + str(del_index) + " deleted at " + del_time + "."

server_output = client.m_username + " deleted MSG #" + str(

del_index) + " \"" + del_txt + "\" at " + del_time + "."

print(server_output)

else:

TYPE = "1" # del fail

DATA = "Delete message fail, can not find such message"

server_output = client.m_username + " " + DATA

print(server_output)

send_msg = FLAG + "%%" + TYPE + "%%" + DATA

# response to "ATU" command

elif (flag == "ATU"):

FLAG = flag

DATA = ""

user_list = show_user_log_exclude_self(client.m_username)

NUM = len(user_list)

if len(user_list) == 0:

DATA = "no other active user"

else:

for user in user_list:

user_arr = user.split("; ")

user_dt = user_arr[0]

user_name = user_arr[1]

user_address = user_arr[2]

user_port = user_arr[3]

txt = user_name + ", " + user_address + ", " + user_port.strip() + ", active since " + user_dt + "."

DATA = DATA + txt + '%%'

send_msg = FLAG + "%%" + str(NUM) + "%%" + DATA

server_output = client.m_username + " issued ATU command."

print(server_output)

# response to "OUT" command

elif (flag == "OUT"):

del_user_log(client.m_username)

client = get_client_from_connect_list(IP)

FLAG = flag

send_msg = FLAG + "%%" + "Bye, " + client.m_username + "!"

socket_status = False

server_output = client.m_username + " logout"

print(server_output)

if send_msg != "":

clientSocket.send(send_msg.encode('utf-8'))

if not socket_status:

clientSocket.close()

# Try to recvice msg from client

def read_client(socket, IP):

try:

return socket.recv(2048).decode('utf-8')

except:

# if exception, indicating that the connection failed, the client is deleted

print(str(IP) + ' Left!')

# Always wait for a message from the client

def recv_handler(socket, clientAddress):

try:

while True:

content = read_client(socket, clientAddress)

if content is None:

break

else:

if __DEBUG: print(str(clientAddress) + " received: " + content)

reponse_to_client(socket, clientAddress, content)

except Exception as e:

# raise Exception

print("[error]: ", e)

exstr = traceback.format_exc()

if __DEBUG: print("debug: " + exstr)

pass

def start(server_port):

serverSocket = socket(AF_INET, SOCK_STREAM)

serverSocket.bind((serverIP, server_port))

serverSocket.listen(1)

print("Server start ... ")

print("waiting client ...")

while True:

clientSocket, clientAddress = serverSocket.accept()

# record each client when server accept successfully

client = ClientInfo(clientAddress)

client_connect_list.append(client)

if __DEBUG: print(str(clientAddress) + ' is trying to connect!')

# start recv_handler for each client

threading.Thread(target=recv_handler, args=(clientSocket, clientAddress)).start()

if __name__ == '__main__':

# create messagelog.txt

msg_file = open('messagelog.txt', 'w')

msg_file.close()

# create cse_userlog.txt

user_file = open('cse_userlog.txt', 'w')

user_file.close()

# python server.py server_port number_of_consecutive_failed_attempts

if len(sys.argv) > 2:

server_port = int(sys.argv[1])

MAX_ERR_COUNT = int(sys.argv[2])

start(server_port)

else:

print("[error] Usage: python server.py server_port number_of_consecutive_failed_attempts ")

# just for test

# server_port = 9000

# MAX_ERR_COUNT = 6

# start(server_port)

以上是 Python3 网络通信 网络聊天室 文件传输 的全部内容, 来源链接: utcz.com/z/387330.html

回到顶部