1、网络入门
1、两种模式
C/S结构 :全称为Client/Server结构,是指客户端和服务器结构
B/S结构 :全称为Browser/Server结构,是指浏览器和服务器结构
2、网络编程三要素
1.网络协议
2.IP地址
3.端口号
3、网络通信协议
不同计算机传输数据的规则
(1)UDP协议,属于传输层协议
UDP: 相当于寄快递,传输数据不安全
1.不需要连接
2.数据可能会丢失
3.速度快
4.每个包最大64kb
应用场景:
1.NBA直播
2.局域网的游戏
(2)TCP协议,属于应用层协议
TCP: 打电话, 传输数据安全
1.需要连接
2.数据不会丢失
3.速度慢
4.建立连接后是以流的形式传输,没有大小限制
应用场景:
1.支付宝扫码付钱
2.聊天程序.
3.下载
4、IP地址
(1)概念:
Internet Protocal Address: 互联网协议地址,每台联网的计算机都会分配一个IP地址(相当于家庭住址)。
(2)作用:
通过IP地址就可以找到某台电脑
(3)分类:
IPV4:点分十进制,例如192.168.84.100 范围:每段的范围0~255
IPV6:冒分十六进制 ABCD:EF01:2345:6789:ABCD:EF01:2345:6789
(4)特殊的ip地址
127.0.0.1: 找本机(本地回环地址)
(5)关于IP的DOS命令行命令
a、查看本机ip地址: ipconfig
b、测试网络是否通畅:ping IP地址/域名/网址
5、端口
(1)概念:
每个联网的程序都会使用一个端口号,通过端口号可以找到电脑上的某个程序(房间号)
(2)端口号范围:
一台电脑端口号的范围:0~65535, 0~1024被操作系统或知名软件使用了.我们使用1024以后的。只有程序运行的起来的时候才会使用端口号
2、IP地址类:InetAddress
(1)概念
代表一个IP地址对象
(2)构造方法(都是静态方法)
static InetAddress getLocalHost() 获取【本机】的IP地址
static InetAddress getByName(String host) 通过【IP地址/域名字符串】得到InetAddress对象
(2)成员方法
String getHostAddress() 返回IP地址字符串
String getHostName() 返回主机名
public static void main(String[] args) throws UnknownHostException {
//构造方法1
// static InetAddress getLocalHost() 获取本机的IP地址
InetAddress localHost = InetAddress.getLocalHost();
System.out.println("localHost = " + localHost); // "DESKTOP-6MABD4P/192.168.84.99"
//普通方法
System.out.println("getHostAddress:" + localHost.getHostAddress()); // 192.168.84.99
System.out.println("getHostName: " + localHost.getHostName()); // DESKTOP-6MABD4P
//构造方法2
// static InetAddress getByName(String host) 通过IP地址/域名字符串得到InetAddress对象
InetAddress byName = InetAddress.getByName("192.168.84.99");
System.out.println("byName = " + byName);
InetAddress bxg = InetAddress.getByName("ntlias3.boxuegu.com");
System.out.println("bxg = " + bxg);
}
3、UDP传输数据
1、UDP传输数据,需要:
发送端,接收端以及他们的数据包
2、发送端 + 接收端—DatagramSocket类
DatagramSocket类的API
//构造方法
DatagramSocket() //创建一个UDP【发送端】,随机绑定一个端口 (UDP发送端)
DatagramSocket(int port) //创建一个UDP【接收端】,绑定要指定的端口 (UDP接收端)
成员方法
void send(DatagramPacket p) //发送数据的
void receive(DatagramPacket p) //接收数据
3、数据包—DatagramPacket类
DatagramPacket类的API
//构造方法
DatagramPacket(byte[] buf, int length) //创建空的数据包,用来接收数据的(接收数据的包)
DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port) //创建一个有数据的数据包,用来发送的
//成员方法
int getLength()//得到包已接收到的数据长度
String getAddress();//得到发送端的IP
String getPort();//得到发送端的端口号
4、UDP数据传输的实现步骤及其代码
(1)建立发送端步骤:
1.创建发送端
2.创建一个【有数据的】数据包(DatagramPacket)(创建一封信)
3.调用发送端的【发送方法】
4.关闭资源
(2)建立接收端步骤:
1.创建一个接收端
2.创建一个【空的】数据包
3.调用接收端的【接收方法】
4.操作数据
5.关闭资源
注意:
接收端一定要有固定的端口让发送端能够找到
先运行接收端,后运行发送端
(3)发送端代码实现
public static void main(String[] args) throws IOException {
System.out.println("发送端启动啦!");
// UDP发送端(DatagramSocket)
// 1.创建发送端
// DatagramSocket() 创建一个UDP发送端,随机绑定一个端口 (UDP发送端)
DatagramSocket socket = new DatagramSocket();
// 2.创建一个数据包(DatagramPacket)(创建一封信)
String str = "你好,约吗?";
byte[] bytes = str.getBytes();
DatagramPacket packet = new DatagramPacket(bytes, 0, bytes.length, InetAddress.getLocalHost(), 6666);
// 3.调用发送端的发送方法
// void send(DatagramPacket p) 发送数据的
socket.send(packet);
// 4.关闭资源
socket.close();
}
(4)接收端代码实现
public static void main(String[] args) throws IOException {
System.out.println("接收端启动啦!");
// 1.创建一个接收端
// DatagramSocket(int port) 创建一个UDP发送端,绑定要指定的端口 (UDP接收端)
DatagramSocket socket = new DatagramSocket(6666);
// 2.创建一个空的数据包
byte[] buf = new byte[1024];
DatagramPacket packet = new DatagramPacket(buf, buf.length);
// 3.调用接收端的接收方法
socket.receive(packet);
// 4.操作数据
// 得到接收的数据的数量
int length = packet.getLength();
System.out.println("接收到的数据: " + new String(buf, 0, length));
// 得到发送者的IP和端口
System.out.println("发送者IP:" + packet.getAddress());
System.out.println("发送者端口: " + packet.getPort());
// 5.关闭资源
socket.close();
}
4、TCP传输数据
1、TCP传输数据,需要:
客户端和服务端
2、客户端—Socket类
(1)构造方法
Socket(String host, int port) 创建客户端并连接指定主机的指定端口
(2)普通方法(这里的普通方法虽然属于Socket类,但是服务端同样可以使用)
InputStream getInputStream() 得到输入流
OutputStream getOutputStream() 得到输出流
3、服务端—ServerSocket类
(1)构造方法:
ServerSocket(int port) 创建一个服务端,绑定要指定的端口
(2)普通方法
Socket accept() 同意客户端的请求
4、TCP数据传输的实现步骤及其代码
(1)建立客户端步骤
1.连接服务端
2.得到输出流
3.写数据
4.得到输入流
5.读取数据
6.关闭资源
(2)建立服务端步骤
1.创建服务端
2.等待客户端的连接,并同意
3.得到输入流读取数据
4.得到输出流写数据
5.关闭资源
注意:
客户端和服务端指定的端口必须一致
先运行服务端,后运行客户端
(3)客户端代码实现
public static void main(String[] args) throws IOException {
System.out.println("客户端启动啦!");
// 1.连接服务端
// Socket(String host, int port) 创建客户端并连接指定主机的指定端口
Socket socket = new Socket("127.0.0.1", 10086);
// 2.得到输出流
OutputStream out = socket.getOutputStream();
// 3.写数据
out.write("你好,约吗???".getBytes());
// 4.得到输入流
InputStream in = socket.getInputStream();
// 5.读取数据
byte[] buf = new byte[1024];
int len = in.read(buf);
System.out.println("客户端收到数据: " + new String(buf, 0, len));
// 6.关闭资源
in.close();
out.close();
socket.close();
}
(4)服务端代码实现
public static void main(String[] args) throws IOException {
System.out.println("服务端启动啦!");
// 1.创建服务端
// ServerSocket(int port) 创建一个服务端,绑定要指定的端口
ServerSocket ss = new ServerSocket(10086);
// 2.等待客户端的连接,并同意
Socket socket = ss.accept();
// 3.得到输入流读取数据
InputStream in = socket.getInputStream();
byte[] buf = new byte[1024];
int len = in.read(buf);
System.out.println("服务端收到: " + new String(buf, 0, len));
// 4.得到输出流写数据
OutputStream out = socket.getOutputStream();
out.write("老地方见".getBytes());
// 5.关闭资源
out.close();
in.close();
socket.close();
ss.close();
}
5、案例练习:文件上传
由于是文件上传,必须准确接受数据,故使用TCP传输数据
注意
客户端代码中解决了文件上传不会停止的问题:socket.shutdownOutput();语句
f服务端代码中解决了上传文件名冲突的问题:让文件名和时间的毫秒值相关,再添加随机数,上传者的ip
服务端
public static void main(String[] args) throws IOException {
System.out.println("文件上传服务端启动啦!");
// 1.创建服务端
ServerSocket ss = new ServerSocket(9999);
// 2.同意客户端的请求
Socket socket = ss.accept();
// 3.得到输入流
InputStream in = socket.getInputStream();
// 让文件名和时间的毫秒值相关,再添加随机数,上传者的ip
String ip = socket.getInetAddress().getHostAddress();
String fileName = System.currentTimeMillis() + "_" + new Random().nextInt(88888888) + "_" + ip + ".png";
// 4.创建文件输出流
FileOutputStream fos = new FileOutputStream("day11demo/upload/" + fileName);
// 5.循环读写数据
int len;
byte[] buf = new byte[1024];
while ((len = in.read(buf)) != -1) {
fos.write(buf, 0, len);
}
System.out.println("服务端上传完成!");
// 6.得到输出流
OutputStream out = socket.getOutputStream();
// 7.发送数据给客户端
out.write("上传完成啦啦啦啦!".getBytes());
// 8.关闭资源
out.close();
fos.close();
in.close();
socket.close();
ss.close();
}
客户端
public static void main(String[] args) throws IOException {
// 1.创建客户端
Socket socket = new Socket("127.0.0.1", 9999);
// 2.得到Socket的输出流
OutputStream out = socket.getOutputStream();
// 3.创建文件输入流
FileInputStream fis = new FileInputStream("c:/MyFileTest/xyz.png");
// 4.循环读写数据
int len;
byte[] buf = new byte[1024];
while ((len = fis.read(buf)) != -1) {
out.write(buf, 0, len);
}
// 文件发送完毕了.给socket输出流写一个结束标记
socket.shutdownOutput();
System.out.println("客户端上传完成!");
// 5.得到Socket的输入流
InputStream in = socket.getInputStream();
// 6.读取数据
byte[] buf2 = new byte[1024];
int len2 = in.read(buf2);
System.out.println("客户端收到: " + new String(buf2, 0, len2));
// 7.关闭资源
in.close();
fis.close();
out.close();
socket.close();
}
6、案例练习:文件上传多线程版本
1.如何实现多人上传?
添加了while循环让服务端可以多个同意客户端的请求和上传(while循环实现多人上传)
2.如何实现同时上传
采用多线程(多线程实现同时上传)
主线程负责3个任务:
1、循环同意客户端的请求任务
2、一旦同意客户端的请求后就创建一个新的线程执行上传的任务
3、run方法外面的while循环任务
服务端
public static void main(String[] args) throws IOException {
System.out.println("文件上传服务端启动啦!");
// 1.创建服务端//让主线程main“做同意”的任务(此外主线程还做while循环的任务)
ServerSocket ss = new ServerSocket(9999);
while (true) {
// 2.同意客户端的请求
Socket socket = ss.accept();
// 创建一个新的线程执行上传的任务
UploadTask task = new UploadTask(socket);
new Thread(task).start();
}
}
客户端
public static void main(String[] args) throws IOException {
// 1.创建客户端
Socket socket = new Socket("127.0.0.1", 9999);
// 2.得到Socket的输出流
OutputStream out = socket.getOutputStream();
// 3.创建文件输入流
FileInputStream fis = new FileInputStream("c:/MyFileTest/xyz.png");
// 4.循环读写数据
int len;
byte[] buf = new byte[1024];
while ((len = fis.read(buf)) != -1) {
out.write(buf, 0, len);
}
// 文件发送完毕了.给socket输出流写一个结束标记
socket.shutdownOutput();
System.out.println("客户端上传完成!");
// 5.得到Socket的输入流
InputStream in = socket.getInputStream();
// 6.读取数据
byte[] buf2 = new byte[1024];
int len2 = in.read(buf2);
System.out.println("客户端收到: " + new String(buf2, 0, len2));
// 7.关闭资源
in.close();
fis.close();
out.close();
socket.close();
}
多线程
public class UploadTask implements Runnable {
private Socket socket;//让主线程main“做同意”的任务(此外主线程还做while循环的任务)
//由于服务端需要客户端的socket,但是run方法不能传参数,故解决办法是: 定义成员变量socket,并用构造方法得到该参数
public UploadTask(Socket socket) {
this.socket = socket;
}
@Override
public void run() {//run方法不能抛异常,必须try...catch
try {
// 上传的代码放到新的线程中执行
// 3.得到输入流
InputStream in = socket.getInputStream();
// 让文件名和时间的毫秒值相关,再添加随机数,上传者的ip
String ip = socket.getInetAddress().getHostAddress();
String fileName = System.currentTimeMillis() + "_" + new Random().nextInt(88888888) + "_" + ip + ".png";
// 4.创建文件输出流
FileOutputStream fos = new FileOutputStream("day11demo/upload/" + fileName);
// 5.循环读写数据
int len;
byte[] buf = new byte[1024];
while ((len = in.read(buf)) != -1) {
fos.write(buf, 0, len);
}
System.out.println("服务端上传完成!");
// 6.得到输出流
OutputStream out = socket.getOutputStream();
// 7.发送数据给客户端
out.write("上传完成啦啦啦啦!".getBytes());
// 8.关闭资源
out.close();
fos.close();
in.close();
socket.close();
} catch (Exception e) {
System.out.println("上传失败");
}
}
}
7、案例练习:模拟网站服务器=
浏览器(Browser)现成的:
地址栏输入的:http://127.0.0.1:8888/web/index.html
http: 网络协议,可以传输文本图片,底层就是TCP协议
127.0.0.1: 服务器的IP地址
8888: 服务器的端口号
web/index.html: 找服务器要这个资源(或者说客户在浏览器对服务器发出要这个资源的信息)
服务器(Server)我们要编写的 ServerSocket
1.得到浏览器想要什么数据
2.返回浏览器想要的数据
模拟网站服务器的核心要领:
1、浏览器要什么数据,服务器就给什么数据
2、底层是TCP协议
public static void main(String[] args) throws IOException {
System.out.println("服务器启动啦");
ServerSocket ss = new ServerSocket(8888);
while (true) {
Socket socket = ss.accept();
// 使用多线给浏览器返回数据
new Thread(new Runnable() {
@Override
public void run() {
try {
// 1.得到浏览器想要什么数据(找到它想要的数据所在的本机的路径)
InputStream in = socket.getInputStream();
System.out.println(socket.getInetAddress());//输出了一堆数据,但是我们只想要第一行数据
// 得到【一行数据】,需要使用字符输入缓冲流: GET /web/index.html HTTP/1.1
// BufferedReader(Reader r); 构造方法中需要字符流,我们socket中得到的是字节流
// 要将字节流转成字符流?转换流可以做这个事情
InputStreamReader isr = new InputStreamReader(in);//把字节流转换为字符流
BufferedReader br = new BufferedReader(isr);//得到字符输入缓冲流
String line = br.readLine(); // 读取到"GET /web/index.html HTTP/1.1"
//但我们只想要 index.html HTTP,故:
// 使用空格分割数据 -> split(" ") -> {"GET", "/web/index.html", "HTTP/1.1"}
String fileName = line.split(" ")[1];//获取分割的字符串数组的第二个数据
// System.out.println("fileName = " + fileName); // /web/index.html
String path = "day11demo" + fileName; // day11demo/web/index.html
System.out.println("path = " + path);
// 2.返回浏览器想要的数据
OutputStream out = socket.getOutputStream();
// 固定套路
out.write("HTTP/1.1 200 OK\r\n".getBytes());
out.write("Content-Type:text/html\r\n".getBytes());
out.write("\r\n".getBytes());
// 读取文件中的数据写给浏览器
FileInputStream fis = new FileInputStream(path);
// 循环读写
int len;
byte[] buf = new byte[1024 * 8];
while ((len = fis.read(buf)) != -1) {
out.write(buf, 0, len);
}
fis.close();
out.close();
in.close();
socket.close();
} catch (Exception e) {
System.out.println("请求失败");
}
}
}).start();
}
}
\n".getBytes());
// 读取文件中的数据写给浏览器
FileInputStream fis = new FileInputStream(path);
// 循环读写
int len;
byte[] buf = new byte[1024 * 8];
while ((len = fis.read(buf)) != -1) {
out.write(buf, 0, len);
}
fis.close();
out.close();
in.close();
socket.close();
} catch (Exception e) {
System.out.println("请求失败");
}
}
}).start();
}
}