Java【网络编程】
一、网络编程入门
1.1 软件结构
1.2 网络通信协议
1.3 协议分类
java.net 包中包含的类和接口,用来专注于网络程序开发。
java.net 包中提供了两种常见的网络协议的支持:
- UDP:用户数据报协议(User Datagram Protocol),无连接通信协议,特点是数据被限制在64kb以内,超出这个范围就不能发送了。数据报:网络传输的基本单位。
- TCP:传输控制协议(Transmission Control Protocol),面向连接的通信协议,每次连接的创建都要经过**”三次握手“**。
1.4 网络编程三要素
协议:计算机实现网络通信必须遵守的规则
IP地址:互联网协议地址(Internet Protocol Address)
- IPv4:一个32位的二进制数,通常被分为4个字节,表示为
a.b.c.d 的形式。最多42亿个。 - IPv6:128位二进制数,每16个字节一组,分为八组十六进制数,号称可以为全世界每一粒沙子编一个地址,解决了网络地址资源紧张的问题。
端口号:端口号标识了一个主机上进行通信的不同的应用程序。常见的端口号:
- 网络端口:80
- 数据库:mysql3306 oracle1521
- Tomcat服务器:8080
二、TCP通信程序
2.1 概述
TCP通信的客户端:向服务器发送连接请求,给服务器发送数据,读取服务器回写的数据。表示客户端的类:java.net.Socket
TCP通信的服务器端:接受客户端的请求,读取客户端发送的数据,给客户端回写数据。表示服务器的类:
java.net.ServerSocket
2.2 Socket类
java.net.Socket :此类实现客户端套接字(也可以叫”套接字“),套接字是两台机器间通信的端点。
套接字:包含了IP地址和端口号的网络单位。
? OutputStream getOutputStream() 返回此套接字的输出流
? InputStream getInputStream() 返回此套接字的输入流
? void close() 关闭此套接字
2.3 SocketServer类
java.net.ServerSocket :此类实现服务器套接字。
2.3 简单的TCP网络程序
TCPClient
public class TCPClient {
public static void main(String[] args) throws IOException {
Socket socket = new Socket("127.0.0.1",8888);
OutputStream os = socket.getOutputStream();
os.write("你好服务器".getBytes());
InputStream is = socket.getInputStream();
byte[] bytes = new byte[1024];
int len = is.read(bytes);
System.out.println(new String(bytes,0,len));
socket.close();
}
}
TCPServer
public class TCPServer {
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(8888);
Socket socket = ss.accept();
InputStream is = socket.getInputStream();
byte[] bytes = new byte[1024];
int len = is.read(bytes);
System.out.println(new String(bytes,0,len));
OutputStream os = socket.getOutputStream();
os.write("你好客户端,服务器收到".getBytes());
ss.close();
}
}
三、综合案例
3.1 文件上传案例
? 明确数据源和目的地: 数据源:/Users/yanzhuang/Desktop/3.jpg 目的地:服务器
【客户端】输入流,从硬盘读取数据到程序中。
【客户端】输出流,写出文件数据到服务端。
【服务端】输入流,读取文件数据到服务端程序。
【服务端】输出流,写出文件数据到服务器硬盘中。
TCPClient
public class TCPClient {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("/Users/yanzhuang/Desktop/3.jpg");
Socket socket = new Socket("127.0.0.1",8888);
OutputStream os = socket.getOutputStream();
byte[] bytes = new byte[1024];
int len = 0;
while((len = fis.read(bytes)) != -1) {
os.write(bytes);
}
socket.shutdownOutput();
InputStream is = socket.getInputStream();
while ((len = is.read(bytes)) != -1){
System.out.println(new String(bytes,0,len));
}
fis.close();
socket.close();
}
}
TCPServer
public class TCPServer {
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(8888);
Socket socket = ss.accept();
InputStream is = socket.getInputStream();
File file = new File("/Users/yanzhuang/Desktop/upload");
if(!file.exists()){
file.mkdirs();
}
FileOutputStream fos = new FileOutputStream(file+"/a.jpg");
int len = 0;
byte[] bytes = new byte[1024];
while((len = is.read(bytes)) != -1) {
System.out.println(new String(bytes,0,len));
fos.write(bytes,0,len);
}
OutputStream os = socket.getOutputStream();
os.write("上传成功".getBytes());
fos.close();
socket.close();
ss.close();
}
}
自定义一个命名规则:放置同名的文件被覆盖
使服务器始终处于监听状态:有一个客户端上传文件,就保存一个文件
多线程提高效率:使用多线程处理多个客户端套接字的请求
优化后服务器端代码:
public class TCPServer {
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(8888);
while(true) {
Socket socket = ss.accept();
new Thread(new Runnable() {
@Override
public void run() {
FileOutputStream fos = null;
try{
InputStream is = socket.getInputStream();
File file = new File("/Users/yanzhuang/Desktop/upload");
if(!file.exists()){
file.mkdirs();
}
String fileName = "zx" + System.currentTimeMillis() + new Random().nextInt(999999) + ".jpg";
fos = new FileOutputStream(file+"/" + fileName);
int len = 0;
byte[] bytes = new byte[1024];
while((len = is.read(bytes)) != -1) {
fos.write(bytes,0,len);
}
OutputStream os = socket.getOutputStream();
os.write("上传成功".getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
fos.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}).start();
}
}
}
3.2 模拟B/S服务器
浏览器服务器交互方式的服务器程序:
public class TCPServer {
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(8080);
while (true) {
new Thread(new Runnable() {
@Override
public void run() {
try{
Socket socket = ss.accept();
InputStream is = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String line = br.readLine();
String[] arr = line.split(" ");
String htmlPath = arr[1].substring(1);
FileInputStream fis = new FileInputStream(htmlPath);
OutputStream os = socket.getOutputStream();
os.write("HTTP/1.1 200 OK\r\n".getBytes());
os.write("Content-Type:text/html\r\n".getBytes());
os.write("\r\n".getBytes());
int len = 0;
byte[] bytes = new byte[1024];
while ((len = fis.read(bytes)) != -1){
os.write(bytes, 0, len);
}
fis.close();
socket.close();
ss.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
}
}
注意:浏览器解析服务器回写的html 页面,如果页面中有图片,那么浏览器就会单独的开一个线程,读取服务器的图片,我们可以使用循环让服务器一直处在监听状态,客户端请求一次,服务器就回写一次。
|