网络通信的基本模式— CS/BS
Client-Server(Cs) : 主要是客户端与服务端之间的联系(就是相应的App和后台的关系)
模型
Browser/Server(Bs): 主要是浏览器与服务端的联系(即不需要下载App,只需下载浏览器即可)
模型
网络通信的三要素:ip地址/端口号/协议
IP地址:“互联网协议地址”
定义: 设备在网络中的地址,是唯一的标识(相当于每一个联网主机的身份证号码)
分类: IPv4 和 IPv6
- IPv4 :192.168.0.1 形式
- IPv6 :ABCD:EF01:2345:6789:ABCD:EF01:2345:6789 形式
IP常用命令:
- ipconfig: 查看本机IP地址
- ping IP地址:检查网络是否连接
特殊IP地址:
本机IP:127.0.0.1或者localhost:称为会送地址也可称本地回环地址,只会寻找当前所在本机
InetAddress类的使用:
import java.io.IOException;
import java.net.InetAddress;
//相当于IP
public class InetAddressDemo1 {
public static void main(String[] args) throws IOException {
//1.获取本机地址对象
InetAddress ip1 = InetAddress.getLocalHost();
System.out.println(ip1.getHostName());//这里ip1已经toString过了
System.out.println(ip1.getHostAddress());
//2.获取域名ip对象
InetAddress ip2 = InetAddress.getByName("www.baidu.com");//根据域名来获取ip地址
System.out.println(ip2);
//3.获取公网IP对象
InetAddress ip3 = InetAddress.getByName("220.181.38.150");
System.out.println(ip3);
//4.判断是否能通: ping 5s之前测试是否可通
System.out.println(ip3.isReachable(5000));
}
}
端口号:标识计算机设备上运行的进程(程序)
端口: 应用程序在设备中的唯一标识
注意: 我们自己开发的程序选择注册端口,且一个设备中不能出现两个程序的端口号一样,否则出错。
通信协议
协议: 数据在网络中传输的规则,常见的协议有UDP协议和TCP协议
传输层的2个常见协议:
- TCP :传输控制协议
- UDP :用户数据报协议
TCP协议特点:
- 使用TCP协议,必须双方先建立连接,他是一种面向连接的可靠通信协议
- 传输前,采用 “三次握手” 方式建立连接,所以是可靠的,四次挥手 断开连接
- 在连接中可进行大数据量的传输
- 连接发送数据都需要确认,且传输完毕后,还需要释放已建立的连接,通信效率较低
TCP协议通信场景:
- 对信息安全要求比较高的场景,例如文件下载,金融等数据的传输
UDP协议:
- UDP协议是一种无连接,不可靠传输的协议
- 将数据源IP,目的地IP和端口封装成数据包,不需要建立连接
- 每个数据包的大小限制在64KB内
- 发送不管对方是否准备好,接收放收到也不确认,所以是不可靠的
- 可以广播发送,发送数据结束时无需释放资源,开销小,速度快
UDP协议通信场景:
- 语音通话,视频会话等
UDP通信:
DatagramSocket() : 创建发送端的Socket对象
DatagramSocket(int port): 创建接收端对象 参数是端口的意思
DatagramPacket: 用于封装数据包的对象(可以理解为菜盘子)
public DatagramPacket(byte buf[], int length,
InetAddress address, int port)
//参数一:封装要发送的数据
//参数二:发送数据的大小
//参数三:服务端的主机IP地址
//参数四:服务端的端口号
//常用方法
public void send(DatagramPacket dp) :发送数据包
public void receive(DatagramPacket dp) :接收数据包
一发一收:
接收端代码
import java.net.DatagramPacket;
import java.net.DatagramSocket;
/**
* 接收端
*/
public class ServerDemo2 {
public static void main(String[] args) throws Exception {
System.out.println("============服务端启动成功===========");
//1.创建接收端对象 需要注册端口
DatagramSocket socket = new DatagramSocket(8888);
//2.创建数据包对象接收数据
//创建接收数据的数据包的大小
byte[] buf = new byte[1024 * 64];
DatagramPacket packet = new DatagramPacket(buf,buf.length);
//3.通过接收端对象的receive方法来接收数据
socket.receive(packet);
//4.取出数据
//读取多少倒出多少
int len = packet.getLength();
String rs = new String(buf,0,len);
System.out.println("收到了" + rs);
socket.close();
}
}
发送端代码
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
/**
* 发送端
*/
public class ClientDemo1 {
public static void main(String[] args) throws Exception {
System.out.println("============接收端启动成功===========");
//1.创建发送端对象:发送端自带默认的端口号
DatagramSocket socket = new DatagramSocket();
//2.创建一个数据包对象封装对象
String str = "我是发送端";
DatagramPacket packet = new DatagramPacket(str.getBytes(),str.length(),
InetAddress.getLocalHost(),8888);
//3.发送数据出去 用发送端对象中的send方法将数据发送出去
socket.send(packet);
//4.关闭资源
socket.close();
}
}
多发多收:
接收端
import java.net.DatagramPacket;
import java.net.DatagramSocket;
/**
* 接收端
*/
public class ServerDemo2 {
public static void main(String[] args) throws Exception {
System.out.println("============服务端启动成功===========");
//1.创建接收端对象 需要注册端口
DatagramSocket socket = new DatagramSocket(8888);
//2.创建数据包对象接收数据
//创建接收数据的数据包的大小
byte[] buf = new byte[1024 * 64];
DatagramPacket packet = new DatagramPacket(buf,buf.length);
while (true) {
//3.通过接收端对象的receive方法来接收数据
socket.receive(packet);
//4.取出数据
//读取多少倒出多少
int len = packet.getLength();
String rs = new String(buf,0,len);
System.out.println("收到了来自" + packet.getAddress() + "的消息" + ",对方端口为" +
packet.getPort() + rs);
}
}
}
发送端:
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner;
/**
- 发送端 多发多收
*/
public class ClientDemo1 {
public static void main(String[] args) throws Exception {
System.out.println("============接收端启动成功===========");
//1.创建发送端对象:发送端自带默认的端口号
DatagramSocket socket = new DatagramSocket();
//2.创建一个数据包对象封装对象
//3.创建扫描器,来发送信息
Scanner sc = new Scanner(System.in);
while (true) {
System.out.println("请说:");
String str = sc.nextLine();
//通过判断是否结束会话
if ("exit".equals(str)){
System.out.println("离线成功");
//5.关闭资源
socket.close();
//6.跳出循环结束程序
break;
}
DatagramPacket packet = new DatagramPacket(str.getBytes(),str.length(),
InetAddress.getLocalHost(),8888);
//4.发送数据出去 用发送端对象中的send方法将数据发送出去
socket.send(packet);
}
}
}
广播与组播
UDP实现广播:
- 使用广播地址:255.255.255.255
- 具体操作:
①:发送端发送的数据包的目的地写的是广播地址,且指定端口
②:本机所在的网段的其他主机的程序只要匹配端口成功即就可以收到消息了。
将目的地改为广播地址:
DatagramPacket packet = new DatagramPacket(str.getBytes(),str.length(),
InetAddress.getByName("255.255.255.255"),8888);
UDP实现组播:
- 使用组播地址:224.0.0.0 ~ 239.255.255.255
- 具体操作:
①:发送端的数据包的目的地是组播IP
②:接收端必须保定该组播IP,端口还要对应发送端的目的端口,这样即可接收该组播消息
③:DatagramSocket的子类MulticastSocket可以在接收端绑定组播IP
TCP通信:
注意: 在Java中只要是使用java.net.Socket类实现通信,底层即是使用了TCP协议
Socket类: TCP通信的客户端的代表类
TCP通信如何使用Socket管道发送和接收数据?
- OutputStream getOutputStream() :获得字节输出流对象(发)
- InputStream getInputStream() :获得字节输入流对象(收)
一发一收:
客户端代码:
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;
/**
* 完成Socket网络编程入门案例的客户端开发 一发一收
*/
public class SocketClientDemo {
public static void main(String[] args) throws Exception {
System.out.println("==========客户端启动成功=========");
//1.创建Socket通信管道请求服务端连接
//public Socket(String host, int port)
// 参数一:服务器的IP地址
// 参数二:服务端端口
Socket socket = new Socket("127.0.1",6666);
//2.从socket通信管道中得到一个字节输出流
OutputStream os = socket.getOutputStream();
//3.把低级的字节流包装成打印流
PrintStream ps = new PrintStream(os);
//4.发送消息
ps.println("我是TCP客户端,我已经与你对接并发出邀请");
ps.flush();
}
}
服务端代码:
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
/**
* 开发socket网络编程入门代码的服务端 实现接收消息
*/
public class ServerSocketDemo {
public static void main(String[] args) throws Exception {
System.out.println("==========服务端启动成功=========");
//1.注册接收端对象
ServerSocket socket = new ServerSocket(6666);
//2.调用accept方法:等待接收客户端的Socket连接请求,家里Socket通信管道
Socket accept = socket.accept();
//3.从socket通信管道中得到一个字节输入流
InputStream is = accept.getInputStream();
//4.把字节输入流包装成缓冲字符输入流进行消息的接收
BufferedReader br = new BufferedReader(new InputStreamReader(is));
//5.按照行读取消息
String msg ;
if ((msg = br.readLine()) != null){
System.out.println(msg);
}
}
}
多发多收:
如何实现多发多收?
- 客户端使用循环反复地发送消息
- 服务端使用循环反复的接收消息
服务端为什么不可以同时接收多个客户端的消息?
- 目前服务端是单线程的,每次只能处理一个客户端的消息
服务端代码
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
/**
* 开发socket网络编程入门代码的服务端 实现接收消息
*/
public class ServerSocketDemo {
public static void main(String[] args) throws Exception {
System.out.println("==========服务端启动成功=========");
//1.注册接收端对象
ServerSocket socket = new ServerSocket(6666);
//2.调用accept方法:等待接收客户端的Socket连接请求,家里Socket通信管道
Socket accept = socket.accept();
//3.从socket通信管道中得到一个字节输入流
InputStream is = accept.getInputStream();
//4.把字节输入流包装成缓冲字符输入流进行消息的接收
BufferedReader br = new BufferedReader(new InputStreamReader(is));
//5.按照行读取消息
String msg ;
while ((msg = br.readLine()) != null){
System.out.println(msg);
}
}
}
客户端代码:
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;
/**
* 完成Socket网络编程入门案例的客户端开发 多发多收
*/
public class SocketClientDemo {
public static void main(String[] args) throws Exception {
System.out.println("==========客户端启动成功=========");
//1.创建Socket通信管道请求服务端连接
//public Socket(String host, int port)
// 参数一:服务器的IP地址
// 参数二:服务端端口
Socket socket = new Socket("127.0.1",6666);
//2.从socket通信管道中得到一个字节输出流
OutputStream os = socket.getOutputStream();
//3.把低级的字节流包装成打印流
PrintStream ps = new PrintStream(os);
Scanner sc = new Scanner(System.in);
while (true) {
System.out.println("请说");
String s = sc.nextLine();
//4.发送消息
ps.println(s);
ps.flush();
}
}
}
通过引入多线程让服务端可以处理多个客户端的通信请求:
创建线程类,并重写run方法:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Socket;
public class ServerReaderThread extends Thread{
private Socket socket;
public ServerReaderThread(Socket socket){
this.socket = socket;
}
@Override
public void run() {
try {
//1.从socket通信管道中得到一个字节输入流
InputStream is = socket.getInputStream();
//2.把字节输入流包装成缓冲字符输入流进行消息的接收
BufferedReader br = new BufferedReader(new InputStreamReader(is));
//3.按照行读取消息
String msg ;
while ((msg = br.readLine()) != null){
System.out.println(msg);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
服务端代码:
import java.net.ServerSocket;
import java.net.Socket;
/**
* 开发socket网络编程入门代码的服务端 实现接收消息
*/
public class ServerSocketDemo {
public static void main(String[] args) throws Exception {
System.out.println("==========服务端启动成功=========");
//1.注册接收端对象
ServerSocket serverSocket = new ServerSocket(6666);
// 定义一个死循环由主线程负责不断的接收客户端的Socket管道连接
while (true) {
//2.每接收到一个死循环有主线程负责不断的接收客户,交给一个独立的子线程
Socket socket = serverSocket.accept();
//3,开始创建独立线程处理socket
new ServerReaderThread(socket).start();
}
}
}
客户端代码:
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;
/**
* 完成Socket网络编程入门案例的客户端开发 多发多收
*/
public class SocketClientDemo {
public static void main(String[] args) throws Exception {
System.out.println("==========客户端启动成功=========");
//1.创建Socket通信管道请求服务端连接
//public Socket(String host, int port)
// 参数一:服务器的IP地址
// 参数二:服务端端口
Socket socket = new Socket("127.0.1",6666);
//2.从socket通信管道中得到一个字节输出流
OutputStream os = socket.getOutputStream();
//3.把低级的字节流包装成打印流
PrintStream ps = new PrintStream(os);
Scanner sc = new Scanner(System.in);
while (true) {
System.out.println("请说");
String s = sc.nextLine();
//4.发送消息
ps.println(s);
ps.flush();
}
}
}