Java网络编程一个基础案例分析,基于TCP协议的客户端上传文件给服务器端(单线程版)
1.首先解释下什么是TCP协议?
简单来时就是客户端与服务器端以什么样的规则传输数据,具体内容大家可以不必理会。为什么说TCP协议是安全的协议呢,这是因为客户端在给服务器端发送信息的时候,必须确认服务端是可以接收数据的,也就是说服务端是存在的,TCP协议依靠3次握手的策略来和服务端建立链接(第一次握手:客户端给服务端发送链接请求。第二次握手:服务端给客户端回馈同意建立链接。第三次握手:客户端给服务端发送已收到服务端发来的链接信息。这时链接已经建立)
2.在Java中,编写文件上传小案例的思路
服务端:利用ServerSocket类,来监听服务端口,随时准备和客服端建立链接。链接建立后,服务端会产生一个Socket对象,产生和客户端协定好的套接字输入流(三次握手的时候已经商定好)来读取客户端发送来的字节流(这里需要重点说下,服务器在读客户端发送来的字节流时,当客户端发送完毕时,如果不通知服务器,服务器是不知道客户端已经发送完毕的,服务器会进入阻塞状态,下面的程序中会特别说明)。服务器读取数据完毕后,调用Socket对象的字节输出流告知数据已经读取完毕
客户端:客户端利用Socket类产生Socket对象(这里的Socket对象和服务器端的Socket对象时一一对应的)。读取本地文件的同时,向服务端发送数据(利用Socket对象的输出流),发送完毕时,告知服务器数据发送完毕(如果不告知,服务端读不到数据将会进入阻塞状态)
3.补充说明(重要):
客户端本地读取文件时,文件是有结束标志的(也就是有限界的)。当使用read()方法时,读到文件尾时,
再没有读到数据时,会正常返回-1。但是,在Socket流对象中,读数据时,字节流可以说是没有界限的,当没有读到数据时(这时也没有收到发送端发来的结束信息),Socket读入流会进入阻塞状态,直到收到数据为止。在上面的程序中,客户端发送数据完毕时,如果不告知服务端(也就是没有使用shutdownOutput()方法),服务端的read()操作将进入阻塞状态,直到收到数据为止(也就是说这是程序休眠了)。查看API帮助文档可以看到:
shutdownOutput
public void shutdownOutput()
throws IOException
禁用此套接字的输出流。对于 TCP 套接字,任何以前写入的数据都将被发送,并且后跟 TCP 的正常连接终止序列。 如果在套接字上调用 shutdownOutput() 后写入套接字输出流,则该流将抛出 IOException。
抛出:
IOException - 如果关闭此套接字时发生 I/O 错误。
它会使服务端的阻塞解除,和读取本地文件时相似(读到了结束标志,返回正常值-1,程序继续往下执行)。
4.具体代码实现
4.1服务端
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(4567);// 只要不在0-1023之间就可以,但必须在65535内
Socket accept = ss.accept();
InputStream in = accept.getInputStream();
FileOutputStream fOut = new FileOutputStream("F:\\shuye.txt");
byte[] byteArray = new byte[1024];
int len = -1;
try{
// 这里如果没有读取到客户端的数据将会进入阻塞状态
while((len = in.read(byteArray))!=-1){fOut.write(byteArray, 0, len);
}
}catch(Exception e){
System.out.println(e.getMessage());
}
fOut.close();
OutputStream out = accept.getOutputStream();
out.write("接收完毕!".getBytes());
out.close();
in.close();
ss.close();}} 4.2客户端程序
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
public class Client {
public static void main(String[] args) throws IOException {
Socket s = new Socket("127.0.0.1",4567);// 和服务端端口号一致
// 链接成功
// 输入流,用来读取本地的数据
FileInputStream fIn = new FileInputStream("D:\\jiayou.txt");
// 输出流,用来向服务器发送数据
OutputStream out = s.getOutputStream();
byte[] byteArray = new byte[1024];
int len = -1;
//发送数据
while((len = fIn.read(byteArray))!=-1){
out.write(byteArray, 0, len);
}
// 发送完毕并告知服务器
s.shutdownOutput();
// 读取服务器发来的反馈
InputStream in = s.getInputStream();
byte[] byteArray2 = new byte[1024];
in.read(byteArray2);
System.out.println("服务器发来的反馈信息是:"+new String(byteArray2));
// 关闭流资源
in.close();
out.close();
fIn.close();
// 关闭链接
System.out.println("客户端退出会话");
s.close();
}
}