计算机网络-网络基础编程实验(JAVA\Python3)
一.实验目的
通过本实验,学习采用Socket(套接字)设计简单的网络数据收发程序,理解应用数据包是如何通过传输层进行传送的。
二.实验内容
学习套接字编程,完成以下的网络数据收发程序。
1.采用TCP进行数据发送的简单程序
2.采用UDP进行数据发送的简单程序
3.多线程/线程池对比
4.简单的chat程序,并能实现互传文件
三.实验过程
使用python语言进行网络数据收发程序的实现。在本机建立客户端和服务器程序,服务器IP使用127.0.0.1。
1.采用TCP进行数据发送的简单程序
客户端:
声明服务器名和端口,通过套接字建立TCP连接。连接建立后向服务器发送一个字符串,等待接收服务器的字节,接收到回车符结束后,关闭客户的套接字,客户和服务器之间的TCP连接也会关闭。
服务器端:
服务器同样需要创建套接字,并将这个创建的套接字和端口绑定,该套接字将监听来自客户的TCP连接请求。当客户发起连接请求后,服务器会创建一个新的套接字,由客户专用。使用新的套接字,服务器就可以和客户通过TCP连接进行通信了。
运行服务器程序和客户端程序,可以进行通信:
2.采用UDP进行数据发送的简单程序
客户端:
基本与TCP相同,区别是不需要建立连接,发送数据时需要指明服务器名和端口。
服务器:
与TCP相比,不需要欢迎套接字,也不需要监听客户端的连接请求,直接对接收到的数据进行处理,并通过套接字向客户端发送数据。
3.多线程/线程池对比
(1)多线程/线程池的对比
当有多个客户端向服务器发起请求时,服务器只能逐个处理客户端的请求,在处理某个客户端的请求时,不会对其他客户的请求作出响应。采用并行服务器的方式,每个连接单独处理,不产生干扰,实现并行服务的方式有两种,一种是为每个客户创建一个线程,另一种是使用线程池。
为每个客户都创建一个新的线程,每个新线程都要消耗系统资源。当客户较多时,线程数逐渐增加,消耗的资源也越来越多。且系统需要对线程进行管理,并处理线程之间的上下文切换。在客户较多的情况下,使用多线程处理客户的请求可能实际上增加了客户端总服务时间。
为了解决线程过多的问题,可以对总线程数进行限制,并重复使用线程。在服务器启动时创建一个由固定数量线程池组成的线程池,当收到一个客户端的连接请求,就将处理请求的任务交给线程池中的一个线程处理,处理结束后该线程将返回线程池。如果连接请求到达服务器时没有可用线程,请求将在队列中等待空闲的线程。
以下用Python语言实现多线程和使用线程池的服务器。尝试服务器程序实现多线程/线程池,需要先了解Python中的多线程及线程池。
(2)Python中的多线程
Python中的threading模块用于支持多线程的操作。使用该模块可以创建线程,并查看已有线程的状态。线程可分为守护线程和非守护线程。
守护线程:守护线程和主线程一起运行,主线程销毁,守护线程会和主线程一起销毁。
非守护线程:主线程销毁,非守护线程继续运行,不受影响。
创建线程时可以设定线程为守护线程。线程运行后,threading模块提供了一些方法支持多线程。如线程阻塞方法,用于实现同步与互斥的锁,条件变量,信号变量等。
(3)线程池
Python中concurrent.futures库中的ThreadPoolExecutor类可以实现线程池。
- ThreadPoolExecutor构造实例时,通过传入max_workers参数来设置线程池中最多能同时运行的线程数。
- 通过submit()方法提交线程需要执行的任务到线程池,返回该任务的句柄。
- done()方法可以判断任务是否结束,cancel()方法可以取消提交的任务,在线程池中已经运行的任务不可以取消。result()方法可以获取任务的返回值,这个方法是阻塞的。
(5)采用多线程的服务器程序
采用为每一个客户创建一个单独的线程服务,通过以上提到过的threading模块实现,具体实现如下:
使用多线程的客户端发起连接请求,运行程序:
(6)采用线程池的服务器程序
使用上述ThreadPoolExecutor创建一个可最多运行5个线程的线程池来对客户进行服务,具体实现如下:
运行程序,前三个请求是线程池中的三个线程同时处理完成的,三个请求处理完毕后,其中的两个线程又处理后两个请求。
4.可实现互传文件的简单chat程序
进行对话和互传文件的处理有所不同,因此先进行约定,客户端如果要发送或上传文件,输入的指令格式应该为[download/upload 文件路径],客户端确认消息类型为请求上传或下载文件,发送请求[download|文件路径]或[upload|文件名|文件大小],服务器收到消息后得知需要上传或下载文件,进行相应的回应(下载文件时回应[文件名|文件大小],上传时回应准备好上传),当双方都做好准备后,开始进行发送。
进行上传文件时,首先需要确认客户端给出的文件是否存在。如果文件存在,读取文件名和文件大小,并按照[upload|文件名|文件大小]的格式向服务器发送upload请求,服务器接收到请求后,拼接好文件的接收路径,创建文件,然后向客户端发送准备好接收文件的消息。客户端收到消息后,就可以开始读取文件,每次读取2048字节,并向服务器发送,服务器接收到后就将数据写入文件,直到文件大小的数据全部接收到。
客户端的部分代码如下:
服务器接收文件的代码如下:
下载文件的过程与接收文件类似,区别在于客户端需要先把文件名发送给服务器,服务器确认文件是否存在,如果文件存在,再进行类似上述上传文件的过程。
客户端下载文件的代码如下:
服务器传输文件的代码如下:
服务器程序采用(3)中编写的线程池服务器,运行程序,将client的文件a.txt上传到服务器,将服务器的b.docx文件下载到client的文件夹中。
客户端:
服务器:
传输结束后,可以在客户和服务器的文件夹client和server中找到下载/上传的文件。程序实现了文件互传的功能。
四.实验总结
通过本实验,学习了采用Socket(套接字)设计简单的网络数据收发程序,并编写了UDP数据发送程序和TCP数据发送程序,熟悉了使用Socket编程的基本方法。了解了服务器的多线程实现和线程池实现方式,并进行简单的实现。通过编写能传输文件的客户-服务器程序,加深了对于客户-服务器文件传输过程的理解。