目录
- 1. 软件结构
- 2. 网络通信协议
- 2.1 UDP协议
- 2.2 TCP协议
- 3.1 客户端
- 3.2 服务器端
- 3.3 文件上传案例实现与优化
1. 软件结构
我们日常生活中所接触到的客户端主要有两种,一种是你打开一个软件你就可以使用它的功能,例如微信、QQ等;另一种是我们熟悉的浏览器,当你想要浏览一些网页或搜索一些东西的时候你需要打开个浏览器输入一个网站
说到这有人可能是觉得其实两种都是参不多,确实也是,但可以用作区分的一个点是你在浏览器中其实都是通过网址来操作,这就衍生出两种不同的软件结构:
- C/S结构
C/S是Client/Server的缩写,客户 / 服务器软件结构 - B/S结构
B/S是Browser/Server的缩写,浏览器 / 服务器软件结构
2. 网络通信协议
有学过计算机网络的同学应该都还记得,OSI参考模型一共有七层,而我们所学习的模型为了理解方便简化成了五层,分别是:物理层、数据链路层、网络层、传输层、应用层,我们这里所讨论的通信协议是属于传输层,在传输层中有两个协议是很常用的,分别是UDP协议和TCP协议
2.1 UDP协议
UDP协议,即用户数据报协议(User Datagram Protocol),这是一个不需要建立连接就可通信的协议,他的优点是效率高,但缺点是不可靠,丢包不会重传,多用于即时性较强、丢失数据包不会影响大致体验的功能,例如直播、视频通话等
2.2 TCP协议
TCP协议,即传输控制协议(Transmission Control Protocol),这是一个需要建立连接才可通信的协议,他建立连接时候需要经过“三次握手”,他的优点是可靠传输、丢包重传,但效率相比UDP较低
3. 网络编程
这里所介绍的网络编程是根据TCP协议来展开的,下面将分开客户端与服务器端进行介绍
首先我们来了解一下网络通信的规则:服务器端是不会主动请求客户端的,必须使用客户端来请求服务器端,因此服务器要先于客户端启动,以等待客户端的请求,在客户端请求服务器并且经过三次握手后,他们就会建立逻辑连接,这个逻辑连接中包含有一个IO流对象,而客户端与服务器端就是使用这个IO流来进行通信,因为需要通信的数据不仅仅是字符,还可能会有图片、视频等,因此该IO流对象是字节流对象
那么刚才所说到的IO流对象怎么来呢?下面来详细介绍一下
3.1 客户端
在
java.net
包中有一个表示客户端的类,
java.net.Socket
:此类实现客户端套接字(也可以就叫 套接字),套接字是两台机器间通信的端点,也可认为套接字是包含了IP地址和端口号的网络单位
在Socket类中有一个常用的构造方法:
public Socket(String host, int port):创建一个流套接字并将其连接到指定主机上的指定端口号。
其中,
host
是你想要连接到的服务器的IP地址,
port
是服务器的端口号
Socket类常用的成员方法有三个:
public OutputStream getOutputStream():返回此套接字的输出流。public InputStream getInputStream():返回此套接字的输入流。public void close():关闭此套接字。
该套接字的相关方法介绍完了,那么怎么实现这个客户端的网络通信功能呢?下面来说一下步骤:
① 创建一个客户端对象Socket,在构造方法中绑定想要访问的服务器IP地址与端口号
② 使用Socket对象中的
getOutputStream
方法获取网络字节输出流
OutputStream
对象并调用其
write
方法向服务器发送数据
③ 使用Socket对象中的
getInputStream
方法获取网络字节输入流
InputStream
对象并调用其
read
方法读取服务器回写的数据
④ 释放Socket资源
好啦,那我们下面来实现一下
由于服务器端还没写,这里先不贴运行效果,但说两点注意事项
- 客户端与服务器进行交互必须使用Socket中提供的网络流,不能使用自己创建的流对象
- 当我们创建客户端对象Socket的时候就会请求服务器并与其进行三次握手建立连接通路,若这时候服务器没有启动则会抛出异常
3.2 服务器端
下面来介绍一下服务器端,同样在
java.net
包下有一个表示服务器的类,
java.net.ServerSocket
:此类实现服务器套接字,服务器套接字等待请求通过网络传入
同样的 ,在ServerSocket类中有一个常用的构造方法:
public ServerSocket(int port):创建绑定到特定端口的服务器套接字。
其中,
port
是服务器的端口号
ServerSocket类常用的成员方法有两个:
public Socket accept():侦听并接受到此套接字的连接。public void close():关闭此套接字。
该套接字的相关方法介绍完了,那么怎么实现这个服务器端的网络通信功能呢?下面来说一下步骤:
① 创建一个服务器端对象ServerSocket,在构造方法中设定该服务器的端口号
② 使用ServerSocket对象中的
accept
方法获取发起请求的客户端对象Socket
③ 使用Socket对象中的
getInputStream
方法获取网络字节输入流
InputStream
对象并调用其
read
方法读取客户端发送的数据
④ 使用Socket对象中的
getOutputStream
方法获取网络字节输出流
OutputStream
对象并调用其
write
方法向客户端回写数据
⑤ 释放Socket资源
⑥ 释放ServerSocket资源
好啦,那我们下面来实现一下
现在可以看运行结果啦,注意要先启动服务器端再启动客户端
3.3 文件上传案例实现与优化
接下来我们再来个综合案例体验一下,这个案例的需求是:读取客户端的本地文件,上传到服务器端(服务器把文件保存到本地),服务器回写成功数据给客户端
其实该案例与前面所介绍的主体参不多,只是增加了读取与写入本地文件的部分,因此具体的实现步骤就不再分析啦,这里直接给出示例代码,大家好好体会:
- 客户端(读取本地文件并上传)
- 服务器端(读取上传文件并保存本地)
运行结果可以自己试试,这里没什么好展示的,不过,有几个地方可以改进:
- 既然是作为服务器端,就要能够一直为客户端服务,因此服务器端应该一直处于监听状态(while死循环)
- 使用多线程技术提高服务器的效率,让其有新的客户端请求就开启新的线程进行出来(Thread)
- 应该要自动命名,以防止同名所引起的文件被覆盖,可按照域名+毫秒值+随机数的规则命名(currentTimeMillis,Random)
下面看看改进后的示例代码(只需改进服务器端)
The end~