1、TCP/IP 协议分析课程实验指导书实验 1 TCP 套接字编程实验实验目的:熟悉基于 java 的 TCP 套接字网络编程方法;熟悉 TCP Client 套接字编程流程;熟悉 TCP Server 套接字编程流程;熟悉 java TCP 套接字编程涉及的 java 类;实验任务:实现一个基本的 TCP 套接字 Client/Server 模式的网络应用系统,该系统中服务器端监听网络,接受客户端的连接请求,并接收客户端发送的数据,并原封部分的发回给客户端。即实现一个类似于 Echo 服务的服务器程序。指导说明:1. TCP 客户端基本编程流程1.1 TCP 客户端工作流程1. 创建流套接字向
2、远程服务器发起连接请求;2. 获取套接字字节流与远程服务器进行数据通信;3. 通信完毕,关闭套接字;1.2 基于 Java 的 TCP 客户端编写基本步骤说明基于 Java 的 TCP 客户端会使用下述类或接口:Socket、InetAddress、InputStream、OutPutStream 等,基本上这些可能在 Java TCP 客户端程序中使用的类大致会包含于下述常用的 Java 库文件中:.*、java.io.*、java.util.*等。因此编写该 TCP 客户端类时,最好事先将需要使用的类所包含于的库文件引入所正在编写的 java 类文件的开头,当然也可以在事后添加。1.2.1
3、 在 TCP 客户端的 java 源文件开头引用 java 库文件import .*;import java.io.*;这里介绍的一个最基本的 TCP 客户端程序中基本就只会使用上述 java 库文件中所包含的类了。大家可以根据编写的程序的需要,视情况引入更多的 java 库文件。1.2.2 与远程服务器端发起 TCP 连接请求TCP 客户端要完成与远程服务器的 TCP 连接的工作,需要做以下几件事情: 设定 TCP 服务器程序所运行的计算机网络接口地址(IP 地址); 设定 TCP 服务器程序所监听的网络端口号; 向远程 TCP 服务器程序发起 TCP 连接请求;看似很多事情,其实在 jav
4、a 中编程完成这些事情,相当简单。通常情况下,如果要连接某个远端服务器程序,那必然要知道该服务器程序所运行的主机 IP 地址或者该主机名。那么就可以通过运行该服务器程序的主机 IP 或主机名来在 TCP 客户端程序中构造一个InetAddress 类对象实例。比如:主机 IP = 192.168.111.123; 主机名 = whpucomputerInetAddress serverIP = InetAddress.getByName(“192.168.111.123”);或InetAddress serverIP = InetAddress.getByName(“whpucomputer”
5、);光知道远程服务器程序所运行的主机地址还不够,TCP 客户端程序还需要知道远程服务器程序所监听的端口号,通常在编程时端口号是一个整型数据。比如:该远程服务器程序所监听的端口号为:8888。知道了远程服务器程序所运行于的主机地址和其所监听的端口号后,就可以使用Socket 类发起与远程服务器程序建立 TCP 连接的请求了,具体代码如下:Socket clientsock = new Socket(serverIP, 8888);上面代码复制符右端的 Socket 方法如果成功返回,那么就会返回一个 Socket 类对象的引用给 Clientsock,在之后的程序中就可以使用这个 Socket
6、类对象引用来完成与远程服务器程序的通信工作了。不过,该段代码有个比较明显的问题:8888( 端口号)被硬编码到代码中,这是一个不好的编程习惯。具体如何跟好的设置端口号,后面整体说明时进行解释。到此,TCP 客户端编写发起向 TCP 服务器程序的 TCP 连接请求的代码就完成了,整体代码大致如下所示:InetAddress serverIP = InetAddress.getByName(“whpucomputer”);Socket clientsock = new Socket(serverIP, 8888);1.2.3 与远程 TCP 服务器程序进行通信基于 java 的 TCP 套接字程序
7、中,与远程网络程序进行通信主要使用的是 Socket 类中的两个类成员 InputSteam 和 OutputStream 这两个字节流对象。获取这两个 Socket 类的成员需要使用 Socket 类提供的两个方法: getInputStream 和 getOutputStream。在前面介绍的代码中,假设已经成功与远程服务器程序建立了一个 TCP 连接,并获取了一个 Socket 类对象的引用。那么获取 InputStream 和 OutputStream 的对象的代码如下:InputStream in = clientsock.getInputStream();OutputStream
8、out = clientsock.getOutputStream();在 java 中使用文件流一般都不会直接使用 InputStream 和 OutputStream 类对象,一来直接使用字节访问数据比较麻烦,二来使用 InputStream 和 OutputStream 类对象会直接进行 I/O 操作,这样使用性能不高,特别是每次输入或输出的数据比较少的情况下,会反复进行 I/O 操作,由于每次 I/O 操作会耗费大量处理时间,因此势必带来运行效率不高的问题。1.2.3.1 对 InputStream 的封装假如,我们想以字符的方式读取套接字的 InputStream 流对象中的字节数据,
9、就可以对 InputStream 字节流对象进行封装,代码如下所示:InputStreamReader in = new InputStreamReader(clientsock.getInputStream();上面的代码只是将原来以字节访问方式的流转换为以字符方式访问的流,并没有改变I/O 操作的方式,为了提高访问信息,我们需要为流提供缓冲区,以便不必每次操作流时都产生 I/O 操作。使用缓冲区时,有些问题必须注意。例如:对于输出流(OutputStream),只有在缓冲区已满或者显式的调用流的 flush 方法时,缓冲区中的数据才会真实的写入文件流,否则,不会执行将缓冲区数据写入输出流的
10、操作;而对于输入流(InputStream),只要缓冲区未满,那么就可以接收外界向本地输入流传送来的数据,而在缓冲区已满时,表示外界向本地发送数据的速度快于本地处理输入流数据的速度,因此此时要拒绝再接收外界发送来的数据。为文件流提供缓冲功能的代码如下:BufferedReader in = new BufferedReader(new InputStreamReader(clientsock.getInputStream();1.2.3.2 对 OutputStream 的封装对于 OutputStream 文件字节流对象的封装基本和 InputStream 文件字节流对象封装的方式一致。具体
11、代码如下:BufferedWriter out = new BufferedWriter(new InputStreamWriter(clientsock.getOutputStream();为了更容易的打印出缓冲区中的字符串,可以进一步对 BufferedWriter 对象进行封装,例如:PrintWriter out = new PrintWrite(new BufferedWriter(new InputStreamWriter(clientsock.getOutputStream();1.2.4 Client 业务逻辑实现这里需要实现的 Client 功能如下描述:“循环 10 次,向
12、 Server 端发送字符串”Hello Server” + i (i 为整数,且 0data_size;c_recvbuff=c_sendbuff=s_recvbuff=s_sendbuffdata_size;c_recvbuff=c_sendbuff=s_recvbuff=s_sendbuffdata_size;c_recvbuff=c_sendbuff=s_recvbuff=s_sendbuffdata_size;实验要求:本次实验为验证性实验,主要目的是熟悉基于 java 的 UDP 套接字编写 client/server 模式网络应用程序的基本步骤和方法。实验后需按要求完成实验报告的
13、撰写工作。实验报告内容格式应包含:11. 实验目的:12. 实验任务:13. 实验结果截图:14. 思考题回答:( 如果有的话)15. 实验总结:附录 A Java I/o 流相关的常用类关系图I n p u t S t r e a m O u t p u t S t r e a mO b j e c tB y t e A r r a y I n p u t S t r e a mF i l e I n p u t S t r e a mO b j e c t I n p u t S t r e a mP i p e d I n p u t S t r e a m S e q u e n c
14、e I n p u t S t r e a mF i l t e r I n p u t S t r e a m D a t a I n p u t S t r e a mB u f f e r e d I n p u t S t r e a mP u s h b a c k I n p u t S t r e a mB y t e A r r a y O u t p u t S t r e a mF i l e O u t p u t S t r e a mO b j e c t O u t p u t S t r e a mP i p e d O u t p u t S t r e a m
15、 F i l t e r O u t p u t S t r e a m D a t a O u t p u t S t r e a mB u f f e r e d O u t p u t S t r e a mP r i n t S t r e a m上图中,除 Object 类之外,所有类都处于 java.io 包中。R e a d e r W r i t e rO b j e c tB u f f e r e d R e a d e rF i l t e r R e a d e rI n p u t S t r e a m R e a d e rP i p e d R e a d e
16、rS t r i n g R e a d e rC h a r A r r a y R e a d e rL i n e N u m b e r R e a d e rF i l e R e a d e rP u s h b a c k R e a d e rB u f f e r e d W r i t e rF i l t e r W r i t e rO u t p u t S t r e a m W r i t e rP i p e d W r i t e r C h a r A r r a y W r i t e r F i l e W r i t e rS t r i n g W r i t e rP r i n t W r i t e r上图中,除 Object 类之外,所有类都处于 java.io 包中。