目录
1.什么是网络编程?
网络编程是可以让设备中的程序与网络上其他设备中的程序进行数据交互的技术
2.基本的通信架构
CS架构:Client客户端/Server服务端
BS架构:Browser浏览器/Server服务端
无论是CS架构,还是BS架构的软件都必须依赖网络编程
3.网络通信三要素
1.IP地址
IP(Internet Protocol):全称"互联网协议地址",是分配给上网设备的唯一标识,可以用来定位网络上的设备。
IP地址有两种形式:
IPv4:
32位,4个字节
点分十进制: 192.168.1.66
IPv6:
128位
冒分十六进制: 2001:0db8:0000:0023:0008:0800:200c:417a
IP域名: 网址
公网IP, 内网IP:
公网IP:是可以连接互联网的IP地址;内网IP:也叫局域网IP,只能组织机构内部使用。
内网IP:192.168. 开头的就是常见的局域网地址,范围即为192.168.0.0--192.168.255.255,专门为组织机构内部使用。
**特殊IP地址:**127.0.0.1、localhost:代表本机IP,只会寻找当前所在的主机。
IP常用命令:
ipconfig :查看本机IP地址
ping IP地址:检查网络是否连通
Java中IP地址的表示:
InetAddress
java
package com.itheima.day13.teacher.demo02_ip;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
/**
* IP:用于在一个网络里,找到某个计算机设备。一般使用IPv4,比如:192.168.29.33
* 域名:因为IP比较难记,所以有了域名系统,更方便记忆
* IP和域名的关系,类似于 手机通讯录里 手机号和姓名备注的关系
* 百度的ip:110.242.68.4
* 百度的域名:www.baidu.com
* IP地址对象:InetAddress
*
*/
public class DemoInetAddress {
public static void main(String[] args) throws IOException {
//获取本机ip信息
InetAddress local = InetAddress.getLocalHost();
String localIp = local.getHostAddress();
System.out.println("本机ip = " + localIp);
String localName = local.getHostName();
System.out.println("本机名 = " + localName);
//获取指定ip或指定域名的信息
// InetAddress baidu = InetAddress.getByName("www.baidu.com");
InetAddress baidu = InetAddress.getByName("110.242.68.4");
String baiduName = baidu.getHostName();
System.out.println("baiduName = " + baiduName);
String baiduIp = baidu.getHostAddress();
System.out.println("baiduIp = " + baiduIp);
//测试指定域名的网络是否畅通:200ms之内能否ping通百度
boolean reachable = baidu.isReachable(200);
System.out.println("reachable = " + reachable);
}
}
2.端口号
标记正在计算机设备上运行的应用程序的,被规定为一个 16 位的二进制,范围是 0~65535
分类:
周知端口:0~1023,被预先定义的知名应用占用(如:HTTP占用 80,FTP占用21)
注册端口:1024~49151,分配给用户进程或某些应用程序
动态端口:49152到65535,之所以称为动态端口,是因为它 一般不固定分配某种进程,而是动态分配
注意:我们自己开发的程序一般选择使用注册端口,且一个设备中不能出现两个程序的端口号一样,否则出错。
3.协议
1**.**网络上通信的设备,事先规定的连接规则,以及传输数据的规则被称为网络通信协议。
2.开放式网络互联标准
OSI网络参考模型:全球网络互联标准
TCP/IP网络模型:事实上的国际标准
3.传输层的2个通信协议
UDP:用户数据报协议
特点:
无连接、不可靠通信,通信效率高
不事先建立连接,数据按照包发,一包数据包含:自己的IP、程序端口,目的地IP、程序端口和数据(限制在64KB内)等。
发送方不管对方是否在线,数据在中间丢失也不管,如果接收方收到数据也不返回确认,故是不可靠的 。
应用场景:语音通话、视频直播
TCP:传输控制协议
特点:
面向连接、可靠通信
要保证在不可靠的信道上实现可靠的传输
TCP主要有三个步骤实现可靠传输:
三次握手建立可靠连接:目的确定通信双方,收发消息都是正常无问题的!(全双工)
传输数据进行确认:目的保证传输数据的可靠性
四次挥手断开连接:目的确保双方数据的收发都已经完成!
应用场景:网页、文件下载、支付
4.UDP通信-快速入门
Java提供了一个java.net.DatagramSocket类来实现UDP通信。
构造器、方法:
客户端实现步骤:
创建DatagramSocket对象(客户端对象)
创建DatagramPacket对象封装需要发送的数据(数据包对象)
使用DatagramSocket对象的send方法,传入DatagramPacket对象
释放资源
服务端实现步骤
创建DatagramSocket对象并指定端口(服务端对象)
创建DatagramPacket对象接收数据(数据包对象)
使用DatagramSocket对象的receive方法,传入DatagramPacket对象
释放资源
java
package com.itheima.day13.teacher.demo01_udp;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
/**
*/
public class Sender {
public static void main(String[] args) throws Exception {
//1. 创建DatagramSocket对象:因为它提供了收发udp数据包的方法
DatagramSocket ds = new DatagramSocket();
//2. 准备数据包
byte[] data = "hello".getBytes();
DatagramPacket packet =
new DatagramPacket(data, data.length, InetAddress.getLocalHost(), 7788);
//3. 把数据包里的数据发送出去
ds.send(packet);
//4. 释放资源
ds.close();
}
}
----------------
package com.itheima.day13.teacher.demo01_udp;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
/**
*/
public class Receiver {
public static void main(String[] args) throws Exception {
//1. 创建DatagramSocket对象,并监听7788端口
DatagramSocket ds = new DatagramSocket(7788);
//2. 准备数据包。用于存储 接收到的数据。UDP的数据包64K
byte[] buffer = new byte[1024 * 64];
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
//3. 接收数据。接收到的数据会被放到packet对象里
ds.receive(packet);
//4. 把接收到的数据打印出来
String str =
new String(packet.getData(), packet.getOffset(), packet.getLength());
System.out.println("str = " + str);
//5. 释放资源
ds.close();
}
}
5.UDP通信-多发多收
客户端实现步骤:
创建DatagramSocket对象(发送端对象)
使用while死循环不断的接收用户的数据输入,如果用户输入的exit则退出程序
如果用户输入的不是exit, 把数据封装成DatagramPacket
使用DatagramSocket对象的send方法将数据包对象进行发送
释放资源
服务端实现步骤:
创建DatagramSocket对象并指定端口(接收端对象)
创建DatagramPacket对象接收数据(数据包对象)
使用DatagramSocket对象的receive方法传入DatagramPacket对象
使用while死循环不断的进行第3步
java
package com.itheima.day13.teacher.demo03_udp;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner;
/**
* 1. 发送的消息内容,由用户进行输入:用Scanner
* Scanner scanner = new Scanner(System.in);
* String line = scanner.nextLine();
* 2. 可以多次发送消息,进行持续通信
* 发送者持续多次发送
* 如果用户输入的是"byebye",就结束循环
*/
public class Sender {
public static void main(String[] args) throws Exception {
//1. 创建DatagramSocket对象:因为它提供了收发udp数据包的方法
DatagramSocket ds = new DatagramSocket();
//2. 准备一个扫描器,用于读取用户的输入
Scanner scanner = new Scanner(System.in);
//3. 持续通信:不断读取用户的输入,把内容发送出去
while (true) {
//读取用户输入的内容
String line = scanner.nextLine();
//封装到数据包里
byte[] data = line.getBytes();
DatagramPacket packet =
new DatagramPacket(data, data.length, InetAddress.getLocalHost(), 7788);
//把数据包里的数据发送出去
ds.send(packet);
//如果用户输入的是byebye,就结束循环
if ("byebye".equals(line)) {
break;
}
}
//4. 释放资源
ds.close();
}
}
---------------------
package com.itheima.day13.teacher.demo03_udp;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
/**
* @author liuyp
* @since 2024/02/28
*/
public class Receiver {
public static void main(String[] args) throws Exception {
//1. 创建DatagramSocket对象,并监听7788端口
DatagramSocket ds = new DatagramSocket(7788);
//2. 准备数据包。用于存储 接收到的数据。UDP的数据包64K
byte[] buffer = new byte[1024 * 64];
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
//3. 持续通信:不断的接收数据并打印出来
while (true) {
//4. 接收数据。接收到的数据会被放到packet对象里
ds.receive(packet);
//5. 把接收到的数据打印出来
String str =
new String(packet.getData(), packet.getOffset(), packet.getLength());
System.out.println(packet.getSocketAddress() +"发送了: " + str);
}
//5. 释放资源
// ds.close();
}
}
6.TCP通信-快速入门
1.Java提供了一个java.net.Socket类来实现TCP通信。
2.客户端实现步骤
创建客户端的Socket对象,请求与服务端的连接。
使用socket对象调用getOutputStream()方法得到字节输出流。
使用字节输出流完成数据的发送。
释放资源:关闭socket管道。
3.服务端实现步骤
创建ServerSocket对象,注册服务端端口。
调用ServerSocket对象的accept()方法,等待客户端的连接,并得到Socket管道对象。
通过Socket对象调用getInputStream()方法得到字节输入流、完成数据的接收。
释放资源:关闭socket管道
java
package com.itheima.day13.teacher.demo04_tcp;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
/**
*/
public class Client{
public static void main(String[] args) throws IOException {
//1. 使用TCP连接 本机的8888端口:创建Socket对象
Socket socket = new Socket("127.0.0.1", 8888);
//2. 通过Socket,把数据发出去到服务端
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
dos.writeUTF("hello");
//3. 通过Socket,接收服务端返回的数据
DataInputStream dis = new DataInputStream(socket.getInputStream());
String answer = dis.readUTF();
System.out.println("收到服务端返回结果:" + answer);
//4. 释放资源
dis.close();
dos.close();
socket.close();
}
}
----------------------
package com.itheima.day13.teacher.demo04_tcp;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
/**
* @author liuyp
* @since 2024/02/28
*/
public class Server {
public static void main(String[] args) throws IOException {
//1. 监听8888端口:创建ServerSocket
ServerSocket serverSocket = new ServerSocket(8888);
//2. 获取客户端的连接:Socket对象。是阻塞方法。如果没有客户端连接进来,就一直阻塞等待
Socket socket = serverSocket.accept();
//3. 通过Socket,接收客户端发过来的数据
DataInputStream dis = new DataInputStream(socket.getInputStream());
String data = dis.readUTF();
System.out.println("接收到客户端发来的数据:" + data);
//4. 通过Socket,给客户端返回数据
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
dos.writeUTF("hi");
//5. 释放资源
dos.close();
dis.close();
socket.close();
serverSocket.close();
}
}
7. TCP通信-多发多收
客户端使用死循环,让用户不断输入消息。
服务端也使用死循环,控制服务端收完消息,继续等待接收下一个消息。
java
package com.itheima.day13.teacher.demo05_tcp;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.util.Scanner;
/**
* 1. 客户端给服务端发送的内容:由用户输入
* 2. 实现客户端和服务端的持续通信
*/
public class Client{
public static void main(String[] args) throws IOException {
//1. 使用TCP连接 本机的8888端口:创建Socket对象
Socket socket = new Socket("127.0.0.1", 8888);
Scanner scanner = new Scanner(System.in);
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
DataInputStream dis = new DataInputStream(socket.getInputStream());
while (true) {
//2. 通过Socket,把数据发出去到服务端:使用Scanner读取用户输入的内容,把内容发出去
String line = scanner.nextLine();
dos.writeUTF(line);
//3. 通过Socket,接收服务端返回的数据
String answer = dis.readUTF();
System.out.println("收到服务端返回结果:" + answer);
//如果用户输入的内容是byebye,就结束
if ("byebye".equals(line)) {
break;
}
}
//4. 释放资源
dis.close();
dos.close();
socket.close();
}
}
-----------------
package com.itheima.day13.teacher.demo05_tcp;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
/**
* @author liuyp
* @since 2024/02/28
*/
public class Server {
public static void main(String[] args) throws IOException {
//1. 监听8888端口:创建ServerSocket
ServerSocket serverSocket = new ServerSocket(8888);
//2. 获取客户端的连接:Socket对象。是阻塞方法。如果没有客户端连接进来,就一直阻塞等待
Socket socket = serverSocket.accept();
DataInputStream dis = new DataInputStream(socket.getInputStream());
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
while (true) {
//3. 通过Socket,接收客户端发过来的数据
String data = dis.readUTF();
System.out.println("接收到客户端发来的数据:" + data);
//4. 通过Socket,给客户端返回数据
dos.writeUTF("hi");
if ("byebye".equals(data)) {
break;
}
}
//5. 释放资源
dos.close();
dis.close();
socket.close();
serverSocket.close();
}
}
**8.**TCP通信-同时接收多个客户端
主线程定义了循环负责接收客户端Socket管道连接
每接收到一个Socket通信管道后分配一个独立的线程负责处理它。
java
package com.itheima.day13.teacher.demo06_tcp;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
/**
*/
public class ServerWorkerRunnable implements Runnable{
private Socket socket;
public ServerWorkerRunnable(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
DataInputStream dis = new DataInputStream(socket.getInputStream());
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
while (true) {
//通过Socket,接收客户端发过来的数据。也是阻塞方法,即:如果当前socket里没有数据可读取,就阻塞
String data = dis.readUTF();
System.out.println("接收到客户端发来的数据:" + data);
//通过Socket,给客户端返回数据
dos.writeUTF("hi");
if ("byebye".equals(data)) {
break;
}
}
//释放资源
dos.close();
dis.close();
socket.close();
} catch (IOException e) {
System.out.println("连接已断开");
}
}
}
--------------------
package com.itheima.day13.teacher.demo06_tcp;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
/**
*/
public class Server {
public static void main(String[] args) throws IOException {
//1. 监听8888端口:创建ServerSocket
ServerSocket serverSocket = new ServerSocket(8888);
//2. 死循环:不断尝试接受客户端的连接。有几个客户端连接进来,就接受几个连接
while (true) {
//3. 获取客户端的连接:Socket对象。是阻塞方法。如果没有客户端连接进来,就一直阻塞等待
Socket socket = serverSocket.accept();
//4. 创建一个线程,由这个新线程专门负责它的通信。线程本身是异步的
new Thread(new ServerWorkerRunnable(socket)).start();
}
// serverSocket.close();
}
}
----------------
package com.itheima.day13.teacher.demo06_tcp;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.util.Scanner;
/**
* 1. 客户端给服务端发送的内容:由用户输入
* 2. 实现客户端和服务端的持续通信
*/
public class Client{
public static void main(String[] args) throws IOException {
//1. 使用TCP连接 本机的8888端口:创建Socket对象
Socket socket = new Socket("127.0.0.1", 8888);
Scanner scanner = new Scanner(System.in);
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
DataInputStream dis = new DataInputStream(socket.getInputStream());
while (true) {
//2. 通过Socket,把数据发出去到服务端:使用Scanner读取用户输入的内容,把内容发出去
String line = scanner.nextLine();
dos.writeUTF(line);
//3. 通过Socket,接收服务端返回的数据
String answer = dis.readUTF();
System.out.println("收到服务端返回结果:" + answer);
//如果用户输入的内容是byebye,就结束
if ("byebye".equals(line)) {
break;
}
}
//4. 释放资源
dis.close();
dos.close();
socket.close();
}
}
9.TCP通信-综合案例
1.即时通信-群聊
是指一个客户端把消息发出去,其他在线的全部客户端都可以收到消息。
需要用到端口转发的设计思想。
服务端需要把在线的Socket管道存储起来,一旦收到一个消息要推送给其他管道。
2**.** 实现一个简易版的BS架构
BS架构的基本原理
java
package com.itheima.day13.teacher.demo07_tcp_tomcat;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
/**
*/
public class TomcatWorkerRunnable implements Runnable {
private Socket socket;
public TomcatWorkerRunnable(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
// while (true) {
try {
//如果想要接收客户端提交过来的数据
// InputStream is = socket.getInputStream();
// is.read();
//直接给客户端返回结果
OutputStream os = socket.getOutputStream();
os.write("HTTP/1.1 200\r\n".getBytes());
os.write("Content-Type: text/html;charset=UTF-8\r\n".getBytes());
os.write("\r\n".getBytes());
os.write("<h1>Hello World!!!</h1>".getBytes());
os.write("<a href='http://www.baidu.com'>百度</a>".getBytes());
//必须给浏览器返回一个结束标志,否则浏览器会一直转圈
socket.shutdownOutput();
} catch (IOException e) {
System.out.println("断开连接");
}
// }
}
}
----------------------
package com.itheima.day13.teacher.demo07_tcp_tomcat;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
/**
*/
public class TomcatServer {
public static void main(String[] args) throws IOException {
//1. 监听8080端口。浏览器访问地址的格式 http://ip地址:8080
ServerSocket serverSocket = new ServerSocket(8080);
//2. 死循环
while (true) {
//3. 不断尝试接受客户端的连接,得到Socket
Socket socket = serverSocket.accept();
//4. 把socket交给一个新线程进行处理
new Thread(new TomcatWorkerRunnable(socket)).start();
}
}
}