网络编程
什么是服务器?
服务器就是一台电脑,就是一台计算机,只不过配置非常高。
以前的代码:
都是在自己的电脑上运行的。一些结果也是打印在控制台或者是保存到文件中的。没有一个交互性的效果。
现在学习了网络编程,可以实现计算机跟计算机之间可以传输数据(交互)。
简单理解:你可以给你的同桌发消息了。你的同桌也可以接收到你的消息了。
什么是网络编程?
计算机跟计算机之间通过网络进行数据传输。
常见的互联网架构?
BS、CS
BS和CS的优缺点
BS:
- 优点:不需要下载客户端,使用起来非常方便。
- 缺点:用户体验比较差,画面不够精美。
CS:
- 缺点:需要下载客户端,而且每一次要更新内容的时候,都要更新客户端,甚至要重新下载,非常麻烦。
- 优点:画面非常精美,用户体验比较好。
在以后实际开发中,根据企业的项目需求来,如果是一些电商,新闻的项目对页面要求不高,可以用BS。
这也是为什么,在电脑上,京东,淘宝,新浪新闻都是BS的原因。
在以后实际开发中,如果对画面有强大的要求,一定是用CS,比如游戏。
网络编程三要素
IP、端口、协议
IP:
- 设备在互联网中的地址,唯一的标识。
IP分类:
公网IP,局域网IP
公网IP:对外,在上外网的时候需要使用的。
局域网IP:192.168开头都是局域网地址。作用:节约IP的使用。
常用命令
ipconfig:查看本机IP地址
ping + IP:检查你的电脑跟指定IP的电脑之间网络是否畅通
####扩展:
ping + 域名 — ping www.baidu.com
####特殊IP:
127.0.0.1,也可以是localhost:是回送地址也称本地回环地址,也称本机IP,永远只会寻找当前所在本机。
####问题:(了解扩展)
假设,我的点IP是192.168.1.255,那么这个IP跟127.0.0.1有什么区别?
共同点:都是表示自己的电脑
区别:192.168.1.255是路由器给你分配的。127.0.0.1(localhost)就表示本机不是路由器分配的。
IP的两种表示形式
IPV4、IPV6
IPV4:32bit位,全球最多只能有42亿多,不够了。渐渐会被IPV6替代。
IPV6:128bit位,足够使用。
InetAddress类
常用方法:
getByName :确定主机名称的IP地址。主机名称可以是机器名称,也可以是IP地址
getHostName:获取主机名
getHostAddress :获取IP
代码示例:
//1.获取一个IP地址(在网络中计算机的对象)
InetAddress address = InetAddress.getByName("192.168.89.92");
//2.获取主机名获取ip地址
//细节:如果能获取到主机名返回的就是主机名
//但是如果有的情况下,获取不到,返回的就是ip地址
String hostName = address.getHostName();
System.out.println(hostName);
//3.获取IP
String ip = address.getHostAddress();
System.out.println(ip);
端口:
一般也会成为端口号。是应用程序在设备中唯一的标识。
一个端口只能被一个应用程序绑定。
一个应用程序可以绑定多个端口。
举例:
10000端口,如果被QQ绑定了,那么10000这个端口就不能再被其他软件绑定了。
但是QQ可以绑定10000,还可以绑定其他端口。
端口号的注意点:
端口号的范围:0~65535
0~1023之间的端口号已经被系统或者一些知名的网络应用给占用了。
比如浏览器80端口 tomcat:8080,Mysql:3306
我们自己在使用的时候,需要用1024以上的端口。
扩展(了解)
我怎么知道系统当中有哪些端口被被人占用了呢?
1,我们下午会写代码去绑定端口,你可以随便绑定一个,如果该端口被占用,那么代码就会报错。
如果该端口没有被占用,那么就不会报错。
2,我们可以使用一些第三方软件进行查询。(鲁大师,360,等一下杀毒软件)
以360为例:点功能大全 —> 流量防火墙 —>网络连接可以看到
协议:
数据在网络中传输的规则。常见:UDP,TCP。
UDP:
面向无连接的协议。(发送数据的时候不保证连接已经建立)
速度快,大小限制最多64K,数据不安全,数据容易丢失。
TCP:
面向连接的协议。(发送数据的时候需要保证连接已经建立)
速度慢,没有大小限制,数据安全。
应用场景(了解,扩展)
UDP:一般来讲对速度比较有要求,但是数据丢失一点没有什么太大的关系。
看电影、视频网络会议。
TCP:一般是对数据安全有要求的情况下。
下载安装包,聊天。
UDP协议发送和接收数据
发送端:
public class Client {
public static void main(String[] args) throws IOException {
//1.创建码头
//如果没有绑定端口,那么系统会让程序绑定一个随机的可用端口
DatagramSocket ds = new DatagramSocket();
//2.确定要发送的数据
String s = "吃俺老孙一棒棒";
byte[] bytes = s.getBytes();
//3.确定要发送的计算机
InetAddress address = InetAddress.getByName("127.0.0.1");
//4.确定要发送的端口
int port = 10000;
//5.把上面的数据,计算机,端口打包在一起
DatagramPacket dp = new DatagramPacket
(bytes,bytes.length,address,port);
//6.发送数据
ds.send(dp);
//7.释放资源
ds.close();
}
}
接收端:
public class Server {
public static void main(String[] args) throws IOException {
//1.创建码头的对象
//接收端,在创建对象的时候需要绑定端口
//表示哥们要从10000端口接收数据
DatagramSocket ds = new DatagramSocket(10000);
//因为我们只要接收即可
//所以,在箱子中,只要准备装数据的数组即可
//IP和端口号,在发送数据的时候才需要。
//2.创建一个新的包裹,用于接收数据
DatagramPacket dp = new DatagramPacket(new byte[1024],1024);
//3.接收数据
ds.receive(dp);
//4.从包裹中获取数据
byte[] bytes = dp.getData();
int len = dp.getLength();
String ip = dp.getAddress().getHostAddress();
System.out.println(ip + "发送过来了:" + new String(bytes,0,len));
//5.释放资源
ds.close();
}
}
练习:UDP的聊天室
需求:
按照下面的要求实现程序
UDP发送数据:数据来自于键盘录入,直到输入的数据是886,发送数据结束
UDP接收数据:因为接收端不知道发送端什么时候停止发送,故采用死循环接收
操作细节:
需要把发送端运行多次。
idea默认只能运行一次,所以需要进行配置。
需要保证第一个红色箭头是当前要运行多次的类,再点击下面的Edit Configurations,再按照下面的图解进行设置即可。
代码示例:
客户端(发送端)
public class Client {
public static void main(String[] args) throws IOException {
//UDP发送数据:数据来自于键盘录入,直到输入的数据是886,发送数据结束
//1. 创建码头对象
DatagramSocket ds = new DatagramSocket();
Scanner sc = new Scanner(System.in);
while (true) {
//2.数据是键盘录入
System.out.println("请输出要聊天的内容");
String line = sc.nextLine();
if("886".equals(line)){
break;
}
byte[] bytes = line.getBytes();
//3.确定接收端
InetAddress address = InetAddress.getByName("127.0.0.1");
//4.确定端口
int port = 10000;
DatagramPacket dp = new DatagramPacket
(bytes,bytes.length,address,port);
//5.发送数据
ds.send(dp);
}
//6.释放资源
ds.close();
}
}
服务端(接收端):
public class Server {//服务器
public static void main(String[] args) throws IOException {
//1.创建码头对象
//需要绑定端口,表示在绑定的端口上接收数据
//要跟发送端发送的端口保持一致
DatagramSocket ds = new DatagramSocket(10000);
//2.创建数据包
DatagramPacket dp = new DatagramPacket(new byte[1024], 1024);
//3.循环接收数据
while (true) {
ds.receive(dp);
//4.获取数据并打印
byte[] bytes = dp.getData();
int len = dp.getLength();
String ip = dp.getAddress().getHostAddress();
System.out.println(ip + "发送过来了:" + new String(bytes, 0, len));
}
//在公司一般来讲,服务器不关机
//所以服务器不需要释放资源
}
}
TCP协议发送和接收数据
客户端:
public class Client {
public static void main(String[] args) throws IOException {
//1,创建客户端Socket对象
//去跟127.0.0.1的10000端口进行连接,如果连接不上,直接报错
Socket socket = new Socket("127.0.0.1",10000);
//2.从连接中,获取输出流,往服务器写数据
OutputStream os = socket.getOutputStream();
//3.写出数据
String s = "abc";
os.write(s.getBytes());
//4.释放资源
socket.close();
}
}
服务端:
public class Server {
public static void main(String[] args) throws IOException {
//1.创建服务端对象,并绑定10000端口
ServerSocket ss = new ServerSocket(10000);
//2.等待客户端链接
//此时服务端在accept方法这里,死等客户端来链接
//如果有客户端来链接,会返回客户端的连接对象
Socket socket = ss.accept();
//3.从连接通道中获取流读数据
InputStream is = socket.getInputStream();
//4.因为不知道发了多少数据,所以循环读取
int b;
while ((b = is.read()) != -1){
System.out.println((char)b);
}
//5.释放资源
//断开和客户端之间的连接
socket.close();
//关闭服务器
ss.close();
}
}
3.7 文件上传
import java.io.*;
import java.net.Socket;
//给服务端上传文件
public class Client {
public static void main(String[] args) throws IOException {
//1.创建对象并连接服务器
Socket socket = new Socket("127.0.0.1",10000);
//2.读取本地文件并写到服务器
//2.1创建本地的字节输入流对象,准备读取本地文件
FileInputStream fis = new FileInputStream("meinv.jpg");
//2.2获取客户端的输出流,准备往服务器写出数据
OutputStream os = socket.getOutputStream();
//2.3循环读取数据并写到服务器
int b;//表示当前从文件中读取到的数据
while((b = fis.read()) != -1){
System.out.println(1);
os.write(b);//把这个数据写到服务器
}
//给服务端一个结束标记
socket.shutdownOutput();
System.out.println("客户端上传文件完毕");
//接收服务端返回的数据
BufferedReader br = new BufferedReader
(new InputStreamReader(socket.getInputStream()));
String serverMessage = br.readLine();
System.out.println(serverMessage);
//3.释放资源
socket.close();
}
}
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.UUID;
public class Server {
public static void main(String[] args) throws IOException {
//1.开启服务
ServerSocket ss = new ServerSocket(10000);
//2.等待客户端链接
Socket socket = ss.accept();
//3.获取输入流,准备接收客户端发送过来的数据
InputStream is = socket.getInputStream();
//生成一个随机的文件名
String fileName = UUID.randomUUID().toString().replace("-", "");
//创建本地的字节输出流对象,准备把读取到的数据保存到本地
FileOutputStream fos = new FileOutputStream("day12-code\\serverfile\\" + fileName + ".jpg");
//循环读取数据,并写到本地
int b;
//此时读到了shutdownOutput的结束标记
while ((b = is.read()) != -1) {
fos.write(b);
}
System.out.println("看看我执行了吗?");
//给客户端返回上传成功的信息
PrintStream ps = new PrintStream(socket.getOutputStream());
ps.println("上传成功");
//释放资源
socket.close();
ss.close();
}
}
文件上传多线程版
客户端
//给服务端上传文件
public class Client {
public static void main(String[] args) throws IOException {
//1.创建对象并连接服务器
Socket socket = new Socket("127.0.0.1",10000);
//2.读取本地文件并写到服务器
//2.1创建本地的字节输入流对象,准备读取本地文件
FileInputStream fis = new FileInputStream("meinv.jpg");
//2.2获取客户端的输出流,准备往服务器写出数据
OutputStream os = socket.getOutputStream();
//2.3循环读取数据并写到服务器
int b;//表示当前从文件中读取到的数据
while((b = fis.read()) != -1){
System.out.println(1);
os.write(b);//把这个数据写到服务器
}
//给服务端一个结束标记
socket.shutdownOutput();
System.out.println("客户端上传文件完毕");
//接收服务端返回的数据
BufferedReader br = new BufferedReader
(new InputStreamReader(socket.getInputStream()));
String serverMessage = br.readLine();
System.out.println(serverMessage);
//3.释放资源
socket.close();
}
}
服务端:
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.UUID;
public class Server {
public static void main(String[] args) throws IOException {
//1.开启服务
ServerSocket ss = new ServerSocket(10000);
while(true){
//2.等待客户端链接
Socket socket = ss.accept();
//一旦有客户端来链接。就开启线程去进行处理
new Thread(
//线程要处理的任务
()->{
try {
//3.获取输入流,准备接收客户端发送过来的数据
InputStream is = socket.getInputStream();
//生成一个随机的文件名
String fileName = UUID.randomUUID().toString().replace("-", "");
//创建本地的字节输出流对象,准备把读取到的数据保存到本地
FileOutputStream fos = new FileOutputStream("day12-code\\serverfile\\" + fileName + ".jpg");
//循环读取数据,并写到本地
int b;
//此时读到了shutdownOutput的结束标记
while ((b = is.read()) != -1) {
fos.write(b);
}
System.out.println("看看我执行了吗?");
//给客户端返回上传成功的信息
PrintStream ps = new PrintStream(socket.getOutputStream());
ps.println("上传成功");
//断开客户端的连接
socket.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
//释放资源的代码一般都是写在finally里面
}
}
).start();
}
//服务器不关机
//ss.close();
}
}
3.9 文件上传线程池版
客户端:
import java.io.*;
import java.net.Socket;
//给服务端上传文件
public class Client {
public static void main(String[] args) throws IOException {
//1.创建对象并连接服务器
Socket socket = new Socket("127.0.0.1",10000);
//2.读取本地文件并写到服务器
//2.1创建本地的字节输入流对象,准备读取本地文件
FileInputStream fis = new FileInputStream("meinv.jpg");
//2.2获取客户端的输出流,准备往服务器写出数据
OutputStream os = socket.getOutputStream();
//2.3循环读取数据并写到服务器
int b;//表示当前从文件中读取到的数据
while((b = fis.read()) != -1){
System.out.println(1);
os.write(b);//把这个数据写到服务器
}
//给服务端一个结束标记
socket.shutdownOutput();
System.out.println("客户端上传文件完毕");
//接收服务端返回的数据
BufferedReader br = new BufferedReader
(new InputStreamReader(socket.getInputStream()));
String serverMessage = br.readLine();
System.out.println(serverMessage);
//3.释放资源
socket.close();
}
}
服务端:
public class Server {
public static void main(String[] args) throws IOException {
//创建线程池
ExecutorService pool = Executors.newFixedThreadPool(10);
//1.开启服务
ServerSocket ss = new ServerSocket(10000);
while(true){
//2.等待客户端链接
Socket socket = ss.accept();
//一旦有客户端来链接。就提交给线程池
pool.submit(new ServerThread(socket));
}
//服务器不关机
//ss.close();
}
}
服务端线程类:
public class ServerThread implements Runnable {
Socket socket;
public ServerThread(Socket socket){
this.socket = socket;
}
@Override
public void run() {
try {
//3.获取输入流,准备接收客户端发送过来的数据
InputStream is = socket.getInputStream();
//生成一个随机的文件名
String fileName = UUID.randomUUID().toString().replace("-", "");
//创建本地的字节输出流对象,准备把读取到的数据保存到本地
FileOutputStream fos = new FileOutputStream("day12-code\\serverfile\\" + fileName + ".jpg");
//循环读取数据,并写到本地
int b;
//此时读到了shutdownOutput的结束标记
while ((b = is.read()) != -1) {
fos.write(b);
}
System.out.println("看看我执行了吗?");
//给客户端返回上传成功的信息
PrintStream ps = new PrintStream(socket.getOutputStream());
ps.println("上传成功");
//断开客户端的连接
socket.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
//释放资源的代码一般都是写在finally里面
}
}
}