21 网络编程
21.1 网络参考模型
- 找到对方IP;
- 数据要发送到对方指定的应用程序上,为标识这些应用程序,故应用数字进行标识,称为端口,逻辑端口;
- 定义通信规则,称为协议,国际组织定义了通用协议TCP/IP。
OSI(Open System Interconnection 开放系统互连)
参考模型TCP/IP 参考模型
七层描述
- 物理层:主要定义物理设备标准,如网线的接口类型、光纤的接口类型、各种传输介质的传输速率等。它的主要作用是传输比特流(就是由1、0转化为电流强弱来进行传输,到达目的地后再转化为1、0,也就是我们常说的数模转换与模数转换)。这一层的数据叫做比特。
- 数据链路层:主要将从物理层接收的数据进行MAC地址(网卡的地址)的封装与解封装。常把这一层的数据叫做帧。在这一层工作的设备是交换机,数据通过交换机来传输。
- 网络层:主要将下层接收到的数据进行IP地址(例,192.168.0.1)的封装与解封装。在这一层工作的设备是路由器,常把这一层的数据叫做数据包。
- 传输层:定义了一些传输数据的协议和端口号(WWW端口号80等)如:TCP(传输控制协议,传输效率低,可靠性强,用于传输可靠性要求高,数据量大的数据),UDP(用户数据报协议,与TCP特性恰恰相反,用于传输可靠性要求不高,数据量小的数据,如QQ聊天数据就是通过这种方式传输的)。主要是将从下层接收的数据进行分段和传输,到达目的地址后再进行重组。常常把这一层叫做段。
- 会话层:通过传输层(端口号:传输端口与接收端口)建立数据传输的通路。主要在你的系统之间发起会话或者接收会话请求(设备之间需要互相认识可以是IP也可以是MAC或者是主机名)。
- 表示层:主要是进行对接收的数据进行解释,加密与解密、压缩与解压缩等(也就是把计算机能够识别的东西转换成人能够识别的东西(如图片、声音等)。
- 应用层:主要是一些终端的应用,比如说FTP(各种文件下载)、WEB(IE浏览)、QQ之类的(可以把它理解成我们在电脑屏幕上可以看到的东西,就是终端应用)。
P.S.
- 每个网卡的MAC地址都是全球唯一的。
- 路由器实现将数据包发送到指定的地点。
- 应用软件之间通信的过程就是层与层之间封包、解封包的过程。
- OSI参考模型虽然设计精细,但过于麻烦,效率不高,因此才产生了简化版的TCP/IP参考模型。
21.2 网络通信要素
1、IP地址
- 网络中设备的标识;
- 不易记忆,可用主机名;
- 本地回环地址:127.0.0.1 主机名:localhost。
Ps:
- 在没有连接互联网的情况,为了让访问本机方便,所以分配了一个默认的IP地址,也就是本地回环地址。
- 通过ping 127.0.0.1可以测试网络是不是通,如果不通,可能是网卡出问题了。
2、 端口号
用于标识进程(应用程序)的逻辑地址,不同进程的标识。
有效端口:0~65535,其中0~1024系统使用或保留端口
Ps:
- 当一台计算机A向另一台计算机B发送QQ信息时,首先路由器通过数据包中的IP地址定位该信息发送到哪一台机器。然后计算机B接收到数据包后,通过此数据包中的端口号定位到发送给本机的QQ应用程序。
- 所谓防火墙,其功能就是将发送到某程序端口的数据屏蔽掉以及将从该程序端口发出的数据也屏蔽掉。
3、传输协议
UDP
- 将数据及源和目的封装成数据包中,不需要建立连接。
- 每个数据报的大小在限制在64k内。
- 因无连接,是不可靠协议。
- 不需要建立连接,速度快。
应用案例:QQ聊天、在线视频用的都是UDP传输协议。
TCP
- 建立连接,形成传输数据的通道。
- 在连接中进行大数据量传输。
- 通过三次握手完成连接,是可靠协议。
- 必须建立连接,效率会稍低。
应用案例:FTP,File Transfer Protocol(文件传输协议)。
import java.net.InetAddress;
public class IPDemo {
public static void main(String[] args) throws Exception {
InetAddress id = InetAddress.getLocalHost();
System.out.println(id.getHostAddress()+id.getHostName());
InetAddress ia = InetAddress.getByName("www.baidu.com");
System.out.println(ia.getHostAddress()+ia.getHostName());
}
}
运行结果:
21.3 udp
- Socket是为网络服务提供的一种机制;
- 通信的两端都有Socket;
- 网络通信即Socket间的通信;
- 数据在两个Socket间通过IO传输。
例1:
发送端:
import java.io.IOException;
import java.net.*;
/*
* 需求:通过udp传输方式,将一段数据发送出去;
* 思路:
* 1、建立updsocket服务;
* 2、提供数据,并将数据封装到数据包中;
* 3、通过Socket服务的发送功能将数据包发送出去;
*/
public class UdpDemo {
public static void main(String[] args) throws IOException {
//创建udp服务,通过DatagramSocket对象
DatagramSocket ds = new DatagramSocket(8888);
//确定数据,并封装成数据包
byte[] buf = "A man can be destroyed but not defeated.".getBytes();
DatagramPacket dp = new DatagramPacket(buf,buf.length,InetAddress.getByName("192.168.1.104"),1000);
//通过Socket服务奖已有数据包发送出去
ds.send(dp);
//关闭资源
ds.close();
}
}
接收端:
import java.io.IOException;
import java.net.*;
/*
* 需求:通过udp传输方式,接收udp协议传输的数据并处理;
* 思路:
* 1、建立updsocket服务;
* 2、定义一个数据包,因为要存储接收到的字节数据;
* 3、通过Socket服务的receive方法将接收到的数据存入已定义好的数据包中;
* 4、通过数据包对象的特有功能,将这些不同的数据取出,打印在控制台。
*/
public class UdpRec1 {
public static void main(String[] args) throws IOException {
DatagramSocket ds = new DatagramSocket(1000);
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket(buf,buf.length);
ds.receive(dp);
String ip = dp.getAddress().getHostAddress();
String data =new String(dp.getData(),0,dp.getLength());
int port = dp.getPort();
System.out.println(ip+":"+data+":"+port);
ds.close();
}
}
运行发送端后运行接收端结果:192.168.1.104:A man can be destroyed but not defeated.:8888
例1:
客户端:
1、建立socket服务,指定要连接主机和端口;
2、获取socket流中的输出流,将数据写到该流中,通过网络发送给服务端。
3、获取socekt流中的输入流,将服务器反馈的数据获取,打印;
4、关闭客户端资源。
import java.io.*;
import java.net.*;
public class TcpClient {
public static void main(String[] args) throws IOException {
//创建客户端的socket服务,指定目的主机和端口
Socket s = new Socket("192.168.1.104",10003);
//为发送数据,应该获取socket流中的输出流
OutputStream out = s.getOutputStream();
out.write("A man can be destroyed but not defeated.".getBytes());
InputStream in = s.getInputStream();
byte[] buf = new byte[1024];
int len = in.read(buf);
System.out.println(new String(buf,0,len));
s.close();
}
}
服务端:
1、建立服务端的socket服务,ServerSocket();并监听一个端口;
2、获取连接过来的客户端对象。
通过ServerSocket的accept方法,没有连接就会等,所以这个方法为阻塞式的。
3、客户端如果发过来数据,那么客户端要使用对应的客户端对 象,并获取到该对象的读取流读取发来的数据。
4、给客户端反馈信息。
5、关闭服务器。
import java.io.*;
import java.io.OutputStream;
import java.net.*;
public class TcpServer {
public static void main(String[] args) throws IOException, InterruptedException {
//建立服务端socket服务,并监听一个端口
ServerSocket ss = new ServerSocket(10003);
//通过accept方法获取连接过来的客户端对象
Socket s = ss.accept();
String ip=s.getInetAddress().getHostAddress();
System.out.println(ip+"...connected");
//获取客户端发送过来的数据,使用客户端对象的读取流读取对象
InputStream in = s.getInputStream();
byte[] buf = new byte[1024];
int len = in.read(buf);
System.out.println(new String(buf,0,len));
OutputStream out = s.getOutputStream();
Thread.sleep(3000);
out.write("U r the man!".getBytes());
s.close();
ss.close();
}
}
运行结果:
服务端显示
192.168.1.104…connected
A man can be destroyed but not defeated.
3秒后客户端显示U r the man!
例2:
客户端:
import java.io.*;
import java.net.Socket;
import java.net.UnknownHostException;
public class TransClient {
/**
* 需求:建立一个文本转换服务器。客户端给服务器发动文本,
* 服务端将文本转成大写返回客户端。
* 而且客户端可不断的进行文本转换,当客户端输入over时转换结束
*
*分析:
*客户端:既然为操作设备数据,则可用io技术。
*源:键盘输入、
*目的:网络设备,网络输出流。操作为文本数据,可以选择字符流
*
*步骤:
*1、建立服务。
*2、获取键盘录入。
*3、将数据发给客户端。
*4、后去服务端返回大写数据。
*5、结束,关闭资源.
* @throws IOException
* @throws UnknownHostException
*/
public static void main(String[] args) throws IOException {
Socket s =new Socket("192.168.1.101",10004);
//定义读取键盘数据的流对象
BufferedReader bufr =
new BufferedReader(new InputStreamReader(System.in));
/*//定义目的,将数据写入到socket输出流,发给服务端
BufferedWriter bufOut =
new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));*/
PrintWriter out = new PrintWriter(s.getOutputStream(),true);
//定义一个socket读取流,读取服务端返回的大写信息
BufferedReader bufIn =
new BufferedReader(new InputStreamReader(s.getInputStream()));
String line = null;
while((line=bufr.readLine())!=null){
if("over".equals(line))
break;
//由于readLine()方法必须要接收到一个换行符才能读取一行,
//因此采用PrintWriter的println方法一条语句就可满足
out.println(line);
/* bufOut.write(line);
bufOut.newLine();
bufOut.flush();*/
String str = bufIn.readLine();
System.out.print("Server:"+str);
}
bufr.close();
s.close();
}
}
服务器端:
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class TransServer {
/**
* 服务端:
* 源:Socket读取流
* 目的:socket输出流
*/
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(10004);
Socket s = ss.accept();
String ip = s.getInetAddress().getHostAddress();
System.out.println(ip+"...connected");
//读取流中数据
BufferedReader bufIn =
new BufferedReader(new InputStreamReader(s.getInputStream()));
/*//将大写数据写入Socket输出流,并发给用户端
BufferedWriter bufOut =
new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));*/
PrintWriter out = new PrintWriter(s.getOutputStream(),true);
String line= null;
while((line=bufIn.readLine())!=null){
System.out.println(line);
out.println(line.toUpperCase());
/* bufOut.write(line.toUpperCase());
bufOut.newLine();
bufOut.flush();*/
}
s.close();
ss.close();
}
}
运行结果:
客户端输入“gabriel”,弹出”Server:GABRIEL”
例3:
客户端:
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
public class TextClient {
/**
* @param args
* @throws IOException
* @throws UnknownHostException
*/
public static void main(String[] args) throws IOException {
Socket s = new Socket("192.168.1.101",10006);
BufferedReader bufr =
new BufferedReader(new FileReader("IPDemo.txt"));
PrintWriter out = new PrintWriter(s.getOutputStream(),true);
String line = null;
while((line = bufr.readLine())!=null){
out.println(line);
}
BufferedReader bufIn = new BufferedReader(new InputStreamReader(s.getInputStream()));
String str = bufIn.readLine();
System.out.println(str);
bufr.close();
s.close();
}
}
服务器端:
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class TextServer {
/**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
ServerSocket ss= new ServerSocket(10006);
Socket s = ss.accept();
String ip=s.getInetAddress().getHostAddress();
System.out.println(ip+"...connected");
BufferedReader bufIn = new BufferedReader(new InputStreamReader(s.getInputStream()));
PrintWriter out = new PrintWriter(new FileWriter("server.txt"),true);
String line = null;
while((line=bufIn.readLine())!=null){
out.println(line);
}
PrintWriter pw = new PrintWriter(s.getOutputStream(),true);
pw.println("上传成功");
out.close();
s.close();
ss.close();
}
}
运行结果:
在该工程目录下放一个IPDemo.txt的文件,运行后,会复制出一个内容一致的server.txt文件