什么是网络编程?
可以让设备中的程序与网络上其他设备中的程序进行数据交互(实现网络通信的)
基本的通信架构
基本的通信架构有2种形式:
CS架构(Client客户端/Server服务端)
BS架构(Browser浏览器/Server服务端)。
网络通信三要素
IP 01 | 端口 02 | 协议 03 |
设备在网路中的地址,是唯一的标识 | 应用程序在设备中唯一的标识 | 链接和数据在网络中传输的规则 |
InetAddress
代表IP地址
名称 | 说明 |
getLocalHost() | 获取本机IP,会以一个inetAddress的对象返回 |
getByName(String host) | 根据ip地址或者域名,返回一个inetAdress对象 |
getHostName() | 获取该ip地址对象对应的主机名。 |
getHostAddress() | 获取该ip地址对象中的ip地址信息。 |
isReachable(int timeout) | 在指定素秒内,判断主机与该ip对应的主机是否能连通 |
端口
标记正在计算机设备上运行的应用程序的,被规定为一个16位的二进制,范围是0~65535.
分类
周知端口:0~1023,被预先定义的知名应用占用(如:HTTP占用80,FTP占用21)
注册端口:1024~49151,分配给用户进程或某些应用程序
动态端口:49152到65535,之所以称为动态端口,是因为它一般不固定分配某种进程,而是动态分配
注意:我们自己开发的程序一般选择使用注册端口,且一个设备中不能出现两个程序的端口号一样,否则出错。
开放式网络互联标准:OSI网络参考模型
OSI网络参考模型:全球网络互联标准
TCP/IP网络模型:事实上的国际标准
OSI网络参考模型 | TCP/IP网络模型 | 各层对应 | 面向操作 |
应用层 | 应用层 | HTTP、FTP、SMTP | 应用程序需要关注的:浏览器、邮箱、程序员一般在这一层开发 |
表示层 | |||
会话层 | |||
传输层 | 传输层 | UDP、TCP... | 选择使用的TCP,UDP协议 |
网络层 | 网络层 | IP... | 封装源和目标IP |
数据链路层 | 数据链路层+物理层 | 比特流... | 物理设备中传输 |
物理层 |
传输层的2个通信协议
UDP(User Datagram Protocol):用户数据报协议;TCP(Transmission ControlProtocol):传输控制协议。
UDP协议
- 特点:无连接、不可靠通信。 通信效率高!语音通话视频直播
- 不事先建立连接,数据按照包发,一包数据包含:自己的IP、程序端口,目的地IP、程序端口和数据(限制在64KB内)等。
- 发送方不管对方是否在线,数据在中间丢失也不管,如果接收方收到数据也不返回确认,故是不可靠的。
TCP协议
- 特点:面向连接、可靠通信,
- TCP的最终目的:要保证在不可靠的信道上实现可靠的传输。
- TCP主要有三个步骤实现可靠传输:三次握手建立连接,传输数据进行确认,四次挥手断开连接。
UDP通信
- 特点:无连接、不可靠通信
- 事先不建立连接;发送端每次把要发送的数据(限制在64KB内)、接收端IP等信息封装成一个数据包,发出去就不管了。
- Java提供一个java.net.DatagramSocket类来实现UDP通信。
DatagramSocket:用于创建客户端、服务端
DatagramPacket:创建数据包
客户端实现步骤
- 创建DatagramSocket对象(客户端对象) ——> 扔韭菜的人
- 创建DatagramPacket对象封装需要发送的数据(数据包对象)——> 韭菜盘子
- 使用DatagramSocket对象的send方法,传入DatagramPacket对象 ——> 开始抛出韭菜
- 释放资源
public class Client {
public static void main(String[] args) throws Exception {
// 创建客户端对象
DatagramSocket socket = new DatagramSocket(7777);
// 创建数据包对象
//public DatagramPacket(byte buf[], int offset, int length,
// InetAddress address, int port)
// 参数一:封装发出去的数据
// 参数二: 发送出去的数据大小(字节个数)
// 参数三:服务端的IP地址
// 参数四:服务器程序的端口
byte[] bytes = "我是快乐的客户端,我爱你abc".getBytes();
DatagramPacket packet = new DatagramPacket(bytes, bytes.length, InetAddress.getLocalHost(),
6666);
// 开始正式发送这个数据报的数据出去
socket.send(packet);
System.out.println("客户端数据发送完成~~");
// 释放资源
socket.close();
}
}
服务端实现步骤
- 创建DatagramSocket对象并指定端口(服务端对象) ——> 接韭菜的人
- 创建DatagramPacket对象接收数据(数据包对象) ——> 韭菜盘子
- 使用DatagramSocket对象的receive方法,传入DatagramPacket对象 ——> 开始接收韭菜
- 释放资源
public class Server {
public static void main(String[] args) throws Exception {
System.out.println("服务端启动~~");
// 创建一个服务器对象
DatagramSocket socket = new DatagramSocket(6666);
// 创建一个数据包对象,用于接收数据的
byte[] buffer = new byte[1024 * 64];
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
// 开始使用数据包来接收客户端发来的数据
socket.receive(packet);
//从字节数组中,把接收到的数据直接打印出来
int len = packet.getLength();
String rs = new String(buffer, 0, len);
System.out.println(rs);
System.out.println(packet.getAddress().getHostAddress());
System.out.println(packet.getPort());
socket.close();
}
}
TCP通信
- 特点:面向链接、可靠通信
- 通信双方事先采用“三次握手”方式建立可靠连接,实现端到端的通信,底层能保证数据成功传给服务器。
- Java提供一个java.net.Socket类来实现TCP通信
TCP通信 ——客户端开发
客户端程序就是通过java.net包下的Socket类实现的
构造器 | 说明 |
public Socket(String host, int port) | 根据指定的服务器ip、端口号请求与服务端建立连接,连接通过,就获得了客户端socket |
方法 | 说明 |
public outputStream getOutputStream() | 获取字节输出流对象 |
public InputStream getInputStream() | 获得字节输入流对象 |
public class Client {
public static void main(String[] args) throws Exception {
// 创建Socket对象,并同时请求与服务程序的连接
Socket socket = new Socket("127.0.0.1", 8888);
// 从socket通信管道中得到一个字节输出流,用来发送数据给服务器
OutputStream os = socket.getOutputStream();
// 把低级的字节输出流包装成数据输出流
DataOutputStream dos = new DataOutputStream(os);
// 开始写数据
dos.writeUTF("约会吗?");
dos.close();
socket.close();
}
}
客户端实现步骤
- 创建客户端的Socket对象,请求与服务端的连接
- 使用socket对象调用getOutputStream()方法得到字节输出流
- 使用字节输出流完成数据的发送
- 释放资源:关闭管道close
TCP通信——服务端程序的开发
服务端是通过java.net包下的ServerSocket类来实现的
ServerSocket
构造器 | 说明 |
public ServerSocket(int port) | 为服务端程序注册端口 |
方法 | 说明 |
public Socket accept() | 阻塞等待客户端的连接请求,一旦与某个客户端成功连接,则返回服务端这边的Socket对象 |
public class Server {
public static void main(String[] args) throws Exception {
System.out.println("服务器启动~~");
// 创建ServerSocket对象 接收客户端数据
ServerSocket serverSocket = new ServerSocket(8888);
// ServerSocket对象accept方法 等待客户端发起请求 也是socket
Socket socket = serverSocket.accept();
// 从Socket管道中得到一个字节流输入
InputStream is = socket.getInputStream();
DataInputStream dis = new DataInputStream(is);
String str = dis.readUTF();
System.out.println(str);
// 获取IP地址
System.out.println(socket.getRemoteSocketAddress());
dis.close();
serverSocket.close();
}
}
服务端实现步骤
- 创建ServerSocket对象,注册服务端端口(与客户端一致)
- 调用ServerSocket对象的accept()方法,等待客户端的连接,并得到Socket管道对象
- 通过Socket对象调用getInputStream()方法得到字节输入流,完成数据的接收
- 释放资源:关闭socket管道
使用TCP通信实现:多发多收消息
客户端使用死循环,让用户不断输入消息
服务端也使用死循环,控制服务端收完消息,继续等待下一个消息
public class Client {
public static void main(String[] args) throws Exception {
// 创建Socket对象,并同时请求与服务程序的连接
Socket socket = new Socket("127.0.0.1", 8888);
// 从socket通信管道中得到一个字节输出流,用来发送数据给服务器
OutputStream os = socket.getOutputStream();
// 把低级的字节输出流包装成数据输出流
DataOutputStream dos = new DataOutputStream(os);
Scanner sc = new Scanner(System.in);
while (true) {
System.out.println("请说:");
String str = sc.next();
// 开始写数据
if("exit".equals(str)){
System.out.println("欢迎下次再来,退出成功~");
dos.close();
socket.close();
break;
}
dos.writeUTF(str);
}
}
}
public class Server {
public static void main(String[] args) throws Exception {
System.out.println("服务器启动~~");
// 创建ServerSocket对象 接收客户端数据
ServerSocket serverSocket = new ServerSocket(8888);
while (true) {
// ServerSocket对象accept方法 等待客户端发起请求 也是socket
Socket socket = serverSocket.accept();
// 从Socket管道中得到一个字节流输入
InputStream is = socket.getInputStream();
DataInputStream dis = new DataInputStream(is);
try {
String str = dis.readUTF();
System.out.println(str);
} catch (Exception e) {
System.out.println(socket.getRemoteSocketAddress() + "离线了");
dis.close();
serverSocket.close();
break;
}
//System.out.println(socket.getRemoteSocketAddress());
}
}
}
TCP服务端与多个客户端通信(启动多线程)
public class Client {
public static void main(String[] args) throws Exception {
// 创建Socket对象,并同时请求与服务程序的连接
Socket socket = new Socket("127.0.0.1", 8888);
// 从socket通信管道中得到一个字节输出流,用来发送数据给服务器
OutputStream os = socket.getOutputStream();
// 把低级的字节输出流包装成数据输出流
DataOutputStream dos = new DataOutputStream(os);
Scanner sc = new Scanner(System.in);
while (true) {
System.out.println("请说:");
String str = sc.next();
// 开始写数据
if("exit".equals(str)){
System.out.println("欢迎下次再来,退出成功~");
dos.close();
socket.close();
break;
}
dos.writeUTF(str);
}
}
}
public class Server {
public static void main(String[] args) throws Exception {
System.out.println("服务器启动~~");
// 创建ServerSocket对象 接收客户端数据
ServerSocket serverSocket = new ServerSocket(8888);
while (true) {
// ServerSocket对象accept方法 等待客户端发起请求 也是socket
Socket socket = serverSocket.accept();
// 创建多线程处理
new ServerReaderThread(socket).start();
}
}
}
public class ServerReaderThread extends Thread {
private final Socket socket;
public ServerReaderThread(Socket socket){
this.socket = socket;
}
@Override
public void run() {
try {
String name = Thread.currentThread().getName();
InputStream is = socket.getInputStream();
DataInputStream dis = new DataInputStream(is);
while (true) {
try {
String str = dis.readUTF();
System.out.println(str);
} catch (Exception e) {
System.out.println(socket.getRemoteSocketAddress() + "下线了");
socket.close();
dis.close();
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}