网络编程
服务器介绍
服务器就是一台配置很高的电脑
互联网架构分类
BS架构 :
- Browser / Server
优点:不需要下载客户端,使用起来非常方便。
缺点:用户体验比较差
CS架构 :
- Client / Server
缺点:需要下载客户端,而且每一次要更新内容的时候,都要更新客户端,甚至要重新下载,非常麻烦。
优点:画面非常精美,用户体验比较好。
网络编程三要素
1)IP地址
设备在网络中的唯一标识
2)端口
程序在设备中的唯一标识
3)协议
通过计算机网络可以使多台计算机实现连接,位于同一个网络中的计算机在进行连接和通信时需要遵守一定的规则,这就好比在道路中行驶的汽车一定要遵守交通规则一样。在计算机网络中,这些连接和通信的规则被称为网络通信协议,它对数据的传输格式、传输速率、传输步骤等做了统一规定,通信双方必须同时遵守才能完成数据交换。常见的协议有 UDP协议 和 TCP协议
网络通讯协议,数据在网络中进行传输
需要遵守协议,不同的协议,带来的效果不同
- UDP:面向无连接,速度快,数据不安全,有传输的大小限制
举例:手机发短信
(腾讯会议屏幕共享):画面丢失了几个数据包,掉帧) - Tcp:面向连接,速度慢,数据安全,没有传输的大小限制
举例:手机打电话- 面向连接:三次握手
1.(客户端向服务端发起请求(拨号)
2.(服务端响应客户端请求(接通)
3.(数据传输(通话)
- 面向连接:三次握手
IP地址
IP(Internet Protocol):全称 【互联网协议地址】,是分配给上网设备的唯一标志。
- 常见的IP分类为:IPv4 和 IPv6
IPv4
IPv6
IP的分类
IP地址形式:
- 公网地址、和私有地址(局域网使用)。
- 192.168.开头的就是常见的局域网地址,范围即为192.168.0.0–192.168.255.255,专门为组织机构内部使用。
IP常用命令:
- ipconfig:查看本机IP地址
- Ping IP地址:检查网络是否连通
特殊IP地址:
- 本机IP:127.0.0.1或者localhost:称为回送地址也可称本地回环地址,只会寻找当前所在本机。
ip和域名
InetAddress
常用方法:
方法 | 说明 |
---|---|
getByName | 确定主机名称的IP地址。主机名称可以是机器名称,也可以是IP地址 |
getHostName | 获取主机名 |
getHostAddress | 获取IP |
//1.获取一个IP地址(在网络中计算机的对象)
InetAddress address = InetAddress.getByName("127.0.0.1");
//2.获取主机名获取ip地址
//细节:如果能获取到主机名返回的就是主机名
//但是如果有的情况下,获取不到,返回的就是ip地址
String hostName = address.getHostName();
System.out.println(hostName);
//3.获取IP
String ip = address.getHostAddress();
System.out.println(ip);
端口号
-
端口:应用程序在设备中唯一的标识。
-
端口号:用两个字节表示的整数,它的取值范围是0~65535。
其中0~1023之间的端口号用于一些知名的网络服务或者应用。
我们自己使用1024以上的端口号就可以了。
-
常见的端口号
- 8080 :tomcat 服务器
- 3306 :MySQL 数据库
- 80 : web
-
-
注意:写代码的时候, 注意端口绑定, 不要出现端口冲突
UDP协议和TCP协议介绍
UDP :
-
用户数据报协议(User Datagram Protocol)
-
UDP是面向无连接通信协议。
速度快,有传输大小限制一次最多发送64K,数据不安全,易丢失数据
- UDP协议没有严格区分出客服端和服务端
- 客户端DatagramSocket
- send:发送 DatagramPacket:包裹对象
- 服务端DatagramSocket
- receive:接收
- 客户端DatagramSocket
TCP :
-
传输控制协议TCP(Transmission Control Protocol)
-
TCP协议是面向连接的通信协议。
速度慢,没有大小限制,数据安全。
UDP协议收发数据
发送数据
1 创建发送端对象 : DatagramSocket ds = new DatagramSocket();
2 创建数据包 : DatagramPacket dp = new DatagramPacket(字节数组, 数据个数, 接收端IP, 接收端端口);
3 发送数据 : ds.send(dp);
4 释放资源 : ds.close();
public static void main(String[] args) throws IOException {
/*
DatagramPacket(byte[] buf, int length, InetAddress address, int port)
参数1: 将传输的数据, 转换为字节, 并存入一个数组
参数2: 数组的长度
参数3: InetAddress(IP对象)
参数4: 端口号
*/
Scanner sc = new Scanner(System.in);
//客户端搭建码头
DatagramSocket socket = new DatagramSocket();
while (true){
//准备要发的数据
System.out.println("请输入:");
String s = sc.nextLine();
//当输入 886 程序停止
if (s.equals("886")){
break;
}
byte[] bytes = s.getBytes();
//将需要发的数据打包
DatagramPacket packet = new DatagramPacket(bytes, bytes.length,
InetAddress.getByName("192.168.11.50"),9898);
//发送数据
socket.send(packet);
}
//关流
socket.close();
接收数据
1 创建接收端对象 : DatagramSocket ds = new DatagramSocket(端口);
2 创建数据包 : DatagramPacket dp = new DatagramPacket(字节数组, 数组的长度);
3 接收数据 : ds.receive(dp);
4 使用接收的数据 : System.out.println(new String(bys, 0, dp.getLength()));
5 释放资源 : ds.close();
public static void main(String[] args) throws IOException {
//服务端搭建码头
DatagramSocket socket = new DatagramSocket(6789);
while (true){
//准备一个接收数据的包裹
byte[] bytes = new byte[1024];
DatagramPacket packet = new DatagramPacket(bytes, bytes.length);
//接收数据
socket.receive(packet);
//拆包裹
byte[] data = packet.getData();
String s = new String(data, 0, packet.getLength());
String hostAddress = packet.getAddress().getHostAddress();
System.out.println("来自"+hostAddress+"的信息:"+s);
}
/*
//关流
socket.close();
*/
}
TCP 协议发数据
-
Java中的 TCP 通信
- Java对基于TCP协议的的网络提供了良好的封装,使用Socket对象来代表两端的通信端口,并通过Socket产生IO流来进行网络通信。
- Java为客户端提供了 Socket 类,为服务器端提供了 ServerSocket 类
构造方法
方法名 | 说明 |
---|---|
Socket(InetAddress address,int port) | 创建流套接字并将其连接到指定IP指定端口号 |
Socket(String host, int port) | 创建流套接字并将其连接到指定主机上的指定端口号 |
相关方法
方法名 | 说明 |
---|---|
InputStream getInputStream() | 返回此套接字的输入流 |
OutputStream getOutputStream() | 返回此套接字的输出流 |
案例解析
public class ClientDemo {
public static void main(String[] args) throws IOException {
//创建客户端的Socket对象(Socket)
//Socket(String host, int port) 创建流套接字并将其连接到指定主机上的指定端口号
Socket s = new Socket("127.0.0.1",10000);
//获取输出流,写数据
//OutputStream getOutputStream() 返回此套接字的输出流
OutputStream os = s.getOutputStream();
os.write("hello,tcp,我来了".getBytes());
//释放资源
os.close();
s.close();
}
}
TCP协议收数据
构造方法
方法名 | 说明 |
---|---|
ServletSocket(int port) | 创建绑定到指定端口的服务器套接字 |
相关方法
方法名 | 说明 |
---|---|
Socket accept() | 监听要连接到此的套接字并接受它 |
案例解析
public class ServerDemo {
public static void main(String[] args) throws IOException {
//创建服务器端的Socket对象(ServerSocket)
//ServerSocket(int port) 创建绑定到指定端口的服务器套接字
ServerSocket ss = new ServerSocket(10000);
//Socket accept() 侦听要连接到此套接字并接受它
Socket s = ss.accept();
//获取输入流,读数据,并把数据显示在控制台
InputStream is = s.getInputStream();
byte[] bys = new byte[1024];
int len = is.read(bys);
String data = new String(bys,0,len);
System.out.println("数据是:" + data);
//释放资源
s.close();
ss.close();
}
}
注意事项
- accept方法是阻塞的,作用就是等待客户端连接
- 客户端创建对象并连接服务器,此时是通过三次握手协议,保证跟服务器之间的连接
- 针对客户端来讲,是往外写的,所以是输出流
针对服务器来讲,是往里读的,所以是输入流 - read方法也是阻塞的
- 客户端在关流的时候,还多了一个往服务器写结束标记的动作
- 最后一步断开连接,通过四次挥手协议保证连接终止
三次握手和四次挥手
- UDP : 面向无连接, 数据不安全, 速度快
- 有传输大小限制 , 一次最多只能发送64k
- TCP : 面向连接, 数据安全, 速度相对来说较慢
- 没有传输大小限制
- 三次握手
- 四次挥手
TCP案例:文件上传
案例需求
客户端:数据来自于本地文件,接收服务器反馈
服务器:接收到的数据写入本地文件,给出反馈
相关方法
方法名 | 说明 |
---|---|
void shutdownInput() | 将此套接字的输入流放置在“流的末尾” |
void shutdownOutput() | 禁止用此套接字的输出流 |
UUID. randomUUID() | 生成随机的文件名 |
import java.io.*;
import java.net.Socket;
public class Client02 {
public static void main(String[] args) throws IOException {
//创建Socket对象指定ip和端口(向服务端发起请求)
Socket socket = new Socket("127.0.0.1",6789);
//使用socket对象获取传输数据的流对象
InputStream is = socket.getInputStream();
OutputStream os = socket.getOutputStream();
//将字节流转换为字符流(读写中文)
BufferedReader br = new BufferedReader(new InputStreamReader(is));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(os));
//创建本地文件对象关联文件
File file = new File("D:\\javawork\\熊宝宝.jpg");
//写出文件名到服务端
bw.write(file.getName());
bw.newLine();
bw.flush();
//读取确认信息
String state = br.readLine();
if (state.equals("ok")){
//开始上传,创建本地字节输入流
FileInputStream fis = new FileInputStream(file);
byte[] bys = new byte[8192];
int len;
while ((len = fis.read(bys)) != -1){
os.write(bys,0,len);
}
fis.close();
//关闭写出的流对象,理解为写出结束标志
socket.shutdownOutput();
}
//读取成功上传信息
System.out.println(br.readLine());
//关流
bw.close();
br.close();
socket.close();
}
}
import java.io.*;
import java.net.Socket;
import java.util.UUID;
public class SubmitFileTask implements Runnable{
private Socket socket;
public SubmitFileTask(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
//使用socket对象获取传输数据的流对象
try {
OutputStream os = socket.getOutputStream();
InputStream is = socket.getInputStream();
//将流对象转换为字符流(读写中文)
BufferedReader br = new BufferedReader(new InputStreamReader(is));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(os));
//读取客服端发送的文件名
String fileName = br.readLine();
File file = new File("D:\\", UUID.randomUUID().toString()+fileName);
//回写确认信息
bw.write("ok");
bw.newLine();
bw.flush();
//创建本地输出流
FileOutputStream fos = new FileOutputStream(file);
byte[] bys = new byte[8192];
int len;
while ((len = is.read(bys)) != -1){
fos.write(bys,0,len);
}
fos.close();
//回写上传成功
bw.write("上传成功");
bw.newLine();
bw.flush();
//关流
bw.close();
br.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class Server02 {
public static void main(String[] args) throws IOException {
//创建ServerSocket对象指定端口号
ServerSocket serverSocket = new ServerSocket(6789);
ThreadPoolExecutor pool = new ThreadPoolExecutor(
2,
5,
60,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(10),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy()
);
while (true) {
//调用accept方法响应请求,得到Socket对象
Socket socket = serverSocket.accept();
//重点:将上传的任务交给线程池中的对象处理
pool.submit(new SubmitFileTask(socket));
}
}
}