网络编程概述
/*
计算机网络
是指将地理位置不同的具有独立功能的多台计算机及其外部设备,
通过通信线路连接起来,在网络操作系统,网络管理软件及网络
通信协议的管理和协调下,实现资源共享和信息传递的计算机系统。
网络编程
就是用来实现网络互连的不同计算机上运行的程序间可以进行数据交换。
*/
网络模型
/*
网络模型一般是指
OSI(Open System Interconnection开放系统互连)参考模型
TCP/IP参考模型
*/
网络模型图解
网络模型7层概述
/*
网络模型7层概述:(重点学习传输层)
1.物理层:主要定义物理设备标准,如网线的接口类型、光纤的接口类型、
各种传输介质的传输速率等。它的主要作用是传输比特流(就是由1、0转化
为电流强弱来进行传输,到达目的地后在转化为1、0,也就是我们常说的数模
转换与模数转换)。这一层的数据叫做比特。
2. 数据链路层:主要将从物理层接收的数据进行MAC地址(网卡的地址)的封装与解封装。
常把这一层的数据叫做帧。在这一层工作的设备是交换机,数据通过交换机来传输。
3. 网络层:主要将从下层接收到的数据进行IP地址(例192.168.0.1)的封装与解封装。在这
一层工作的设备是路由器,常把这一层的数据叫做数据包。
4. 传输层:定义了一些传输数据的协议和端口号(WWW端口80等),如:TCP(传输控制协议,
传输效率低,可靠性强,用于传输可靠性要求高,数据量大的数据),UDP(用户数据报协议,
与TCP特性恰恰相反,用于传输可靠性要求不高,数据量小的数据,如QQ聊天数据就是通过这种
方式传输的)。 主要是将从下层接收的数据进行分段和传输,到达目的地址后再进行重组。
常常把这一层数据叫做段。
5.会话层:通过传输层(端口号:传输端口与接收端口)建立数据传输的通路。主要在你的系统之间
发起会话或者接受会话请求(设备之间需要互相认识可以是IP也可以是MAC或者是主机名)
6.表示层:主要是进行对接收的数据进行解释、加密与解密、压缩与解压缩等(也就是把计算机能够识别
的东西转换成人能够能识别的东西(如图片、声音等)。
7.应用层: 主要是一些终端的应用,比如说FTP(各种文件下载),WEB(IE浏览),QQ之类的(可以把它理解
成我们在电脑屏幕上可以看到的东西.就是终端应用)。
*/
网络通信三要素
/*
IP地址:InetAddress
网络中设备的标识,不易记忆,可用主机名
端口号
用于标识进程的逻辑地址,不同进程的标识
传输协议
通讯的规则
常见协议:TCP,UDP
举例:
某一天朱佳乐要和一个女生表白,怎么办呢?
1、他要去找到那个女生 -- IP地址
2、找到了之后,他开始表白了,跟她说话,对着耳朵说 -- 端口号
3、他要说什么呢?“I love you”
但是呢,那个女生没有学过英语。
他就没有必要说英语,说她能听懂的语言,说“我爱你” -- 协议
*/
IP地址:InetAddress
/*
IP地址:InetAddress
网络中设备的唯一标识。
但是我们计算机只能识别二进制的数据,所以我们想着IP地址在计算机存储的格式
应该也是二进制的。
查看windows中ip地址:win+R ,输入cmd,输入ipconfig回车
IP: 192.168.3.170
换算成二进制:11000000.10101000.00000011.10101010
实际上存储的时候:11000000101010000000001110101010,但是呢,这样的形式,
在学习的时候,难免要去配置地址等,记忆等等涉及到地址操作的时候,表示起来会很麻烦。
所以,为了更方便的表示ip地址,我们就把ip地址上的每一个字节的数据换算成十进制,然后再在
字节与字节之间用 .分割表示,而这样的表示法:叫做点分十进制表示法。
IP地址的组成:网络号段 + 主机号段
IP地址的分类:
A类:第一个号段为网络号段 + 后面三个号段都为主机号段
可以容纳:256 * 256 * 256-2台主机
B类:前两个号段为网络号段 + 后面两个为主机号段
可以容纳:256 * 256-2台主机
C类:前三个号段为网络号段 + 最后一个为主机号段
可以容纳:256-2台主机
D类:224.0.0.1---239.255.255.254
E类:240.0.0.1---247.255.255.254
特殊的IP地址
1、localhost(本机/回环地址):127.0.0.1
127.0.0.1 回环地址,可用于测试本机的网络是否有问题,DOS命令:ping 127.0.0.1
2、广播地址:
x.x.x.255
3、网络地址:
x.x.x.0
两个DOS命令:
1、查看windows中ip地址:
win+R ,输入cmd,输入ipconfig回车
2、测试网络是否联通
ping + IP地址/hosts名
如何计算可以容纳多少台主机(以A类IP地址为例):
A类有三个主机号段,每个号段可容纳2^8个主机
所以三个号段可容纳2^24个主机
但是三个号段全为0和全为1的不可以作为主机号段,所以可容纳2^24-2个主机
为什么主机号为全0和全1不作分配?
①主机号全为“0”。不论哪一类网络,主机号全为“0”表示指向本网,常用在路由表中;
②主机号全为“1”。主机号全为“1”表示广播地址,向特定的所在网上的所有主机发送数据包。
*/
import java.net.InetAddress;
import java.net.UnknownHostException;
/*
为了方便我们获取IP地址,java提供了一个类InetAddress(java.net)供我们使用
方法:
static InetAddress getByName(String host)
确定主机名称的IP地址。
String getHostName()
获取此IP地址的主机名。
String getHostAddress()
返回文本显示中的IP地址字符串。
*/
public class InetAddressDemo {
public static void main(String[] args) throws Exception {
//static InetAddress getByName(String host)
//确定主机名称的IP地址。
//此时会产生编译时期异常:可能那个IP地址不存在
InetAddress address = InetAddress.getByName("192.168.3.170");
System.out.println(address);//-/192.168.3.170
//String getHostName()
//获取此IP地址的主机名。
String hostName = address.getHostName();
System.out.println(hostName);
//String getHostAddress()
//返回文本显示中的IP地址字符串。
String hostAddress = address.getHostAddress();
System.out.println(hostAddress);//192.168.3.170
}
}
端口号
/*
端口号
物理端口--网卡口
逻辑端口--我们指的就是逻辑端口
A:每个网络程序都会至少有一个逻辑端口
B:用于标识进程的逻辑地址,不同进程的标识
C:有效端口:0~65535,其中0~1024系统使用或保留端口。
通过netstat -ano可以查看端口号
*/
协议
/*
协议
UDP协议(举例:发短信,飞鸽传书)
将数据源和目的封装成数据包中,不需要建立连接;
每个数据报包的大小在限制在64k;因无连接,是不可靠协议;
不需要建立连接,速度快
TCP协议(举例:打电话,极域)
建立连接,形成传输数据的通道;在连接中进行大数据量传输;
通过三次握手完成连接,是可靠协议;必须建立连接,效率会稍低
(三次握手四次挥手)
*/
Socket
/*
Socket套接字:
网络上具有唯一标识的IP地址和端口号组合在一起才能构成唯一能识别的标识符套接字。
Socket原理机制:
通信的两端都有Socket。
网络通信其实就是Socket间的通信。
数据在两个Socket间通过IO传输。
*/
Socket机制图解
UDP传输
/*
DatagramSocket与DatagramPacket
建立发送端,接收端。
建立数据包。
调用Socket的发送接收方法。
关闭Socket。
发送端与接收端是两个独立的运行程序。
*/
UDP协议发送数据--发送端思路
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
/*
UDP协议发送数据:
1、创建发送端的Socket对象(DatagramSocket)
2、创建数据,将数据封装成数据包
3、调用Socket对象中的一个方法,将数据包发出去
4、释放资源,关闭Socket对象
*/
public class SendDemo1 {
public static void main(String[] args) throws Exception {
//通过DatagramSocket类中的无参构造方法创建发送端的Socket对象
DatagramSocket ds = new DatagramSocket();
//创建数据
//数据转成字节数组
byte[] bytes = "你好,大数据".getBytes();
//获取数组长度
int length = bytes.length;
//IP地址对象
InetAddress address = InetAddress.getByName("192.168.3.170");
//端口号(1024~65535)
int port = 10086;
//将数据封装成数据包
//调用DatagramPacket类中的有参构造方法
//DatagramPacket(byte[] buf, int length, InetAddress address, int port)
//构造用于发送长度的分组的数据报包 length指定主机上到指定的端口号。
DatagramPacket dp = new DatagramPacket(bytes, length, address, 10086);
//调用DatagramSocket类对象中的一个方法,将数据包发出去
//void send(DatagramPacket p)
//从此套接字发送数据报包。
ds.send(dp);
//释放资源,关闭Socket
ds.close();
}
}
UDP协议发送数据--接收端思路
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
/*
UDP协议接收数据:
1、创建接收端的Socket对象
2、创建一个数据包(用来接收数据的容器)
3、调用Socket对象方法接收数据
4、解析数据包,显示在控制台上
5、释放资源
*/
public class ReceiveDemo1 {
public static void main(String[] args) throws Exception {
//通过DatagramSocket类中的有参构造方法创建接收端的Socket对象
//DatagramSocket(int port)
//构造数据报套接字并将其绑定到本地主机上的指定端口。
DatagramSocket ds = new DatagramSocket(10086);
//创建一个数据包(用来接收数据的容器)
//DatagramPacket(byte[] buf, int length)
//构造一个 DatagramPacket用于接收长度的数据包 length 。
byte[] bytes = new byte[1024];
int length = bytes.length;
DatagramPacket dp = new DatagramPacket(bytes, length);
//调用DatagramSocket类对象中的一个方法接收数据
//public void receive(DatagramPacket p)
//从此套接字接收数据报包。
ds.receive(dp);
//解析数据包,显示在控制台上
//DatagramPacket类中
//byte[] getData() 返回数据缓冲区。
byte[] data = dp.getData();
//字节数组转字符串
String s = new String(data, 0, data.length);
//获取发送数据计算机的IP地址
//DatagramPacket类中
//InetAddress getAddress() 返回该数据报发送或接收数据报的计算机的IP地址。
InetAddress address = dp.getAddress();
String ip = address.getHostAddress();
System.out.println(ip+"发送的数据是:"+s);
//释放资源
ds.close();
}
}
UDP协议发送数据的案例
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
/*
接收端不能重复启动,否则报错--端口被占用异常
*/
public class ReceiveDemo2 {
public static void main(String[] args) throws Exception {
//创建接收端的Socket对象
DatagramSocket ds = new DatagramSocket(12345);
while (true){
//创建接收数据包
byte[] bytes = new byte[1024];
DatagramPacket dp = new DatagramPacket(bytes, bytes.length);
//调用Socket对象接收数据包的方法
ds.receive(dp); //阻塞状态-等发送端发送数据
//解析数据
String ip = dp.getAddress().getHostAddress();
String hostName = dp.getAddress().getHostName();
//DatagramPacket类中
//int getLength() 返回要发送的数据的长度或接收到的数据的长度。
String s = new String(dp.getData(), 0, dp.getLength());
System.out.println(ip+":"+hostName+"发送来数据:"+s);
}
}
}
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
/*
键盘录入数据进行发送
如果输入的是886,那么发送端就结束输入
多个发送端可以对一个接受端发送信息
*/
public class SendDemo2 {
public static void main(String[] args) throws Exception {
//创建发送端的Socket对象
DatagramSocket ds = new DatagramSocket();
//封装键盘录入的数据
//System类(java.lang)
//System.in:
//public static final InputStream in“标准”输入流。
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String line = null;
while ((line = br.readLine())!=null){
if("886".equals(line)){
break;
}
//如果输入的不是886将数据打包发送
DatagramPacket dp = new DatagramPacket(line.getBytes(), line.getBytes().length,
InetAddress.getByName("192.168.3.170"), 12345);
//调用Socket对象发送数据包
ds.send(dp);
}
//释放资源
ds.close();
}
}
TCP传输
/*
Socket和ServerSocket
建立客户端和服务器端
建立连接后,通过Socket中的IO流进行数据的传输
关闭socket
同样,客户端与服务器端是两个独立的应用程序。
*/
TCP传输-服务端思路
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
/*
TCP服务端接收数据:
1、创建服务器端的Socket对象
2、监听客户端的连接,返回一个相应的Socket对象
3、获取输入流对象,读取数据显示在控制台上
4、释放资源
*/
public class ServerDemo1 {
public static void main(String[] args) throws Exception {
//创建服务器端的Socket对象
//ServerSocket类中的有参构造方法
//ServerSocket(int port)
//创建绑定到指定端口的服务器套接字。
ServerSocket ss = new ServerSocket(12345);
//监听客户端的连接,返回一个相应的Socket对象
//ServerSocket类中的方法
//Socket accept()
//侦听要连接到此套接字并接受它。
Socket s = ss.accept();
//获取输入流对象,读取数据显示在控制台上
//Socket类中方法:
//InputStream getInputStream() 返回此套接字的输入流。
InputStream is = s.getInputStream();
byte[] bytes = new byte[1024];
int length = is.read(bytes);
String s1 = new String(bytes, 0, length);
//获取ip地址
String ip = s.getInetAddress().getHostAddress();
System.out.println(ip+":"+s1);
//释放资源
s.close();//是s.close而不是ss.close,服务器一般不会关
}
}
TCP传输-客户端思路
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.util.Scanner;
/*
TCP协议发送数据:
1、创建发送端的Socket对象
这一步一旦成功了,说明连接建立成功
2、获取输出流对象,写数据
3、释放资源
TCP编程先启动服务器端,否则报错-连接被拒绝
ConnectException: Connection refused: connect
*/
public class ClientDemo1 {
public static void main(String[] args) throws Exception {
//创建发送端的Socket对象
//调用Socket类有参构造方法
//Socket(InetAddress address, int port)
//创建流套接字并将其连接到指定IP地址的指定端口号。
// Socket s = new Socket(InetAddress.getByName("192.168.3.170"), 12345);
//但是这样创建会很麻烦
//Socket类中还有一个有参构造方法
//Socket(String host, int port)
//创建流套接字并将其连接到指定主机上的指定端口号。
Socket s = new Socket("192.168.3.170", 12345);
//获取输出流对象,写数据
//Socket类中的方法:
//OutputStream getOutputStream() 返回此套接字的输出流。
OutputStream os = s.getOutputStream();
os.write("大数据,yyds".getBytes());
//释放资源
s.close();
}
}
TCP传输-案例
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;
public class ClientDemo2 {
public static void main(String[] args) throws Exception {
//创建客户端Socket对象
Socket s = new Socket("192.168.3.170", 12345);
//获取输出流,往服务器写数据
OutputStream outputStream = s.getOutputStream();
//获取输入流,读取服务器给的反馈
InputStream inputStream = s.getInputStream();
Scanner sc = null;
while (true){
sc = new Scanner(System.in);
String next = sc.next();
outputStream.write(next.getBytes());
byte[] bytes = new byte[1024];
int length = inputStream.read(bytes);
String s1 = new String(bytes, 0, length);
System.out.println("服务器反馈:"+s1);
}
}
}
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerDemo2 {
public static void main(String[] args) throws Exception {
//创建服务器端的Socket对象
ServerSocket ss = new ServerSocket(12345);
//监听客户端的连接
Socket s = ss.accept();
//获取输出流,给客户端反馈
OutputStream outputStream = s.getOutputStream();
//获取输入流,读取客户端给的信息
InputStream inputStream = s.getInputStream();
while (true) {
byte[] bytes = new byte[1024];
int length = inputStream.read(bytes);
String s1 = new String(bytes, 0, length);
String ip = s.getInetAddress().getHostAddress();
System.out.println(ip + ":" + s1);
outputStream.write("服务器已经收到".getBytes());
}
}
}
TCP传输-案例plus
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerDemo4 {
public static void main(String[] args) throws Exception{
System.out.println("=================服务端启动=================");
//1.注册端口:public ServerSocket(int port)
ServerSocket serverSocket = new ServerSocket(10086);
//2.定义一个循环不断接受客户端的连接请求
while (true) {
//3.开始等待接受客户端的Socket管道连接
Socket accept = serverSocket.accept();
new ServerReaderThread(accept).start();
}
}
}
class ServerReaderThread extends Thread{
private Socket socket;
public ServerReaderThread(Socket socket){
this.socket = socket;
}
@Override
public void run() {
try {
//3.从socket通信管道中得到一个字节输入流
InputStream is = socket.getInputStream();
//4.把字节输入流转换成字符输入流
InputStreamReader isr = new InputStreamReader(is);
//5.把字符输入流包装为缓冲字符输入流
BufferedReader br = new BufferedReader(isr);
//6.按照行读取消息
String line;
while ((line = br.readLine())!= null){
System.out.println(socket.getRemoteSocketAddress()+"说:"+line);
}
}catch (Exception e){
System.out.println("客户端"+socket.getRemoteSocketAddress()+"下线了。");
}
}
}
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.util.Scanner;
public class ClientDemo3 {
public void chat(){
Scanner sc = null;
try {
//创建TCP协议客户端的Socket对象
Socket s = new Socket("192.168.3.170", 10086);
System.out.println("请输入想要发送的数据:");
//获取输出流对象
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
while (true){
sc = new Scanner(System.in);
String next = sc.next();
bw.write(next);
bw.newLine();
bw.flush();
}
}catch (IOException e){
e.printStackTrace();
}
}
public static void main(String[] args) {
new ClientDemo3().chat();
}
}
文件上传案例
import java.io.*;
import java.net.Socket;
public class UserThread implements Runnable {
private Socket s;
public UserThread(Socket s) {
this.s = s;
}
@Override
public void run() {
try {
// 封装通道内的流
BufferedReader br = new BufferedReader(new InputStreamReader(
s.getInputStream()));
// 封装文本文件
// BufferedWriter bw = new BufferedWriter(new
// FileWriter("Copy.java"));
// 为了防止名称冲突
String newName = System.currentTimeMillis() + ".txt";
BufferedWriter bw = new BufferedWriter(new FileWriter(newName));
String line = null;
while ((line = br.readLine()) != null) { // 阻塞
bw.write(line);
bw.newLine();
bw.flush();
}
// 给出反馈
BufferedWriter bwServer = new BufferedWriter(
new OutputStreamWriter(s.getOutputStream()));
bwServer.write("文件上传成功");
bwServer.newLine();
bwServer.flush();
// 释放资源
bw.close();
s.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class UploadServer {
public static void main(String[] args) throws IOException {
// 创建服务器Socket对象
ServerSocket ss = new ServerSocket(11111);
while (true) {
Socket s = ss.accept();
new Thread(new UserThread(s)).start();
}
}
}
import java.io.*;
import java.net.Socket;
public class UploadClient {
public static void main(String[] args) throws IOException {
// 创建客户端Socket对象
Socket s = new Socket("192.168.3.170", 11111);
// 封装文本文件
// BufferedReader br = new BufferedReader(new FileReader(
// "InetAddressDemo.java"));
BufferedReader br = new BufferedReader(new FileReader(
"a.txt"));
// 封装通道内流
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
s.getOutputStream()));
String line = null;
while ((line = br.readLine()) != null) { // 阻塞
bw.write(line);
bw.newLine();
bw.flush();
}
// Socket提供了一个终止,它会通知服务器你别等了,我没有数据过来了
s.shutdownOutput();
// 接收反馈
BufferedReader brClient = new BufferedReader(new InputStreamReader(
s.getInputStream()));
String client = brClient.readLine(); // 阻塞
System.out.println(client);
// 释放资源
br.close();
s.close();
}
}