本文目录
第七章 : Java基础网络编程
1. 网络编程入门
1.1 软件结构
C/S结构与B/S结构。
1.2 网络通信协议
专注于研究传输层:
TCP
传输控制协议。UDP
用户数据报协议。
1.3 网络编程三要素
- 协议
- IP地址
- 端口号
2. TCP通信程序
2.1 概述
Java中,提供了两个类用于实现TCP通信程序。
- 客户端
java.net.Socket
, 创建Socket对象。 - 服务端
java.net.ServerSocket
,创建ServerSocket对象。
客户端与服务端之间的逻辑连接包含一个IO对象,本质上是字节流对象。
2.2 TCP客户端代码实现
构造方法:
Socket(String host, int port);
成员方法:
OutputStream getOutputStream()
返回套接字的输出流。InputStream getInputStream()
返回套接字的输入流。close()
关闭套接字。
使用步骤:
- 创建套接字对象,绑定服务器IP和端口。
- 使用
getOutputStream()
获取套接字输出流对象。 - 使用套接字输出流对象的
write()
方法给服务器发消息。 - 使用
getInputStream()
获取套接字输入流对象。 - 使用套接字输入流对象的
read()
方法读取服务器回复的消息。 - 使用
close()
关闭socket对象。
提示:
创建套接字对象时,如果服务器没有启动,就会抛出异常java.net.ConnectException
。
package SocketDemo;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
public class TCPCLient {
public static void main(String[] args) throws IOException {
Socket socket = new Socket("127.0.0.1", 9999);
OutputStream outputStream = socket.getOutputStream();
outputStream.write("客户端说: 爷给你发数据了。".getBytes());
InputStream inputStream = socket.getInputStream();
byte[] bytes = new byte[1024];
int len = inputStream.read(bytes);
System.out.println(new String(bytes, 0, len));
socket.close();
}
}
2.3 TCP服务端代码实现
构造方法:
ServerSocket(int port);
绑定服务器本机端口号。
成员方法:
Socket accept()
监听并获取到客户端发送来的套接字。OutputStream getOutputStream()
返回套接字的输出流。InputStream getInputStream()
返回套接字的输入流。close()
关闭套接字。
使用步骤:
- 创建服务端套接字对象,绑定服务器本机端口号。
- 使用
accept()
方法监听并获取客户端套接字。 - 使用客户端套接字的
getInputStream()
方法获取字节输入流对象。 - 使用
InputStream
对象的read()
方法获取客户端发来的消息。 - 使用客户端套接字的
getOutputStream()
对象获取字节输出流对象。 - 使用
OutputStream
对象的write()
方法给客户端发消息。 - 关闭客户端套接字。
- 关闭服务端套接字。
package SocketDemo;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Arrays;
public class TCPServer {
public static void main(String[] args) throws IOException {
ServerSocket socket = new ServerSocket(9999);
Socket clientSocket = socket.accept();
InputStream inputStream = clientSocket.getInputStream();
byte[] bytes = new byte[1024];
int len = inputStream.read(bytes);
System.out.println(new String(bytes,0,len));
OutputStream outputStream = clientSocket.getOutputStream();
outputStream.write("服务器说: 爷收到了".getBytes());
clientSocket.close();
socket.close();
}
}
3. 文件上传示例
3.1 文件上传
3.2 文件上传的阻塞问题
read()方法如果没有输入可用,此方法将阻塞。
read方法并不能读取到文件的结束符,也就是-1,所以客户端和服务器都会进入阻塞状态。
解决方案:
发送文件的套接字主动关闭套接字输出流。
public void shutdownOutput()
禁用此套接字的输出流。对于 TCP 套接字,任何以前写入的数据都将被发送,并且后跟 TCP 的正常连接终止序列。
4. 文件上传的多线程优化
4.1 文件名称
在服务端自定义文件名称。
4.2 服务端多线程监听并接收客户端的套接字
- 让服务器一直处于监听状态
- 有客户端上传文件,就开启一个新线程。
new Thread(new Runnable() { @Override public void run() { //上传文件的代码 } }).start();
- 因为run方法没有抛出异常,所以异常都需要用try-catch进行处理。
5. 模拟B/S结构的服务端
注意:
浏览器如果检测到网页有图片,就会开新线程去访问图片,所以服务端需要写成多线程模式。