目录
写在前面的话
概览
环境
URL请求程序:
2. 系统时间查询
服务端
T_TCPServer.py代码
客户端
T_TCPClient.py代码
运行效果
3. 网络文件传输
服务端
TF_TCPServer.py代码
运行效果(后面加了远程功能,效果图暂时还在本地)
4. 网络聊天室
服务端
UDPServer.py代码
客户端
UDPClient.py代码
运行效果
开启服务器,jennie客户端和john客户端陆续加入群聊
聊天(4栏:服务器,mike, john, jennie)
mike回复消息同样被转发给全部用户
用户退出
问题及解决:
URL 请求程序
系统时间查询
网络文件传输
网络聊天室
实验心得体会
总结
写在前面的话
这个危,开学初老师曾觉得这个编程实验可能会劝退我这个小白。emmm熬过来了。上学期有老师说得先自学下Java搞这个,emmm寒假整c++一些个小系统,Java纯纯学点点写几个基本小系统罢了觉得不太可。幸好我的班用python。前年学了点毛皮捡捡还能用,1周速成python,再1周速成个多人聊天室,爬过来就可。
概览
▪ UDP与TCP套接字的区别
▪ UDP和TCP套接字编程方法
▪ 简单网络应用的编程思路
▪ 网络编程相关的一些库
1. URL 请求程序
2. 系统时间查询
3. 网络文件传输
4. 网络聊天室
环境
▪ 具有Internet连接的主机, VScode(直接在其终端运行),PowerShell
▪ 编程语言: python
-
URL请求程序:
请求一个网页,并存储为html文件,计算所请求网页的大小。打印所请求网页的URL、存储文件名、文件大小等信息。(图1 2)
Figure 1 url请求程序
调用requests库请求对应url并保存。将url切片获得文件名,通过os库获得文件大小。
Figure 2 保存下来HTML网页文件
2. 系统时间查询
▪ 实现一个基于客户/服务器的系统时间查询程序。
▪ 传输层使用TCP。
▪ 交互过程
1) 客户端向服务器端发送字符串Time。
2) 服务器端收到该字符串后,返回当前系统时间。
3) 客户端向服务器端发送字符串Exit。
4) 服务器端返回Bye,然后结束TCP连接。
-
服务端
服务端创建TCP协议的socket套接字进程,端口号为12000,并保持监听状态。一旦收到客户端的连接就从socket套接字中获取客户端发送的消息。 如果收到无效信息回复提示。直到Exit才停止收发并断开连接。并可以与另外的客户端通信。
T_TCPServer.py代码
from socket import *
import time#获得系统时间
serverPort = 12000
serverSocket = socket(AF_INET, SOCK_STREAM)
serverSocket.bind(('', serverPort))
serverSocket.listen(1)
print('----------------------------------')
print('服务器已经准备好连接了')
print('服务器地址为:',serverSocket.getsockname())
while True:
connectionSocket, addr = serverSocket.accept()
print('----------------------------------')
print('接收到一个新连接')
print('连接地址为:',serverSocket.getsockname())
print('客户端地址为:',addr)
while True:
request = connectionSocket.recv(1024).decode()
if request == '':
continue
print('收到请求: ', request)
if request == 'Time':
# 格式化成2016-03-20 11:45:39形式
now = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
print('发送响应:',now)
connectionSocket.send(('收到当前服务器上系统时间为:'+now).encode())
elif request == 'Exit':
print('发送响应:Bye')
connectionSocket.send('收到回复:Bye'.encode())
connectionSocket.close()#结束TCP连接
break#跳出循环
else:
print('发送响应:输入有误,请重新输入')
connectionSocket.send('输入有误,请重新输入'.encode())
-
客户端
考虑远程实现,需要手动输入服务端IP及端口号12000。通过ipconfig查询,本机IP:192.168.0.107。
T_TCPClient.py代码
from socket import *
clientSocket = socket(AF_INET, SOCK_STREAM)
print('1个客户端正在运行')
serverName = input('请输入要连接的服务端IP:')
serverPort = int(input('请输入要连接的服务端的端口号:'))
clientSocket.connect((serverName, serverPort))
print('客户端地址:',clientSocket.getsockname())
print('连接到',clientSocket.getpeername())
while True:
request = input('发送一条请求:')
clientSocket.send(request.encode())#请求发送
modifiedSentence = clientSocket.recv(1024)#回复报文
print(modifiedSentence.decode())#打印回复
if request == 'Exit':
clientSocket.close()
break
-
运行效果
先启动服务端,再启动客户端A连接并交互,发送“Time”时服务端回复系统时间,发送“Exit”时回复“Bye”并断开与客户端的连接。发送命令时无效,并回复提示信息。断开与客户端A的连接后服务端还可以继续与其他客户端通信。(图3)
Figure 3 系统时间查询
3. 网络文件传输
▪ 实现一个基于客户/服务器的网络文件传输程序。
▪ 传输层使用TCP。
▪ 交互过程
1) 客户端从用户输入获得待请求的文件名。
2) 客户端向服务器端发送文件名。
3) 服务器端收到文件名后,传输文件。
4) 客户端接收文件,重命名并存储在硬盘。
-
服务端
同样服务端需要先创建TCP协议下的与固定端口号绑定的进程套接字socket,并保持监听状态等待与客户端连接。每次发送文件分组64字节,直至发送完毕后关闭连接。引入os库来查看文件是否存在,并返回提示。
TF_TCPServer.py代码
from socket import *
clientSocket = socket(AF_INET, SOCK_STREAM)
print('-----------------------------')
print('客户端正在运行')
serverName = input('请输入要连接的服务端IP:')
serverPort = int(input('请输入要连接的服务端的端口号:'))
clientSocket.connect((serverName, serverPort))
print('客户端地址:',clientSocket.getsockname())
print('连接到',clientSocket.getpeername())
filename = input('请输入所请求的文件名:')
clientSocket.send(filename.encode())#请求发送
print('已发送文件名 【', filename, '】 至服务器')
modifiedSentence = clientSocket.recv(1024).decode()#回复报文
print(modifiedSentence)#打印回复报文
if modifiedSentence == 'ok':
file_size = clientSocket.recv(1024).decode()#获得文件大小
print('文件大小为:', file_size, ' 字节')#打印回复报文
print('-----------------------------')
f = open(filename, 'wb')#新建待写入文件
while True:
r = clientSocket.recv(64)#每次接收64字节报文
if len(r)==0:
print('-----------------------------')
print('1个名为 【', filename, '】、大小为 ', file_size, ' 字节的文件已被保存')
print('接收完毕!')
print('-----------------------------')
break#发完退出
print('收到 ', len(r), ' 字节的数据')
f.write(r)#写入文件
clientSocket.close()
-
运行效果(后面加了远程功能,效果图暂时还在本地)
先启动好服务器并保持监听状态等待连接,在启动客户端与服务器建立连接(TCP),发送所请求传输的文件名。服务器收到文件名后利用os库查询是否存在文件。文件不存在返回提示信息并断开连接。文件存在则开始传输每次64字节的文件分组,直至检测到待传输文件分组为0字节时停止传输,并提示传输完毕信息,断开连接。客户端在每次收到文件分组的时候将其存于客户端本地的同名文件。直至所收到分组为0字节时确定接收完毕,打印提示信息并关闭连接。
4. 网络聊天室
▪ 实现一个基于客户/服务器的网络聊天程序。
▪ 要求实现多个用户的群聊。要求客户端打印聊天消息,服务器打印系统信息。
▪ 传输层使用UDP。
▪ 不要求实现GUI界面。
-
服务端
服务器程序维护一个聊天室用户字典列表userList来了解聊天室里有哪些用户(客户端),映射关系为IP:name。新用户到达时加进来,旧用户离开时删除。每次用户发来消息,通过知道聊天室里有哪些用户(客户端),确定该用户是否是新用户。
需要实现的群聊功能是一个用户发消息,所有用户都能收到。相应地,一个客户端把聊天消息发给服务器,服务器再将收到的消息转发给所有客户端。所以在服务端需要有一个SendUser() 函数来实现每次收到信息转发给用户表内全部用户相同信息的功能。
服务端创建与固定端口号绑定的socket并开启等待消息,消息有3类:新用户加入的昵称消息,‘quit’用户退出群聊消息,一般聊天消息。
第一类为新用户加入的昵称消息。如果所收到客户端的IP是用户字典里不存在的,则可判断为新用户且发送过来的消息为他的昵称。回复欢迎并将新用户加入消息转发给全部用户(用户字典里的)。
第二类“quit”消息。当用户发送“quit”时,用户退出群聊,服务端断开与其的连接并将该消息转发给全部用户。
第三类一般聊天消息。如果所接受消息并非上面2类,就可判断其为一般聊天消息,服务器需要将其加上用户名转发给用户字典里的全部用户。用户名可通过消息的IP地址在用户字典里查询到。
UDPServer.py代码
from socket import *
userList = {} #创建userList(map ip:name)
def SendUsers(userList, data):#将数据发送给全部用户
for user in userList:
serverSocket.sendto(data.encode(), user)
print('发送给 ', user, ' ---> ',data)
serverPort = 12000
serverSocket = socket(AF_INET, SOCK_DGRAM)
serverSocket.bind(('', serverPort))
print('服务器已经准备好接收了')
while True:
message, clientAddress = serverSocket.recvfrom(2048)
m = message.decode()#解码报文
print('收到来自 ', clientAddress, ' 的信息 ---> ', m)
#如果所接收用户地址不在,则为新用户,加入列表
if not clientAddress in userList:
userList[clientAddress] = m
SendUsers(userList, m + ' 已经加入群聊!')
elif m == 'quit':#
SendUsers(userList, userList[clientAddress]+' 已经离开群聊!')
del userList[clientAddress]
else:#聊天
SendUsers(userList, userList[clientAddress]+':'+m )
-
客户端
客户端为了保证收发同时,需要两个线程实现的。一个线程Receive() 负责接收并显示消息,另一个线程Send() 负责获取输入和发送消息。Python里threading库可以实现多线程编程。
UDPClient.py代码
from socket import *
import threading#多线程
import time
#发送函数线程
def Send(clientSocket, serverName, serverPort):
while True:
m = input()
clientSocket.sendto(m.encode(), (serverName, serverPort))
if m == 'quit':
time.sleep(1)#延时1秒再关闭连接,避免收发线程冲突
clientSocket.close()#发送退出则断开连接并跳出循环
break
#接收函数线程
def Receive(clientSocket):
while True:
try: # 利用 close 作为退出标志
modifiedMessage, serverAddress = clientSocket.recvfrom(2048)
print(modifiedMessage.decode())
except:
break
serverName = 'localhost'
serverPort = 12000
clientSocket = socket(AF_INET, SOCK_DGRAM)#创建客户端进程套接字
#发送昵称给服务器
name = input('请输入昵称:')
clientSocket.sendto(name.encode(), (serverName, serverPort))
print('欢迎', name, ',输入 <quit> 退出群聊!')
#创建接收进程并启动
th_Send = threading.Thread(target=Send, args=(clientSocket, serverName, serverPort))
th_Receive = threading.Thread(target=Receive, args=(clientSocket,))
th_Send.start()
th_Receive.start()
-
运行效果
总体效果
-
开启服务器,jennie客户端和john客户端陆续加入群聊
服务器先开启,jennie客户端先发送昵称加入消息,服务器收到消息并将jennie加入用户字典并向jennie回复欢迎信息,并将新用户加入消息转发给全部用户(当前用户字典仅有jennie)。接下来john加入,同样的过程,这时全部用户(jennie,john)同时收到转发消息,后面还有Mike加入下图未展示(左:服务器,中:john客户端2,右:jennie客户端1)
-
聊天(4栏:服务器,mike, john, jennie)
jennie先发送消息“你们知道那时候可以会深大吗”,服务器收到消息后通过查询用户字典知道jennie为老用户,发送的是一般聊天信息,则会加上jennie昵称把该消息转发给全部用户(用户字典里:jennie, john, mike),全部用户客户端可以收到该条消息。
-
mike回复消息同样被转发给全部用户
-
用户退出
用户在发送‘quit’给服务器后,服务器会将信息该用户退出信息转发给全部用户,而该用户也会等待1s后(避免与receive线程冲突)在关闭客户端连接。如图中mike退出。
mike退出后其他用户继续聊天,服务器也继续接收。
问题及解决:
-
URL 请求程序
- 在创建文件名名时可以直接通过接受的url字符串切片获得。
- 可通过os库的相关功能获得文件大小
-
系统时间查询
- 在“系统时间查询”实验部分中,我发现要实现远程就必须添加服务器地址的输入。也必须获得本机IP。通过ipconfig查询,本机IP:192.168.0.107
- 通过socket库的getsockname() 函数可获得本地进程IP地址和端口号,getpeername() 获得所连接的远程服务端的地址和端口号。
- 利用time库获得格式化系统时间表达。
-
网络文件传输
- 通过文件分组的大小是否为0判断是否文件发送完毕。
- 利用os库判断文件是否存在
-
网络聊天室
- 显示套接字不可以作为线程函数参数,经查阅资料发现线程参数接受的是元组,需要在参数参数后面加‘,’代表这是个元组。
- 某一个用户退出时,服务器显示某个远程主机强制断开连接报错,而代码里我在用户发送‘quit’后就断开该客户端,可能会导致后续客户端继续收服务器信息的receive线程出现问题,所以为了避免冲突我在用户发送‘quit’后等待1s再关闭客户端连接,避免客户端receive线程发生冲突,问题也解决了。
实验心得体会
通过本次实验,学会通过python的requests库来完成url请求并获得网页文件和相关信息。同时可利用os库获取文件具体信息。
我加深关于TCP/UDP套接字的认识,了解了他们的区别,比如TCP套接字连接后服务端就可以保持监听状态,创建连接后的短期内通信与同一客户端无需再次连接。而UDP并无严格意义的连接过程,服务端也没有监听的概念。
另外我通过该次试验,初步学习了socket编程方法,通过套接字的创建实现服务端与客户端基于TCP/UDP协议的进程通信,实现文件传输。
学会了基于此再利用多线程编程的知识即可实现多人聊天功能,比如在这里通过利用python的threading库实现客户端的收发双线程并发通信。注意的是当客户端准备关闭连接的时候,需等待一段时间避免接收线程由于管道的提前关闭而导致出错。
总结
我们可以通过python的requests库来完成url请求并获得网页文件和相关信息。
通过套接字的创建实现服务端与客户端基于TCP/UDP协议的进程通信,实现文件传输。文章来源:https://www.toymoban.com/news/detail-430039.html
基于此再利用多线程编程的知识即可实现多人聊天功能,比如在这里通过利用python的threading库实现客户端的收发双线程并发通信。注意的是当客户端准备关闭连接的时候,需等待一段时间避免接收线程由于管道的提前关闭而导致出错。文章来源地址https://www.toymoban.com/news/detail-430039.html
到了这里,关于【计算机网络】4 Socket网络编程的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!