一、网络编程简介
生产环境下,通常网络编程和前面 线程结合起来使用过。
网络: 可以用来连接计算机和计算机之间的通讯,分为:局域网,广域网,城域网
局域网:例如公司内部,班级内部的这种内部网 :192.168.XXX.XXX
广域网:连接国家之间,国家内部的网络
城域网:连接城市内部的网络
网络编程:通过上述的网络技术,来实现计算机之间内部程序的 数据交互
1.1 网络编程-OSI模型
早期:由于计算机硬件 和 操作系统软件上 相互可能存在不匹配的情况,而导致计算机之间无法正常使用 网络进行通讯
后来:为了解决这个问题,国际标准化组织ISO 制定了 计算机之间通讯时,需要采用一种通讯模型 这个模型就叫OSI模型
OSI (操作系统通讯接口)
1.2 OSI七层通讯模型(了解)
自上而下:应用层,表现层,会话层,传输层,网络层,数据链路层,物理层
上三层
应用层,表现层,会话层
上三层都跟我们的程序有一定的关系。
应用层:就是我们的程序
表现层:对应用层的数据进行转换,变成计算机能认识的数据
会话层:建立计算机之间的会话通讯通道
传输层
下三层
网络层,数据链路层,物理层
下三层都跟操作系统的底层有关,或者是跟物理硬件有关
网络层:就是我们IP,Mask地址这些东西
数据链路层:在硬件之间进行数据以 ‘帧’为单位的 传输层
物理层:就是硬件 网卡 网线 路由器 交换机 ……
1.3 TCP/IP分层模型(记住)
上述的OSI 7层太过于复杂,所以就简化为4层模型
应用层:上三层,就是我们的应用程序
传输层:将上三层的数据 和 下三层的数据 进行双向传输
网络互连层:使用IP建立网络之间连接
网络接口层:计算机通讯网络入口
1.4 IP、端口、防火墙
IP地址和域名:都是家的家庭地址
端口:防火墙和家的门
防火墙:家外的院墙
不要轻易关闭防火墙
二、网络传输协议
2.1 网络通信的端
客户端:Client
胖客户端:需要你使用安装包进行安装客户端
瘦客户端:就是浏览器
服务端:Server
2.2 C/S架构 和 B/S架构的区别
1、不同的地方
C/S架构模式系统:客户端需要你安装的
B/S架构模式系统:客户端就是浏览器,不需要你安装
2、不同的地方
C/S架构模式系统:如果客户端程序,需要更新,那么就需要下载 更新包
B/S架构模式系统:如果客户端程序,需要更新,只需要刷新浏览器即可(例如:淘宝做改版,你只需要重新刷新淘宝页面即可)
我们以后工作,都是以B/S为主,C/S在我们程序就是一种辅助。
2.3 客户端和服务端通讯协议
从通讯协议上面来讲:底层协议就2大体系:TCP/IP协议,UDP通讯
HTTP协议,WEBSOCKET协议,这些都是针对TCP/IP协议的上层封装
2.4 Tcp/IP协议
Tcp/Ip协议,是一种端到端的,并且依赖连接的,而且通讯安全的协议。
因为采用的是:1问1答模式
例如:打电话
2.4.1 连接的建立方式
Tcp/Ip协议,通讯之前,计算机需要先建立 连接。但是它建立连接时,为了保证一定连接建立成功,所以它采用一种“三次握手”的建立规则
三次握手的具体步骤:
第一步:客户端 向 服务器 发送建立连接的请求 SYN(客户端的标识,代表客户端是谁)
第二步:服务端 向 客户端 回复可以建立连接,并颁发 ACK(客户端标识,这个标识是服务端颁发,用来服务端识别 多个客户端)
第三步:客户端 向 服务端 正式建立连接
三次握手完毕之后,就采用1问1答模式,进行数据交互
类似于谈恋爱:
男孩 想要约会一个女孩,首先会给女孩说:今晚,出来吃饭吧
女孩回复说:可以啊,在哪里呢?
男孩回复说:出来,我们一起去海底捞吃“火锅”吧
2.4.2 连接的断开方式
Tcp/IP协议,断开通讯之前,需要“四次挥手”的过程,原因是:它同样需要确保断开成功,以及清理数据成功
四次挥手的具体步骤
第一步:客户端请求 断开连接
第二步:服务端回复 收到请求
第三步:服务端回复 你的数据已清空,可以断开连接
第四步:客户端回复 OK,那我就断开(正式断开)
类似情侣分手:
第一步:男孩向女孩提出分手
第二步:女孩回复:你好绝情啊
第三步:女孩把你的所有东西,扔之门外
第四步:男孩说:那我们就永远不再见吧
2.4.3 连接分类
长连接
短连接
建立连接之后,我们通讯一次之后,立马断开连接;下次如果需要再和你通讯,那么就需要建立新的连接
三、Socket通讯(面试的重灾区)
Socket 又被称为“套接字”。有一个层叫传输层,传输层的作用:将上三层的数据 和下三层的数据进行 转换
套接字:实际上就是咱们传输层用来传输数据的 一种载体 (通道)
3.1 Socket套接字编程
套接字编程中,核心类,都在java.net包下:
使用其中的哪些类取决于程序所需处理的通讯协议。例如,基于TCP的应用程序可使用 Socket、ServerSocket 等类;基于UDP的应用程序则使用DatagramPacket、DatagramSocket、MulticastSocket 等类;基于 HTTP 和 FTP 等协议直接访问 URL 资源的应用程序可使用 URL、URLConnection 等类。
3.2 TCP/IP协议-套接字编程
初级案例(1)-客户端向服务端发送消息
package com.woniuxy.java26.study.socket.tcp;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
/**
* TCP/IP协议的服务端(代码)
*
* @author Administrator
*
*/
public class SocketServerStudy {
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println("主线程启动!!!!!");
ServerSocket server = null;
try {
//创建Socket 的服务端,并指定8080 为通讯端口
server = new ServerSocket(8080);
//接收客户端(该方法是个阻塞式方法)
Socket socket = server.accept();
//输入流
InputStream in = socket.getInputStream();
//转换流
InputStreamReader isr = new InputStreamReader(in,"UTF-8");
//缓冲流
BufferedReader br = new BufferedReader(isr);
//边读边响应
String str = "";
while((str = br.readLine()) != null) {
System.out.println("服务器接收的数据是:" + str);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
package com.woniuxy.java26.study.socket.tcp;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.net.UnknownHostException;
public class SocketClientStudy {
public static void main(String[] args) {
// TODO Auto-generated method stub
//本机使用127.0.0.1 或者 localhost
Socket socket = null;
try {
socket = new Socket("127.0.0.1", 8080);
//建立传输流
OutputStream os = socket.getOutputStream();
OutputStreamWriter osw = new OutputStreamWriter(os, "UTF-8");
BufferedWriter bw = new BufferedWriter(osw);
//向服务端发送数据
bw.write("这是一个TCP/IP的测试信息!!!!");
bw.flush();
try {
Thread.sleep(200);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
bw.close();
osw.close();
os.close();
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
初级案例(2)-服务端支持多客户端
将socket.accept()方法,置于while(true){}
package com.woniuxy.java26.study.socket.tcp;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
/**
* TCP/IP协议的服务端(代码)
*
* @author Administrator
*
*/
public class SocketServerStudy {
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println("主线程启动!!!!!");
ServerSocket server = null;
try {
//创建Socket 的服务端,并指定8080 为通讯端口
server = new ServerSocket(8080);
while(true) {
//接收客户端(该方法是个阻塞式方法)
Socket socket = server.accept();
//输入流
InputStream in = socket.getInputStream();
//转换流
InputStreamReader isr = new InputStreamReader(in,"UTF-8");
//缓冲流
BufferedReader br = new BufferedReader(isr);
//边读边响应
String str = "";
while((str = br.readLine()) != null) {
System.out.println("服务器接收的数据是:" + str);
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
package com.woniuxy.java26.study.socket.tcp;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.net.UnknownHostException;
public class SocketClientStudy {
public static void main(String[] args) {
// TODO Auto-generated method stub
//本机使用127.0.0.1 或者 localhost
Socket socket = null;
try {
socket = new Socket("127.0.0.1", 8080);
//建立传输流
OutputStream os = socket.getOutputStream();
OutputStreamWriter osw = new OutputStreamWriter(os, "UTF-8");
BufferedWriter bw = new BufferedWriter(osw);
//向服务端发送数据
bw.write("这是一个TCP/IP的测试信息!!!!");
bw.flush();
try {
Thread.sleep(200);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
bw.close();
osw.close();
os.close();
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
3.3 Socket编程的步骤(面试题)
1、使用ServerSocket 类,监听某一个端口
2、使用socket.accept(),等待接收客户端的连接
3、使用Socket类,使用IP地址,端口号,创建客户端与服务端的连接
初级案例(3)-服务端向客户端发送文件
package com.woniuxy.java26.study.socket.tcp;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
/**
* TCP/IP协议的服务端(代码)
*
* @author Administrator
*
*/
public class SocketServerStudy {
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println("主线程启动!!!!!");
ServerSocket server = null;
try {
//创建Socket 的服务端,并指定8080 为通讯端口
server = new ServerSocket(8080);
while(true) {
//接收客户端(该方法是个阻塞式方法)
Socket socket = server.accept();
//建立流
OutputStream os = socket.getOutputStream();
OutputStreamWriter osw = new OutputStreamWriter(os, "UTF-8");
BufferedWriter bw = new BufferedWriter(osw);
//操作流
bw.write("服务器说:天王盖地虎!!!");
bw.newLine();
bw.write("服务器说:你脸红什么!!!");
bw.newLine();
bw.write("END");
bw.newLine();
bw.flush();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
package com.woniuxy.java26.study.socket.tcp;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Socket;
import java.net.UnknownHostException;
public class SocketClientStudy {
public static void main(String[] args) {
// TODO Auto-generated method stub
//本机使用127.0.0.1 或者 localhost
Socket socket = null;
try {
socket = new Socket("127.0.0.1", 8080);
InputStream in = socket.getInputStream();
InputStreamReader isr = new InputStreamReader(in, "UTF-8");
BufferedReader br = new BufferedReader(isr);
String str = "";
while(!(str = br.readLine()).equals("END")) {
System.out.println("客户端接收的消息是:" + str);
}
socket.close();
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
初级案例(4)-使用线程完成服务端与客户端的读写文件分离
package com.woniuxy.java26.study.socket.tcp;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ArrayBlockingQueue;
import com.woniuxy.java26.study.socket.handle.ServerReaderTask;
import com.woniuxy.java26.study.socket.handle.ServerWriterTask;
/**
* TCP/IP协议的服务端(代码)
*
* @author Administrator
*
*/
public class SocketServerStudy {
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println("主线程启动!!!!!");
ServerSocket server = null;
try {
// 创建Socket 的服务端,并指定8080 为通讯端口
server = new ServerSocket(8080);
while (true) {
// 接收客户端(该方法是个阻塞式方法)
Socket socket = server.accept();
//定义一个传输队列(FIFO)
ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<String>(1024);
// 读
Thread t1 = new Thread(new ServerReaderTask(socket,queue));
t1.start();
// 写
Thread t2 = new Thread(new ServerWriterTask(socket,queue));
t2.start();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
package com.woniuxy.java26.study.socket.handle;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Socket;
import java.net.SocketException;
import java.util.concurrent.ArrayBlockingQueue;
/**
* 服务端的 读取任务
*
* @author Administrator
*
*/
public class ServerReaderTask implements Runnable {
/**
* 代表连接通道
*/
private Socket socket;
/**
* 数据传输通道
*/
private ArrayBlockingQueue<String> queue;
public ServerReaderTask(Socket socket, ArrayBlockingQueue<String> queue) {
super();
this.socket = socket;
this.queue = queue;
}
@Override
public void run() {
// TODO Auto-generated method stub
// 输入流
InputStream in = null;
InputStreamReader isr = null;
BufferedReader br = null;
try {
if(!socket.isClosed() && socket.isConnected()) {
// 获得客户端的IP地址
String hostName = socket.getInetAddress().getHostName();
System.out.println(hostName);
in = socket.getInputStream();
// 转换流
isr = new InputStreamReader(in, "UTF-8");
// 缓冲流
br = new BufferedReader(isr);
// 读取消息
String str = "";
while (!(str = br.readLine()).equals("END")) {
System.out.println("服务器接收[" + hostName + "]的消息:" + str);
}
queue.add("[" + hostName + "] 的消息,我已收到!!!");
}
} catch (Exception e) {
// TODO Auto-generated catch block
if(e instanceof SocketException) {
System.out.println("该连接已被重置!!!!");
}
}
}
}
package com.woniuxy.java26.study.socket.handle;
import java.io.BufferedWriter;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.net.SocketException;
import java.util.concurrent.ArrayBlockingQueue;
/**
* 服务端的 写入任务
*
* @author Administrator
*
*/
public class ServerWriterTask implements Runnable {
/**
* 代表连接通道
*/
private Socket socket;
/**
* 数据传输通道
*/
private ArrayBlockingQueue<String> queue;
public ServerWriterTask(Socket socket, ArrayBlockingQueue<String> queue) {
super();
this.socket = socket;
this.queue = queue;
}
@Override
public void run() {
// TODO Auto-generated method stub
//建立传输流
OutputStream os = null;
OutputStreamWriter osw = null;
BufferedWriter bw = null;
try {
if(!socket.isClosed() && socket.isConnected()) {
os = socket.getOutputStream();
osw = new OutputStreamWriter(os, "UTF-8");
bw = new BufferedWriter(osw);
while(!queue.isEmpty()) {
bw.write(queue.poll());
bw.newLine();
}
bw.flush();
}
} catch (Exception e) {
// TODO Auto-generated catch block
if(e instanceof SocketException) {
System.out.println("该连接已被重置!!!!");
}
}
}
}
package com.woniuxy.java26.study.socket.tcp;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;
public class SocketClientStudy {
public static void main(String[] args) {
// TODO Auto-generated method stub
// 本机使用127.0.0.1 或者 localhost
Socket socket = null;
// 建立传输流
OutputStream os = null;
OutputStreamWriter osw = null;
BufferedWriter bw = null;
try {
socket = new Socket("127.0.0.1", 8080);
if (!socket.isClosed() && socket.isConnected()) {
os = socket.getOutputStream();
osw = new OutputStreamWriter(os, "UTF-8");
bw = new BufferedWriter(osw);
for(int i = 0; i < 10; i ++) {
System.out.println("客户端输出:" + i);
Thread.sleep(2000);
bw.write("长江,长江,我是黄河!!!!");
bw.newLine();
}
bw.write("END");
bw.newLine();
}
bw.flush();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if (!socket.isClosed() && socket.isConnected()) {
try {
bw.close();
osw.close();
os.close();
socket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
四、UDP协议(了解)
UDP协议,是一种端到端,但是不依赖连接的,而且通讯不安全的协议。
因为采用的是:只管发,不管有没有接收到 例如:发短信,发微信,发QQ信息
4.1 UDP协议的用法
TCP/IP:ServerSocket Socket ,那么UDP就依赖的是DatagramSocket,DatagramPacket
UDP: 服务端和客户端 DatagramSocket,DatagramPacket
package com.woniuxy.java27.study;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
public class UDPServerStudy {
public static void main(String[] args) {
// TODO Auto-generated method stub
try {
//数据缓冲区
byte[] data = new byte[10240];
//定义一个UDP服务端,监听的端口是9090
DatagramSocket server = new DatagramSocket(9090);
//定义用来接收数据的数据包对象
DatagramPacket packet = new DatagramPacket(data, data.length);
while(true) {
//读取数据到 缓冲区
server.receive(packet);
System.out.println("从:[" + packet.getSocketAddress() +"] 接收了数据!!!!!");
//输出客户端的 传递的信息
System.out.println("服务端接收的信息:" + new String(packet.getData(),"GBK"));
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
package com.woniuxy.java27.study;
import java.io.UnsupportedEncodingException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
public class UDPClientStudy {
public static void main(String[] args) {
// TODO Auto-generated method stub
String str = "给高老师:今晚吃鸡!!!";
//获得字符串的字节数组
try {
byte[] datas = str.getBytes("GBK");
//定义数据包(并指定数据表,需要发送的地址)
DatagramPacket packet = new DatagramPacket(datas,
datas.length, new InetSocketAddress("127.0.0.1", 9090));
//定义一个Socket通道
DatagramSocket socket = new DatagramSocket(5003);
//发送内容
socket.send(packet);
System.out.println("发送成功!!!!");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
4.2 面试题(Tcp/IP 与 UDP的区别)
1、TCP/IP过于依赖连接,UDP不是特别的依赖连接对象
2、TCP/IP在传输时,采用1问1答模式,它可以确保双向传输的数据,对方一定可以接收到,而UDP只管发,不在乎对方是否可以接收到
3、TCP/IP相对而言比较安全些,而UDP数据安全度较差