------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
1. 网络参考模型
>> OSI参考模型:
· 7应用层:主要是一些终端应用,比如说FTP,WEB,QQ之类(可以理解为 我们在电脑屏幕上看到的东西就是终端应用)
· 6表示层:主要是对接收的数据进行解释、加密与解密、压缩与解压等(也就是将计算机可以是别的数据转换为人可以识别的数据:图片,声音等)
· 5会话层:通过传输层建立数据传输的通路,主要在你的系统之间发起会话或者接受会话请求(设备之间需要互相认识,可以是IP地址、MAC地址、主机名)
· 4传输层:定义了一些传输数据的协议和端口号(端口号:传输端口与接收端口),如:TCP(传输控制协议,传输速率低,可靠性强,用于传输一些可靠性要求高,数据量大的数据),UDP(用户数据协议,最大传输64K数据,与TCP相反,如:QQ聊天的消息就是通过UDP来传输的)。主要将从下层接收的数据进行分段和传输,到达目的地地址后再进行重组。常把这一层的数据叫做段。
· 3网络层:主要将从下层接收到的数据(帧)进行IP地址的封装与解封,在这一层工作的设备是路由器(比如:要访问新浪网,就需要通过路由器来指引怎么能到达 新浪网)。常把这一层的数据称为 数据包
· 2数据链路层:主要将从物理层接收来的数据进行MAC地址(网卡地址,每块网卡在出厂时都会有一个全球唯一的地址)的封装与解封装,在这一层工作的设备是交换机,数据通过交换机来传输。这一层的数据通常称为 帧。
· 1物理层:主要定义物理设备标准,如:网线、光纤的接口类型,各种传输介质(无线、蓝牙、红外线)的传输速率等。它的主要作用是传输比特流 (就是先由本地的 1 0 转换为强弱电流,到达目的地后再转换为 1 0)。 这一层的数据叫做比特。
。举例: 有一个数据,从一端(发送端)发送到另一端(接收端):
[发送]
· 在发送端先通过<应用层>(如QQ)给数据封装一个标记,标记表示是哪个应用发送出去的数据;
· 经过<应用层>封装后到达了<表示层>,<表示层>对数据进行解释,判断出发送出去的是什么数据(图片、文字...),同时添加一层标记,记录数据类型;
· 之后到达了<会话层>,通过<会话层>指定了数据要发送给谁(MAC、IP、主机名)
· 之后到达了<传输层>,通过<传输层>来判断要通过什么协议(TCP、IP)来发送数据,经过哪个端口发送,同时添加标记,记录端口号与传输协议
· 之后到达了<网络层>,将从<传输层>接收到的数据进行IP地址的封装,封装的IP地址表示要发送给哪个IP地址
· 之后到达了<数据链路层>,将从<网络层>接收到的数据进行MAC地址的封装,封装的MAC地址表示要发送给哪个MAC地址
· 之后到达了<物理层>,将数据转换为 模拟信号(强弱电流),通过物理介质(网线、蓝牙...)将数据发送出去
[接收]
· 从发送端的物理介质(网线、蓝牙...)接收到了模拟信号,先将模拟信号转换为 1 0 数据
· 将转换后的数据从<数据链路层>到<应用层>一层一层的进行解析、解包,到达指定的应用,完成接收
>> TCP/IP参考模型:
· 4应用层:由OSI中的应用层、表示层、会话层这三层合并而成
· 3传输层:同OSI中的传输层一样
· 2网际层:同OSI中的网络层一样
· 1主机至网络层:由OSI中的数据链路层、物理层合并而成
2. DNS
。 DNS就是一个域名解析服务器,当要通过主机名访问一个网站时(如:www.sina.com.cn主机名,访问新浪),会通过本机设置的DNS地址对应的服务器来解析
主机名为www.sina.com.cn对应的IP地址,如果解析成功(根据主机名找到了对应的IP),那么可以访问该网站,如果没有找到对应的IP,则无法访问
一般解析主机名时都会先根据本地hosts文件解析,如果想屏蔽一个网站(如:www.baidu.com),可以在hosts文件中配置 127.0.0.1 www.baidu.com
这样,在本地访问www.baidu.com的时候就不会访问到百度了
。 IP地址对应的java类:InetAddress,可以根据一个给定的主机名或者IP地址来获取一个InetAddress对象,通过获取的对象可以得到主机名、IP地址
对应的主机的信息
3. Socket
。 Socket就是为网络服务提供的一种机制,通信的两端都有Socket,网络通信其实就是Socket通信,数据在两个Socket之间通过IO传输
4. UDP
。 对于 DatagramPacket 的构造方法:如果构造方法的参数中有 InetAddress 、 SocketAddress,则表示该构造方法用于创建要发送的数据包
如果没有这两项,则表示该构造方法用于创建要接收的数据包
<示例代码1:>
《发送端》
>> 创建UDP传输的发送端
1. 建立UDP的Socket服务(DatagramSocket)
2. 将要发送的数据封装在数据包中(DatagramPacket)
3. 通过UDP的Socket服务将数据包发送出去
4. 关闭Socket服务
package com.kingowe.upd;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
/**
* @author 孔超
* 本类是UDP传输数据的发送端
*
*/
public class UDPSend {
public static void main(String[] args) throws IOException{
System.out.println("发送端启动。。。。。");
/*
* DatagramSocket(int aPort)
* Constructs a UDP datagram socket which is bound to the specific port aPort on the localhost.
*/
DatagramSocket ds = new DatagramSocket(11111); // 【指定当前程序使用的端口号,不指定 编译器随机分配一个】
byte[] bs = new String("UDP测试,发送的数据!").getBytes();
/*【将要发送的数据封装在数据包中,并指定端口号,该端口号为接收端 接收数据的端口号】*/
DatagramPacket dp = new DatagramPacket(bs, bs.length, InetAddress.getByName("192.168.175.1"), 12345);
ds.send(dp); // 发送数据包
ds.close();
}
}
《接收端》
>> 创建UDP传输的接收端
1. 建立UDP的Socket服务(DatagramSocket),因为要接收数据,必须明确一个端口号
2. 创建数据包(DatagramPacket),用于存储接收到的数据(数据包对象自己知道怎么解析这些数据)。
3. 使用Socket服务的receive()方法将接收到的数据存储到数据包中
4. 通过数据包中的方法解析这些数据
5. 关闭资源
</pre><pre name="code" class="java">package com.kingowe.upd;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
/**
* @author 孔超
* 本类是UDP传输数据的接收端
*
*/
public class UDPReceive {
public static void main(String[] args)throws IOException {
System.out.println("接收端启动。。。。。");
DatagramSocket ds = new DatagramSocket(12345); // 【建立UDP的Socket服务,同时指定接收数据的端口号】
byte[] bs = new byte[1024];
// 【接收数据使用参数中没有InetAddress、SocketAddress的构造方法】
DatagramPacket dp = new DatagramPacket(bs, bs.length);
ds.receive(dp); // 该方法是阻塞式方法,如果没有接收到数据,程序会停在这里
String data = new String(dp.getData());
// 【数据包中封装的端口号,该端口号是发送端程序使用的端口号(注意与发送数据使用的端口号的区别)】
int port = dp.getPort();
InetAddress iaddress = dp.getAddress();
String hostName = iaddress.getHostName();
String hostAddress = iaddress.getHostAddress();
System.out.println("data:" + data);
System.out.println("port:" + port);
System.out.println("hostName:" + hostName);
System.out.println("hostAddress:" + hostAddress);
ds.close();
}
}
/*
* 输出结果:
*
* data:UDP测试,发送的数据!
* port:11111
* hostName:PC201411121725
* hostAddress:192.168.175.1
*/
5. TCP/IP
。 【注意】在客户端或服务器端读取数据时,如果缺少结束标记,程序就会死锁,如:在服务器端使用 BufferedReader 中的 readLine() 方法读取 客户端的OutputStream时,会读取一整行数据,而一行数据的结束标记为 \r\n , 当在客户端发送数据时 如果使用的是 print() 之类的方法,没有传递结束标记,那么在服务器端会一直读取,无法进行下一步操作
>> 创建TCP传输的客户端
<示例代码(上传文本到服务器):>
package com.kingowe.tcpupload;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
public class UploadClient {
public static void main(String[] args)throws IOException {
Socket socket = new Socket("192.168.175.1", 10010);
OutputStream os = socket.getOutputStream();
PrintWriter out = new PrintWriter(os, true);
/* 【这里第二个参数 true 表示的自动刷新,必须要刷新缓存,可以创建PrintWriter
时使用这种方式指定自动刷新,也可以每次写完一条数据之后使用 out.flush() 手动刷新】 */
BufferedReader br = new BufferedReader(new FileReader("c:\\client.txt"));
String line = "";
while((line=br.readLine()) != null){
out.println(line);
}
// //out.println("over"); // 传统方式,自己指定一个结束标记,当服务器端读到该标记时,表示文件结束,终止读取
socket.shutdownOutput(); // 【【为socket流添加结束标记,如果不添加结束标记,在服务器端就会一直读取】】
InputStream is = socket.getInputStream();
BufferedReader read = new BufferedReader(new InputStreamReader(is));
String result = read.readLine(); // 读取服务器返回的数据
System.out.println(result);
br.close();
socket.close();
}
}
>> 创建TCP传输的服务端
<示例代码(上传文本到服务器):>
package com.kingowe.tcpupload;
import java.io.BufferedReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class UploadServer {
public static void main(String[] args)throws IOException {
ServerSocket ss = new ServerSocket(10010);
Socket s = ss.accept(); // 取得客户端连接进来的Socket对象
InputStream is = s.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
PrintWriter out = new PrintWriter(new FileWriter("c:\\server.txt")); // 【<1>】
String line = "";
while((line=br.readLine()) != null){
// //if(line.equals("over")){
// // break;
// //}
// //out.write(line);
// 【使用 write()方法,为了不让格式乱掉,需要读取一行在写入时写入一个换行】
// //out.newLine();
out.println(line);
}
out.flush(); //【 手动刷新,也可以在 <1> 处创建 PrintWriter 时,指定第二个参数为true(表示自动刷新)】
OutputStream os = s.getOutputStream();
PrintWriter pw = new PrintWriter(new OutputStreamWriter(os));
pw.println("上传完毕!!!");
pw.flush();
/* 【如果创建该 PrintWriter时没有添加第二个参数 true ,则这里必须手动刷新,否则上一句的
<span style="white-space:pre"> </span>输出流会保存在PrintWriter中,不会刷新到socket流中,指定自动刷新参数:
new PrintWriter(new OutputStreamWriter(os), true);】 */
out.close();
s.close();
ss.close();
}
}
。【 以上代码注意点最重要的就三点】:
1. 输入、输出流的一行的结束标记(换行)
2. 刷新缓冲区
3. 客户端文件上传完成后的结束标记
<示例代码(上传图片到服务器):>
《客户端》
package com.kingowe.tcppicupload;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
/**
* 上传图片到服务器程序的客户端
* @author Administrator
*
*/
public class PicClient {
public static void main(String[] args)throws IOException {
Socket s = new Socket("192.168.175.1", 10011);
FileInputStream fis = new FileInputStream("C:\\01.jpg"); // 源文件流
OutputStream os = s.getOutputStream();// 客户端的输出流,用于向服务器端输出数据
byte[] b = new byte[1024];
int len = 0;
while((len=fis.read(b)) != -1){
os.write(b, 0, len);
}
os.flush();
s.shutdownOutput(); // 【记得写关闭标记】
InputStream is = s.getInputStream(); // 客户端的输入流,用于读取服务器端返回的数据
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String message = br.readLine();
System.out.println(message);
System.out.println("OK");
fis.close();
s.close();
}
}
《服务器端》
package com.kingowe.tcppicupload;
import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
/**
* 上传图片到服务器程序的服务器端
* @author Administrator
*/
public class PicServer {
public static void main(String[] args) throws IOException{
ServerSocket ss = new ServerSocket(10011);
Socket s = ss.accept();
InputStream is = s.getInputStream(); // 服务器端的输入流,用于接收客户端传入的数据
FileOutputStream fos = new FileOutputStream("C:\\00.jpg");
byte[] b = new byte[1024];
int len = 0;
while((len=is.read(b)) != -1){
fos.write(b, 0, len);
}
fos.flush();
OutputStream os = s.getOutputStream();
BufferedWriter pw = new BufferedWriter(new OutputStreamWriter(os));
pw.write("上传完毕!!!");
pw.newLine(); // 【记得写入换行】
pw.flush();
fos.close();
s.close();
ss.close();
}
}
>> 以上程序改进:多客户端上传文件到服务器(使用线程)
《服务器端》
package com.kingowe.multithreadupload;
import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
/**
* 上传图片到服务器程序的服务器端
* @author Administrator
*/
public class PicServer {
private static ServerSocket ss;
public static void main(String[] args) throws IOException{
ss = new ServerSocket(10011);
int num = 1;
while(true){
Socket s = ss.accept(); // 因为该方法为阻塞式的方法,所以可以while(true)
new Thread(new MyTask(s, num++)).start();
// 【这里,启动一个线程处理服务器端连接进来的客户端】
}
}
}
class MyTask implements Runnable{
private Socket s;
private int num;
public MyTask(Socket s, int num){
this.s = s;
this.num = num;
}
@Override
public void run() { // 【在线程中像单客户端一样处理】
try{
InputStream is = s.getInputStream(); // 服务器端的输入流,用于接收客户端传入的数据
FileOutputStream fos = new FileOutputStream("C:\\0" + num + ".zip");
byte[] b = new byte[1024];
int len = 0;
while((len=is.read(b)) != -1){
fos.write(b, 0, len);
}
fos.flush();
OutputStream os = s.getOutputStream();
BufferedWriter pw = new BufferedWriter(new OutputStreamWriter(os));
pw.write("上传完毕!!!");
pw.newLine();
pw.flush();
fos.close();
}catch(IOException e){
e.printStackTrace();
}finally{
try {
s.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}