网络编程
基本概念
IP地址
IP地址(Internet Protocol)
唯一标识网络上的每一台计算机
IP地址的组成
32位,由4个8位二进制数组成
----
IP地 址 = 网络地址 +主机地址
网络地址:标识计算机或网络设备所在的网段
主机地址:标识特定主机或网络设备
端口号
端口号标识正在计算机上运行的进程(程序)
不同的进程有不同的端口号,一个 16 位的整数 0~65535。
DNS
DNS:Domain Name System,域名系统
是一个分布式的数据库 负责将输入的域名与对应的IP地址进行匹配 解析
网络模型
网络模型属于使用网络进行数据传输的各个环节统 称
网络模型的存在是为了规范数据传输的各个细节 保证数据的安全性
七层模型太过了理想化 未被实现 真正落地实现的 就是 五层模型
网络套接字
端口号与IP地址的组合得出一个网络套接字:Socket。
网络编程就是通过Socket发送和接收数据的。
Socket概念
通信链路的端点就称为“套接字”(Socket),是提供给应用程序的接口。
Socket的底层机制复杂,Java平台提供了一些简单的API,无序了解底层机制(网络)。
Socket类
用于客户端使用的;
构造方法
Socket(InetAddress host, int port)
作用:创建套接字并将其连接到指定的本地端口,本地地址。
参数:String类型的IP地址,int类型端口号
返回值:创建一个套接字 socket
实例:
// 创建客户端Socket对象 指定主机地址为本机 端口号为8899
Socket socket = new Socket("127.0.0.1",8899);
方法
getOutputStream()
作用:创建一个输出流,用于写数据,发送数据。
参数:无
返回值:OutputStream流对象
实例:
ServerSocket server = new ServerSocket(8899);
Socket socket = server.accept();
OutputStream outputStream = socket.getOutputStream();
shutdownOutput()
作用:关闭输出流。
参数:无
返回值:无
实例
ServerSocket server = new ServerSocket(8899);
Socket socket = server.accept();
socket.shutdownOutput();
getInputStream()
作用:创建一个输入流,用于读数据,读取数据。
参数:无
返回值:InputStream流对象
实例:
ServerSocket server = new ServerSocket(8899);
Socket socket = server.accept();
InputStream inputStream = socket.getInputStream();
getInetAddress()
作用:返回套接字所连接的地址。
参数:无
返回值:InetAddress类型对象
实例:
InetAddress inetAddress = socket.getInetAddress()
// 获取主机名称
String name = inetAddress.getHostName()
ServerSocket类
用于服务端使用;
构造方法
ServerSocket(int port)
作用:创建绑定到指定端口的服务器套接字
参数:int类型端口号
返回值:ServerSocket类型对象 server,创建绑定到指定端口的服务器套接字;
实例:
ServerSocket server = new ServerSocket(9090);
Socket socket = server.accept();
方法
accept()
作用:侦听要连接到此套接字并接受它
细节:等待客户端连接后,后续代码才会执行。
参数:无
返回值:Socket对象,套接字socket
实例:
ServerSocket server = new ServerSocket(8899);
Socket socket = server.accept();
DatagramPacket类
数据报包用于实现无连接分组传送服务;UDP传输数据的包对象;
构造方法
DatagramPacket(byte[] buf, int length)
作用:构造一个 DatagramPacket用于接收长度的数据包
参数:byte类型数组,长度
返回值:Socket类型
实例:
// 准备数据
String msg = "你好,服务器";
byte[] sendData = msg.getBytes();
// 准备数据包
DatagramPacket packet = new DatagramPacket(sendData,sendData.length);
DatagramPacket(byte[] buf, int length, InetAddress address, int port)
作用:构造用于发送长度的分组的数据报包 length指定主机上到指定的端口号
参数:byte类型数组,长度,地址对象,端口号
返回值:Socket类型
实例:
// 准备数据
String msg = "你好,服务器";
byte[] sendData = msg.getBytes();
// 获取地址对象 使用InetAddress类静态方法
InetAddress localhost = InetAddress.getByName("127.0.0.1");
// 准备数据包
DatagramPacket packet = new DatagramPacket(sendData,sendData.length,localhost,12455);
方法
getAddress()
作用:返回该数据报发送或接收数据报的计算机的IP地址
参数:无
返回值:InetAddress,IP地址信息对象
实例:
// 创建UDPsocket
DatagramSocket socket = new DatagramSocket(12455);
// 创建用于存放数据的空间
byte[] receiveData = new byte[36];
// 创建UDP包对象
DatagramPacket receivePacket = new DatagramPacket(receiveData,receiveData.length);
// 接收数据,并把数据存放在包对象中
socket.receive(receivePacket);
// 包对象中获取IP地址信息
// 获取客户端的IP信息
InetAddress clientAddress = receivePacket.getAddress();
// 获取端口号
int clientPort = receivePacket.getPort();
getSocketAddress()
作用:获取该数据包发送到或正在从其发送的远程主机的SocketAddress
参数:无
返回值:Socket类型
实例:
getData()
作用:获取数据包中的数据
参数:无
返回值:byte[]
实例:
// 获取地址对象 使用InetAddress类静态方法
InetAddress localhost = InetAddress.getByName("127.0.0.1");
// 准备数据包
DatagramPacket packet = new DatagramPacket(sendData,sendData.length,localhost,12455);
// 准备DatagramPacket对象
DatagramSocket socket = new DatagramSocket();
// 发送数据
socket.send(packet);
// 客户端接收数据
// 准备数组接收数据
byte[] receiveData = new byte[36];
// 准备数据包对象
DatagramPacket receivePacket = new DatagramPacket(receiveData,receiveData.length);
// 接收数据
socket.receive(receivePacket);
// 调用方法获取 获取接收到的数据
byte[] data = receivePacket.getData();
DatagramSocket类
用于UDP创建Socket对象
构造方法
DatagramSocket()
作用:构造数据报套接字并将其绑定到本地主机上的任何可用端口
参数:无
返回值:DatagramSocket类型对象
实例:
// 创建UDPsocket
DatagramSocket socket = new DatagramSocket();
DatagramSocket(int port, InetAddress laddr)
作用:创建一个数据报套接字,绑定到指定的本地地址
参数:端口号,地址
返回值:DatagramSocket类型对象
实例:
// 获取地址对象 使用InetAddress类静态方法
InetAddress localhost = InetAddress.getByName("127.0.0.1");
// 创建UDPsocket
DatagramSocket socket = new DatagramSocket(12455,localhost);
DatagramSocket(int port)
作用:构造数据报套接字并将其绑定到本地主机上的指定端口
参数:端口号
返回值:DatagramSocket类型对象
实例:
// 创建UDPsocket
DatagramSocket socket = new DatagramSocket(12455);
方法
receive(DatagramPacket p)
作用:从此套接字接收数据报包
参数 :DatagramPacket类型对象
返回值:空
实例:
DatagramSocket socket = new DatagramSocket(12455);
System.out.println("服务已启动");
// 准备数组接收数据
byte[] receiveData = new byte[36];
// 准备数据包对象
DatagramPacket receivePacket = new DatagramPacket(receiveData,receiveData.length);
// 接收数据
socket.receive(receivePacket);
// 调用方法获取 获取接收到的数据
byte[] data = receivePacket.getData();
// 解析数据
String msg = new String(data,0,data.length);
System.out.println(msg);
send()
作用:从此套接字发送数据报包
参数:DatagramPacket p
返回值:空
实例:
// 创建Socket对象
DatagramSocket socket = new DatagramSocket(12455);
// 发送数据
byte[] sendData = "你好,客户端".getBytes();
// 准备数据包
DatagramPacket packet = new DatagramPacket(sendData, sendData.length,clientAddress,clientPort);
socket.send(packet);
InetAddress类
表示IP协议地址
构造方法
无
方法
getByAddress(byte[] addr)(静态方法)
作用:从此套接字接收数据报包(静态方法)
参数:byte类型数组
返回值:InetAddress对象
实例:
// 获取地址对象 使用InetAddress类静态方法
byte[] address = "127.0.0.1".getBytes();
InetAddress localhost = InetAddress.getByAddress(address);
getByName(String host)(静态方法)
作用:确定主机名称的IP地址(静态方法)
参数:String类型名称
返回值:InetAddress对象
实例:
// 获取地址对象 使用InetAddress类静态方法
InetAddress localhost = InetAddress.getByName("127.0.0.1");
getPort()
作用:获取端口号(实例方法)
参数:String类型名称
返回值:InetAddress对象
实例:
// 创建Socket对象
DatagramSocket socket = new DatagramSocket(12455);
System.out.println("服务已启动");
// 准备数组接收数据
byte[] receiveData = new byte[36];
// 准备数据包对象
DatagramPacket receivePacket = new DatagramPacket(receiveData,receiveData.length);
// 接收数据
socket.receive(receivePacket);
// 调用方法获取 获取接收到的数据
byte[] data = receivePacket.getData();
// 获取客户端的IP信息
InetAddress clientAddress = receivePacket.getAddress();
int clientPort = receivePacket.getPort();
TCP协议
TCP协议:面向连接的 安全的 可靠的传输协议;
单客户端(单线程、IO流)的TCP通信示例:
package com.NetProgram.Demo;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args) {
try {
// 创建服务器端Socket对象 端口号为 8899
ServerSocket server = new ServerSocket(8899);
System.out.println("服务器已启动");
// 接收客户端请求 获取到Socket对象
// accept()方法会等待用户发送请求 然后再继续向下执行
Socket socket = server.accept();
// 根据Socket对象获取字节读取流
InputStream inputStream = socket.getInputStream();
// 根据字节读取流创建 BufferedReader对象
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
// 读取对象
System.out.println("接收到客户端信息:" + reader.readLine());
// 发送信息
OutputStream outputStream = socket.getOutputStream();
String msg = "你好,客户端";
outputStream.write(msg.getBytes());
// 关闭流
socket.shutdownOutput();
} catch (IOException e) {
e.printStackTrace();
}
}
}
package com.NetProgram.Demo;
import java.io.*;
import java.net.Socket;
public class Client {
public static void main(String[] args) {
try {
// 创建客户端Socket对象 指定主机地址为本机 端口号为8899
Socket socket = new Socket("127.0.0.1",8899);
// 根据Socket对象获取到字节输出流
OutputStream outputStream = socket.getOutputStream();
String message = "你好,服务器";
// 写入信息
outputStream.write(message.getBytes());
// 关闭写入流
socket.shutdownOutput();
// 读取数据
InputStream inputStream = socket.getInputStream();
// 创建带缓存读取流
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
// 打印
System.out.println("服务器回复:" + reader.readLine());
} catch (IOException e) {
e.printStackTrace();
}
}
}
多客户端(多线程、IO流)的TCP通信示例:
package com.NetProgram.Demo2;
import java.io.*;
import java.net.Socket;
public class Client2 {
public static void main(String[] args) {
// 创建 Socket对象
try {
Socket socket = new Socket("localhost",9090);
// 获取一个写入流对象
OutputStream outputStream = socket.getOutputStream();
outputStream.write("你好,服务器".getBytes());
// 关闭写入流
socket.shutdownOutput();
// 读取流
InputStream inputStream = socket.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
// 打印数据
System.out.println("服务器回复:" + reader.readLine());
} catch (IOException e) {
e.printStackTrace();
}
}
}
package com.NetProgram.Demo2;
import java.io.*;
import java.net.Socket;
public class Client1 {
public static void main(String[] args) {
// 创建 Socket对象
try {
Socket socket = new Socket("127.0.0.1",9090);
// 获取一个写入流对象
OutputStream outputStream = socket.getOutputStream();
outputStream.write("你好,服务器".getBytes());
// 关闭写入流
socket.shutdownOutput();
// 读取流
InputStream inputStream = socket.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
// 打印数据
System.out.println("服务器回复:" + reader.readLine());
} catch (IOException e) {
e.printStackTrace();
}
}
}
package com.NetProgram.Demo2;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args) {
// 创建Socket
try {
ServerSocket server = new ServerSocket(9090);
System.out.println("服务器已启动");
// 一直运行
while(true){
Socket socket = server.accept();
// 每一个socket,开启一个线程
ServerThread serverThread = new ServerThread(socket);
serverThread.start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
package com.NetProgram.Demo2;
import java.io.*;
import java.net.InetAddress;
import java.net.Socket;
public class ServerThread extends Thread{
// 定义属性为 套接字 socket
private Socket socket;
public ServerThread(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
// 服务器运行的代码,多线程
InputStream inputStream = null;
try {
// 获取 读取流对象
inputStream = socket.getInputStream();
// 创建带缓存读取流
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
// 获取客户端的信息
InetAddress clientInetInfo = socket.getInetAddress();
String hostName = clientInetInfo.getHostName();
String hostAddress = clientInetInfo.getHostAddress();
// 打印信息
System.out.println("hostName: " + hostName + "|" + reader.readLine());
// 获取写入流
OutputStream outputStream = socket.getOutputStream();
// 写入数据
outputStream.write("我是服务器,我一直在...".getBytes());
// 关闭写入流
socket.shutdownOutput();
} catch (IOException e) {
e.printStackTrace();
}
}
}
使用TCP协议 实现 多个客户端连接同一个服务器;
分析:多个客户端属于多个线程 请求同一个服务器 服务器也应该使用不同的线程来处理不同的客户端;
即 每次接收一个新的请求 就分配一个新的线程来处理对应的客户端;
TCP协议三次握手四次挥手
TCP协议的三次握手:
第一次:客户端向服务器发送连接请求;
第二次:服务器向客户端响应连接请求;
第三次:客户端与服务器建立连接;
TCP协议的四次挥手:
第一次:客户端向服务器发送断开连接请求
第二次:服务器向客服端响应收到断开连接请求(因为TCP连接是双向的,所以此时服务器依然可以 向客户端发送信息)
第三次:客户端等待服务器发送信息完成,向服务器确定全部信息发送完毕,并且断开客户端与服务器的连接
第四次:服务器向客户端断开连接
UDP协议
UDP 用户数据报协议 非面向连接的 不安全 不可靠的传输协议 效率高;
Server端
package com.NetProgram.Demo3;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
public class Server {
public static void main(String[] args) {
try {
// 创建Socket对象
DatagramSocket socket = new DatagramSocket(12455);
System.out.println("服务已启动");
// 准备数组接收数据
byte[] receiveData = new byte[36];
// 准备数据包对象
DatagramPacket receivePacket = new DatagramPacket(receiveData,receiveData.length);
// 接收数据
socket.receive(receivePacket);
// 调用方法获取 获取接收到的数据
byte[] data = receivePacket.getData();
// 获取客户端的IP信息
InetAddress clientAddress = receivePacket.getAddress();
int clientPort = receivePacket.getPort();
// 解析数据
String msg = new String(data,0,data.length);
System.out.println(msg);
// 发送数据
byte[] sendData = "你好,客户端".getBytes();
// 准备数据包
DatagramPacket packet = new DatagramPacket(sendData, sendData.length,clientAddress,clientPort);
socket.send(packet);
} catch (SocketException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Client端
package com.NetProgram.Demo3;
import java.io.IOException;
import java.net.*;
public class Client {
public static void main(String[] args) {
// 准备数据
String msg = "你好,服务器";
byte[] sendData = msg.getBytes();
try {
// 获取地址对象 使用InetAddress类静态方法
InetAddress localhost = InetAddress.getByName("127.0.0.1");
// 准备数据包
DatagramPacket packet = new DatagramPacket(sendData,sendData.length,localhost,12455);
// 准备DatagramPacket对象
DatagramSocket socket = new DatagramSocket();
// 发送数据
socket.send(packet);
// 客户端接收数据
// 准备数组接收数据
byte[] receiveData = new byte[36];
// 准备数据包对象
DatagramPacket receivePacket = new DatagramPacket(receiveData,receiveData.length);
// 接收数据
socket.receive(receivePacket);
// 调用方法获取 获取接收到的数据
byte[] data = receivePacket.getData();
// 解析数据
String res = new String(data,0,data.length);
System.out.println(res);
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (SocketException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}