网络编程基础
计算机网络
所谓计算机网络,就是把分布在不同区域的计算机与专门的外部设备用通信线路相互连接成一个规模大,而且功能强的网络系统,从而使得计算机之间可以相互传递信息,共享数据、软件等资源。
网络分类 :按照地理范围:广域网( Internet) ,城域网( 一个城市、多个城市,长城宽带),局域网(机房)
网络编程
所谓网络编程(不是网站编程),指的就是在同一个网络中不同机器之间的通信
网络编程参考模型
OSI参考模型:包括七层:[物理层]、[数据链路层]、[网络层]、[传输层]、[会话层]、[表示层]和[应用层]
TCP/IP参考模型:包括四层:
1.链路层(数据链路层/物理层):包括操作系统中的设备驱动程序、计算机中对应的网络接口卡
2.网络层:处理分组在网络中的活动,比如分组的选路。
3.传输层:主要为两台主机上的应用提供端到端的通信。
4.应用层(应用层/表示层/会话层):负责处理特定的应用程序细节
通信协议
需要通信的设备之间需要实现相同的通信协议
TCP/IP网络分层 :链路层、网络层、传输层、应用层
通信协议分类:
网络层IP协议:IPV4和IPV6,互联网协议
传输层协议:TCP和UDP
应用层协议:HTTP、FTP、SMTP、POP3
TCP协议:TCP(Transmission Control Protocol 传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议。数据大小无限制。建立连接的过程叫三次握手,断开叫四次断开。
UDP协议:UDP 是User Datagram Protocol的简称, 中文名是用户数据报协议,是TCP/IP参考模型中一种无连接的传输层协议,提供面向事务的简单不可靠信息传送服务,每个包的大小64Kb。
IP协议:[Internet Protocol]网际协议,能使连接到网上的所有计算机网络实现相互通信的一套规则,规定了计算机在因特网上进行通信时应当遵守的规则。IP协议中包含一块非常重要的内容就是为计算机分配了一个唯一标识即IP地址。
相关类的使用
Java提供了InetAddress类来代表ip地址,是对ip地址的抽取和封装,有两个子类: Inet4Address,Inet6Address,分别表示IPv4和IPv6
常用方法:
//1.获取主机:主机名称和ip地址
/**
* static InetAddress getLocalHost()
返回本地主机。
*/
InetAddress id1 = null;
try {
id1 = InetAddress.getLocalHost();
//USER-VG9EDR1SST/10.31.165.42
System.out.println(id1);
} catch (UnknownHostException e) {
// 未知的主机
e.printStackTrace();
}
//2.获取ip地址的字符串表示形式
/**
* String getHostAddress()
返回 IP 地址字符串(以文本表现形式)。
*/
String str1 = id1.getHostAddress();
System.out.println(str1);//10.31.165.42
//3.获取主机名
/**
* String getHostName()
获取此 IP 地址的主机名。
*/
String str2 = id1.getHostName();
System.out.println(str2);
//4.根据主机或者ip地址获取InetAddress对象
/**
* static InetAddress getByName(String host)
在给定主机名的情况下确定主机的 IP 地址。
*/
try {
InetAddress id2 = InetAddress.getByName("10.31.165.42");
///10.31.165.42
System.out.println(id2);
InetAddress id3 = InetAddress.getByName("www.baidu.com");
//www.baidu.com/115.239.211.112
System.out.println(id3);
} catch (UnknownHostException e) {
e.printStackTrace();
}
//5.根据主机或者ip地址获取所有InetAddress对象
/**
* static InetAddress[] getAllByName(String host)
*/
try {
InetAddress[] arr = InetAddress.getAllByName("www.baidu.com");
for(InetAddress address:arr) {
//www.baidu.com/115.239.210.27
System.out.println(address.toString());
//115.239.210.27
System.out.println(address.getHostAddress());
//www.baidu.com
System.out.println(address.getHostName());
}
} catch (UnknownHostException e) {
e.printStackTrace();
}
基于TCP的Socket(套接字)通信模型
Socket是操作系统提供的一种底层的通信机制,Java仅仅是对底层的socket的一种封装。供开发人员方便使用。
例:客户端发送消息,服务端接收消息
服务器(5步)
1 创建服务器套接字,并指定端口号
2 接受客户端连接,并返回客户端套接字
3 获取输入输出流
4 处理
5 关闭
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
/**
* 服务器端:
* 编程步骤:
* 1 创建服务器套接字,并指定端口号
* 2 接受客户端连接,并返回客户端套接字
* 3 获取输入输出流
* 4 处理
* 5 关闭
*
*/
public class TcpServer {
public static void main(String[] args) throws Exception{
//1 创建服务器套接字,并指定端口号
ServerSocket listener=new ServerSocket(10086);
//2 接受客户端连接,并返回客户端套接字(阻塞方法)
System.out.println("服务器已启动....");
Socket socket = listener.accept();
//3 获取输入|输出流
InputStream is = socket.getInputStream();
BufferedReader br=new BufferedReader(new InputStreamReader(is));
// 4 处理
String data = br.readLine();
System.out.println("客户端说:"+data);
//5 关闭
br.close();
socket.close();
listener.close();
}
}
客户端:(4步)
1 创建客户端套接字,并指定服务器的地址和端口号
2 获取输入|输出流
3 处理
4 关闭
import java.io.BufferedWriter;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;
/**
* Tcp客户编码
* 步骤:
* 1 创建客户端套接字,并指定服务器的地址和端口号
* 2 获取输入|输出流
* 3 处理
* 4 关闭
*
*/
public class TcpClient {
public static void main(String[] args) throws Exception{
//1 创建客户端套接字,并指定服务器的地址和端口号
Socket socket=new Socket("10.9.61.190", 10086);
//2 获取输入|输出流
OutputStream os = socket.getOutputStream();
BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(os));
//3 处理
bw.write("好久不见");
bw.flush();
//4 关闭
bw.close();
socket.close();
}
}
例:客户端发送消息,服务端回复消息
服务器端
public class Server {
public static void main(String[] args) {
//1.实例化一个ServerSocket的对象
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(7777);
//2.监听,获取连接到的客户端
System.out.println("等待服务端的连接...");
//注意:在未连接成功之前,将一直处于阻塞状态
Socket socket = serverSocket.accept();
System.out.println("连接成功");
//3.获取网络到内存的一个输入流
InputStream input = socket.getInputStream();
//4.读取数据
byte[] arr = new byte[1024];
int len = input.read(arr);
String message = new String(arr, 0, len);
//5.组织信息
String ipString = socket.getInetAddress().getHostAddress();
int port = socket.getPort();
System.out.println(ipString +":" + port + "说:" + message);
//6.服务端给客户端回复消息
OutputStream output = socket.getOutputStream();
output.write("你也好,好久不见了".getBytes());
output.flush();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
客户端
public class Client {
public static void main(String[] args) {
// 1.建立一个与服务端之间的连接
/**
* 面向连接:三次握手 创建一个Socket的对象,就相当于完成了三次握手,建立了一个安全的连接
*/
// 注意:包含了两个流:InputStream和OutputStream
Socket socket = null;
try {
socket = new Socket(InetAddress.getByName("10.31.165.42"), 7777);
// 2.将需要发送的数据写入到网络中
OutputStream output = socket.getOutputStream();
// 3.写入
output.write("hello你好吗?".getBytes());
output.flush();
// 4.收取服务端发送来的消息
InputStream input = socket.getInputStream();
byte[] arr = new byte[1024];
int len = input.read(arr);
String message = new String(arr, 0, len);
System.out.println("来自服务端的回复:" + message);
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
例:模拟网盘,上传下载图片
服务器端:
public class Server {
public static void main(String[] args) {
//1,实例化一个ServerSocket对象
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(8888);
//2.等待客户端的连接
System.out.println("等待连接...");
Socket socket = serverSocket.accept();
System.out.println("连接成功");
//3.读取网络中的数据
//输入流:读取网络中的数据
BufferedInputStream input = new BufferedInputStream(socket.getInputStream());
//输出流:将读取到的数据写入到服务端的磁盘
BufferedOutputStream output = new BufferedOutputStream(new FileOutputStream(new File("file/server/image_copy.png")));
//4.读取和写入
byte[] arr = new byte[1024];
int len = 0;
while((len = input.read(arr)) != -1) {
output.write(arr, 0, len);
output.flush();
}
System.out.println("上传文件成功");
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
客户端:
public class Client {
public static void main(String[] args) {
//1.创建Socket对象
Socket socket = null;
try {
socket = new Socket(InetAddress.getByName("10.31.165.42"), 8888);
//2.输入流和输出流
//输入流:用来读取客户端磁盘上的图片文件
BufferedInputStream input = new BufferedInputStream(new FileInputStream(new File("file/client/image.png")));
//输出流:将图片写入到网络中
BufferedOutputStream output = new BufferedOutputStream(socket.getOutputStream());
//3.将读取到的数据写入
byte[] arr = new byte[1024];
int len = 0;
while((len = input.read(arr)) != -1) {
output.write(arr, 0, len);
output.flush();
}
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
finally {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
多个客户端,一个服务器通信 详见:聊天室
注册、登录案例 详见:注册登录(简易)
基于UDP的通信
概念:
User Datagram Protocol的简称,用户数据包协议,提供面向事务的简单不可靠信息传送服务
特点:
- 不安全
- 无连接
- 效率高
- UDP传输数据时是有大小限制的,每个被传输的数据报必须限定在64KB之内
传输的过程中使用的类:
DatagramSocket: 数据报套接字,表示用来发送和接收数据报包的套接字
DatagramPacket:此类表示数据报包。每个包最大64kb。
例:在使用UDP通信的时候,不分客户端,服务器端,两者是平等的
发送方
public class Sender {
public static void main(String[] args) {
//端口号表示的是指定的接收方的端口号,而发送方的端口是由系统自动分配的
sendMessage("10.31.165.42", 6666,"你好啊");
}
/**
* 发送数据
* @param ip 指定接收方的ip地址
* @param port 指定接收方的端口号
* @param message 需要发送的信息
*/
public static void sendMessage(String ip,int port,String message) {
//1.实例化DatagramSocket的对象
//注意:和流的使用类似,使用套接字完成之后需要关闭
DatagramSocket socket = null;
try {
socket = new DatagramSocket();
//2.将需要发送的数据封装为数据报包
/**
* DatagramPacket(byte[] buf, int length, InetAddress address, int port)
构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号。
*/
DatagramPacket packet = new DatagramPacket(message.getBytes(), message.length(), InetAddress.getByName(ip), port);
//3.发送
/**
* void send(DatagramPacket p)
从此套接字发送数据报包。
*/
//将数据写入网络的过程
socket.send(packet);
} catch (SocketException e) {
// 父类为IOException
e.printStackTrace();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
接收方
public class Receiver {
public static void main(String[] args) {
//1.实例化DatagramSocket的对象
//需要进行绑定端口号:由发送方发送来的端口号进行决定
/**
* DatagramSocket(int port)
创建数据报套接字并将其绑定到本地主机上的指定端口。
*/
DatagramSocket socket = null;
try {
socket = new DatagramSocket(6666);
//2.将接收到的数据封装到数据报包中
/**
* DatagramPacket(byte[] buf, int length)
构造 DatagramPacket,用来接收长度为 length 的数据包。
*/
byte[] arr = new byte[1024];
DatagramPacket packet = new DatagramPacket(arr, arr.length);
System.out.println("等待接收数据~~~~~~~");
//3.接收数据
/**
* void receive(DatagramPacket p)
从此套接字接收数据报包。
*/
//注意:将数据从网络中读取出来
socket.receive(packet);
//4.获取发送方的详细信息
//信息
/**
* byte[] getData()
返回数据缓冲区。
*/
byte[] messages = packet.getData();
String result = new String(messages, 0, messages.length);
//获取发送方的ip地址
/**
* InetAddress getAddress()
返回某台机器的 IP 地址,此数据报将要发往该机器或者是从该机器接收到的。
*/
InetAddress address = packet.getAddress();
String ip = address.getHostAddress();
//获取消息是从发送发的哪个端口号发出来的
/**
* int getPort()
返回某台远程主机的端口号,此数据报将要发往该主机或者是从该主机接收到的。
*/
int port = packet.getPort();
System.out.println(ip + ":" + port + "说:" + result);
} catch (SocketException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}