网络编程
网络编程
第一章 网络编程入门
1.1 软件结结构
- C/S结构: 全称为Client/Server结构,是指客户端和服务器结构。常见程序有QQ、迅雷等软件
- B/S结构: 全称为Browser/Server结构,是指浏览器和服务器结构。常见浏览器有谷歌、火狐等
- 网络编程:就是在一定的协议下,实现两台计算机的通信的程序。
1.2 网络通信协议
- 网络通信协议:通过计算机网络可以使多台计算机实现连接,位于同一个网络中的计算机在进行连接和通信时需要遵守一定的规则。在计算机网络中,这些连接和通信的规则被称为“网络协议”,它对数据的传输格式、传输速率、传输步骤等做了统一规定,通信双方必须同时遵守才能完成数据交换。
- TCP/IP协议:
1.3协议分类
UDP协议:
TCP/IP协议:
1.4 网络编程三要素
协议:
计算机网络通信必须遵守的规则。
IP地址:
指互联网协议地址,计算机唯一的编号。
ping 空格 IP地址:检查网络是否连接
特殊的IP地址:
本机IP地址:127.0.0.1、localhost
端口号:
系统随机给网络软件分配的一个不重复"门牌号",是一个逻辑端口。
第二章 TCP通信程序
2.1 概述
2.2 Socket类
构造方法
public Socket(String host,int port); 创建一个流套接字并将其连接到指定主机上的指定端口号。
参数:
String host:服务器的IP地址/服务器主机的名称
int port:服务器的端口号
成员方法
public OutputStream getOutputStream(); 返回此套接字的输出流
public InputStream getInputStream(); 返回此套接字的输入流
public void close(); 关闭此套接字
package day10.net.demo1TCP;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
/*
实现步骤:
1.创建一个客户端对象的Socket,构造方法中绑定服务器的IP地址和端口号
2.使用Socket对象中的getOutputStream():获取网络字节输出流OutputStream对象
3.调用网络字节输出流OutputStream对象中的write():给服务器发送数据
4.使用Socket对象中的getInputStream():获取网络字节输入流InputStream对象
5.调用网络字节输入流InputStream对象中的read():读取服务器回写的数据
6.释放资源(Socket)
注意:
1.客户端和服务器端进行交互,必须使用Socket类中提供的网络流,不能使用自己创建的流对象
2.当我们创建客户端对象Socket的时候,就会去请求服务器并和服务器经过3次握手建立连接
这时:
如果服务器没有启动,那么就会抛出异常! ConnectException:Connection refused: connect
如果服务器已经启动,那么就可以进行交互。
*/
public class TCPClient {
public static void main(String[] args) throws IOException {
//1.创建一个客户端对象的Socket,构造方法中绑定服务器的IP地址和端口号
Socket socket = new Socket("127.0.0.1",8888);
//2.使用Socket对象中的OutputStream(): 获取网络字节输出流对象
OutputStream outputStream = socket.getOutputStream();
//3.调用网络字节输出流OutputStream对象中的write():给服务器发送数据
outputStream.write("你好服务器".getBytes());
//添加一个结束标记,防止服务器端读取时发生阻塞!
socket.shutdownOutput();
//4.使用Socket对象中的getInputStream():获取网络字节输入流InputStream对象
InputStream inputStream = socket.getInputStream();
//5.调用网络字节输入流InputStream对象中的read():读取服务器回写的数据
/*byte[] bytes = new byte[1024];
int len = inputStream.read(bytes);
System.out.print(new String(bytes,0,len));*/
int len = 0;
byte[] bytes = new byte[1024];
while((len = inputStream.read(bytes)) != -1){
System.out.println(new String(bytes,0,len));
}
//6.释放资源
socket.close();
}
}
2.3 ServerSocket类
构造方法
public ServerSocket(int port); 创建绑定到特定端口的服务器套接字。
成员方法
public Socket accept(); 侦听并接收到此套接字的连接。
package day10.net.demo1TCP;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
/*
服务器的实现步骤:
1.创建服务器ServerSocket对象和系统要指定的端口号
2.使用ServerSocket对象中的accept():获取到请求的客户端对象Socket
3.使用Socket对象中的getInputStream():获取网络字节输入流InputStream对象
4.调用网络字节输入流InputStream对象中的read():读取客户端发送的数据
5.使用Socket对象中的getOutputStream():获取网络字节输出流OutputStream对象
6.调用网络字节输出流OutputStream对象中的write():给客户端回写数据
7.释放资源(Socket,ServerSocket)
*/
public class TCPServer {
public static void main(String[] args) throws IOException {
//1.创建服务器ServerSocket对象和系统要指定的端口号
ServerSocket serverSocket = new ServerSocket(8888);
//2.使用ServerSocket对象中的accept():获取到请求的客户端对象Socket
Socket socket = serverSocket.accept();
//3.使用Socket对象中的getInputStream():获取网络字节输入流InputStream对象
InputStream inputStream = socket.getInputStream();
//4.调用网络字节输入流InputStream对象中的read():读取客户端发送的数据
/*byte[] bytes = new byte[1024];
int len = inputStream.read(bytes);
System.out.println(new String(bytes,0,len));*/
int len = 0;
byte[] bytes = new byte[1024];
while((len = inputStream.read(bytes)) != -1){
System.out.println(new String(bytes,0,len)); //发生阻塞! 读取不到结束标记-1,因为客户端并没有把结束标记-1上传。
}
//发生阻塞后,下面的代码均不会执行,且客户端和服务器端一直处于开放状态!
//5.使用Socket对象中的getOutputStream():获取网络字节输出流OutputStream对象
OutputStream outputStream = socket.getOutputStream();
//6.调用网络字节输出流对象中的write():给客户端回写数据
outputStream.write("收到谢谢".getBytes());
//7.释放资源
socket.close();
serverSocket.close();
}
}
2.4 简单的TCP网络程序
TCP通信分析图解
客户端向服务器发送数据
package day10.net;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
/*
TCP通信的客户端:向服务器发送连接请求,给服务器发送数据,读取服务器回写的数据。
表示客户端的类:
java.net.Socket:此类实现客户端套接字。
套接字:套接字是两台机器间通信的端点,包含了IP地址和端口号的网络单位(包含了IP地址和端口号的计算机)。
Constructor:
public Socket(String host,int port); 创建一个流套接字并将其连接到指定主机上的指定端口号。
参数:
String host:服务器的IP地址/服务器主机的名称
int port:服务器的端口号
成员方法:
public OutputStream getOutputStream(); 返回此套接字的输出流
public InputStream getInputStream(); 返回此套接字的输入流
public void close(); 关闭此套接字
实现步骤:
1.创建一个客户端对象的Socket,构造方法中绑定服务器的IP地址和端口号
2.使用Socket对象中的getOutputStream():获取网络字节输出流OutputStream对象
3.调用网络字节输出流OutputStream对象中的write():给服务器发送数据
4.使用Socket对象中的getInputStream():获取网络字节输入流InputStream对象
5.调用网络字节输入流InputStream对象中的read():读取服务器回写的数据
6.释放资源(Socket)
注意:
1.客户端和服务器端进行交互,必须使用Socket类中提供的网络流,不能使用自己创建的流对象
2.当我们创建客户端对象Socket的时候,就会去请求服务器并和服务器经过3次握手建立连接
这时:
如果服务器没有启动,那么就会抛出异常! ConnectException!
如果服务器已经启动,那么就可以进行交互。
*/
public class TCPClient {
public static void main(String[] args) throws IOException {
//1.创建一个客户端对象的Socket,构造方法中绑定服务器的IP地址和端口号
Socket socket = new Socket("127.0.0.1",8888);
//2.使用Socket对象中的OutputStream(): 获取网络字节输出流对象
OutputStream outputStream = socket.getOutputStream();
//3.调用网络字节输出流OutputStream对象中的write():给服务器发送数据
outputStream.write("你好服务器".getBytes());
//4.使用Socket对象中的getInputStream():获取网络字节输入流InputStream对象
InputStream inputStream = socket.getInputStream();
//5.调用网络字节输入流InputStream对象中的read():读取服务器回写的数据
byte[] bytes = new byte[1024];
int len = inputStream.read(bytes);
System.out.print(new String(bytes,0,len));
/*int len = 0;
byte[] bytes = new byte[1024];
while((len = inputStream.read(bytes)) != -1){
System.out.println(new String(bytes,0,len));
}*/
//6.释放资源
socket.close();
}
}
服务器向客户端回写数据
package day10.net;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
/*
TCP通信的服务器端:接收客户端的请求,读取客户端发送的数据,给客户端回写数据
表示服务器的类:
java.net.ServerSocket:此类实现服务器套接字
构造方法:
public ServerSocket(int port); 创建绑定到特定端口的服务器套接字。
服务器端必须明确一件事情:必须得知道是哪个客户端请求的服务器?
所以可以使用accept()获取到请求的客户端对象Socket
成员方法:
public Socket accept(); 侦听并接收到此套接字的连接。
服务器的实现步骤:
1.创建服务器ServerSocket对象和系统要指定的端口号
2.使用ServerSocket对象中的accept():获取到请求的客户端对象Socket
3.使用Socket对象中的getInputStream():获取网络字节输入流InputStream对象
4.调用网络字节输入流InputStream对象中的read():读取客户端发送的数据
5.使用Socket对象中的getOutputStream():获取网络字节输出流OutputStream对象
6.调用网络字节输出流OutputStream对象中的write():给客户端回写数据
7.释放资源(Socket,ServerSocket)
*/
public class TCPServer {
public static void main(String[] args) throws IOException {
//1.创建服务器ServerSocket对象和系统要指定的端口号
ServerSocket serverSocket = new ServerSocket(8888);
//2.使用ServerSocket对象中的accept():获取到请求的客户端对象Socket
Socket socket = serverSocket.accept();
//3.使用Socket对象中的getInputStream():获取网络字节输入流InputStream对象
InputStream inputStream = socket.getInputStream();
//4.调用网络字节输入流InputStream对象中的read():读取客户端发送的数据
byte[] bytes = new byte[1024];
int len = inputStream.read(bytes);
System.out.println(new String(bytes,0,len));
// int len = 0;
// byte[] bytes = new byte[1024];
// while((len = inputStream.read(bytes)) != -1){
// System.out.println(new String(bytes,0,len));
// break; //??? 使用break语句跳出循环;否则程序会一直停在这里,不继续往下执行,也只打印出一条输出语句???
// }
//5.使用Socket对象中的getOutputStream():获取网络字节输出流OutputStream对象
OutputStream outputStream = socket.getOutputStream();
//6.调用网络字节输出流对象中的write():给客户端回写数据
outputStream.write("收到谢谢".getBytes());
//7.释放资源
socket.close();
serverSocket.close();
}
}
第三章 综合案例
3.1 文件上传案例
文件上传的原理图:
package day10.net.demo2FileUpload;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
/*
文件上传案例:
客户端:读取本地文件,上传到服务器,读取服务器回写的数据
明确:
数据源:c:\\lyq.jpg
目的地:服务器
实现步骤:
1.创建一个本地字节输入流FileInputStream对象,构造方法中绑定要读取的数据源
2.创建一个客户端Socket对象,构造方法中绑定服务器的IP地址和端口号
3.使用Socket中的getOutputStream(): 获取网络字节输出流OutputStream对象
4.调用本地字节输入流FileInputStream对象中的read():读取本地文件
5.调用网络字节输出流OutputStream对象中的write():把读取到的文件上传到服务器
6.使用Socket中的getInputStream():获取网络字节输入流InputStream对象
7.使用网络字节输入流InputStream对象中的read():读取服务器回写的数据
8.释放资源
*/
public class TCPClient {
public static void main(String[] args) throws IOException {
//1.创建一个本地字节输入流FileInputStream对象,构造方法中绑定要读取的数据源
FileInputStream fis = new FileInputStream("c:\\lyq.jpg");
//2.创建一个客户端Socket对象,构造方法中绑定服务器的IP地址和端口号
Socket socket = new Socket("127.0.0.1",8280);
//3.使用Socket中的getOutputStream(): 获取网络字节输出流OutputStream对象
OutputStream os = socket.getOutputStream();
//4.调用本地字节输入流FileInputStream对象中的read():读取本地文件
int len = 0;
byte[] bytes = new byte[1024];
while((len = fis.read(bytes)) != -1){
//5.调用网络字节输出流OutputStream对象中的write():把读取到的文件上传到服务器
os.write(bytes,0,len);
}
/*
解决read()读取阻塞问题?
使用Socket类中的:
public void shutdownOutput();
禁用此套接字的输出流。对于 TCP 套接字,任何以前写入的数据都将被发送,并且后跟 TCP 的正常连接终止序列。
*/
socket.shutdownOutput(); //结束标记:关闭网络输出流OutputStream对象。
System.out.println("11111111111111111111");
//6.使用Socket中的getInputStream():获取网络字节输入流InputStream对象
InputStream is = socket.getInputStream();
//7.使用网络字节输入流InputStream对象中的read():读取服务器回写的数据
while((len = is.read(bytes)) != -1){
System.out.println(new String(bytes,0,len));
}
System.out.println("3333333333333333 while循环阻塞没有打印!");
//8.释放资源
socket.close();
fis.close();
}
}
package day10.net.demo2FileUpload;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Random;
/*
文件上传案例:
服务器端:读取客户上传的文件,保存到服务器的硬盘,给客户端回写“上传成功!”
明确:
数据源:客户端上传的文件
目的地:服务器的硬盘 d:\\upload\\lyq.jpg
实现步骤:
1.创建一个服务器ServerSocket对象,和系统要指定的端口号
2.使用ServerSocket对象中的accept():获取到请求的客户端Socket对象
3.使用Socket对象中的getInputStream():获取网络字节输入流InputStream对象
4.判断d:upload文件夹是否存在,不存在则创建
5.创建一个本地的字节输出流FileOutputStream对象,构造方法中绑定要输出的目的地
6.调用网络字节输入流InputStream对象中的read():读取客户端上传的文件
7.调用本地字节输出流FileOutputStream对象中的write():把读取到的文件保存在服务器的硬盘上
8.使用Socket对象中的getOutputStream():获取网络字节输出流OutputStream对象
9.调用网络字节输出流OutputStream对象中的write():给客户的回写“上传成功!”
10.释放资源(FileOutputStream,Socket,ServerSocket)
*/
public class TCPServer {
public static void main(String[] args) throws IOException {
//1.创建一个服务器ServerSocket对象,和系统要指定的端口号
ServerSocket serverSocket = new ServerSocket(8280);
//2.使用ServerSocket对象中的accept():获取到请求的客户端Socket对象
Socket socket = serverSocket.accept();
//3.使用Socket对象中的getInputStream():获取网络字节输入流InputStream对象
InputStream is = socket.getInputStream();
//4.判断d:upload文件夹是否存在,不存在则创建
File file = new File("d:\\upload");
if(file.exists() == false){
file.mkdirs();
}
//5.创建一个本地的字节输出流FileOutputStream对象,构造方法中绑定要输出的目的地
FileOutputStream fos = new FileOutputStream(file + "\\lyq.jpg");
System.out.println("22222222222222222222");
//6.调用网络字节输入流InputStream对象中的read():读取客户端上传的文件
int len = 0;
byte[] bytes = new byte[1024];
/*
客户端如果没有shutdownOutput()结束标记,read()将会陷入阻塞。
因为客户端读取到文件末尾时返回的结束标记-1,并没有被上传;
服务器端即便读取完了,由于没有结束标记,read()陷入阻塞状态,
while循环下面的语句也就无法执行。
*/
while((len = is.read(bytes)) != -1){
//7.调用本地字节输出流FileOutputStream对象中的write():把读取到的文件保存在服务器的硬盘上
fos.write(bytes);
}
System.out.println("4444444444444 while循环阻塞没有打印!");
//8.使用Socket对象中的getOutputStream():获取网络字节输出流OutputStream对象
OutputStream os = socket.getOutputStream();
//9.调用网络字节输出流OutputStream对象中的write():给客户的回写“上传成功!”
os.write("上传成功!".getBytes());
//10.释放资源(FileOutputStream,Socket,ServerSocket)
fos.close();
socket.close();
//让服务器一直处于监听状态时:不用关闭!
serverSocket.close();
}
}
3.2 文件上传案例的阻塞问题
3.2 模拟B\S服务器
package day10.net.demo4BSTCP;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
/*
创建BS版本TCP服务器:
客户端是浏览器。
*/
public class TCPServer {
public static void main(String[] args) throws IOException {
//1.创建一个服务器ServerSocket,和系统要指定的端口号
ServerSocket server = new ServerSocket(8080);
//2.使用accept():获取到请求的客户端对象(浏览器)
Socket socket = server.accept();
//3.使用Socket对象中的getInputStream():获取网络字节输入流对象
InputStream is = socket.getInputStream();
//4.使用网络字节输入流InputStream对象中的read():读取浏览器端的请求信息
/*int len = 0;
byte[] bytes = new byte[1024];
while((len = is.read(bytes)) != -1){
System.out.println(new String(bytes,0,len));
}*/
//把is网络字节输入流对象,转换为字符缓冲输入流
BufferedReader br = new BufferedReader(new InputStreamReader(is));
//把浏览器端请求信息的第一行读取出来 GET /day10/net/web/index.html HTTP/1.1
String line = br.readLine();
//把读取的信息进行切割:只要中间部分 /day10/net/web/index.html
String[] arr = line.split(" ");
//把路径前边的/去掉,进行截取 day10/net/web/index.html
String htmlpath = arr[1].substring(1);
//创建一个本地字节输入流,构造方法中绑定要读取的html路径
FileInputStream fis = new FileInputStream(htmlpath);
//使用Socket中的getOutputStream(): 获取网络字节输出流OutputStream对象
OutputStream os = socket.getOutputStream();
//写入HTTP协议响应头,固定写法
os.write("HTTP/1.1 200 OK\r\n".getBytes());
os.write("Content-Type:text/html\r\n".getBytes());
//必须要写入空行,否则浏览器不解析
os.write("\r\n".getBytes());
//一读一写复制文件,把服务器读取的html文件回写给客户端
int len = 0;
byte[] bytes = new byte[1024];
while((len = is.read(bytes)) != -1){
os.write(bytes,0,len);
}
//释放资源
fis.close();
socket.close();
server.close();
}
}