一、网络模型
OSI(Open System Interconnection 开放系统互连)参考模型
物理层:主要定义物理设备标准,如网线的接口类型、光纤的接口类型、各种传输介质的传输速率等。它的主要作用是传输比特流(就是由1、0转化为电流强弱来进行传输,到达目的地后再转化为1、0,也就是我们常说的数模转换与模数转换)。这一层的数据叫做比特。
数据链路层:主要将从物理层接收的数据进行MAC地址(网卡的地址)的封装与解封装。常把这一层的数据叫做帧。在这一层工作的设备是交换机,数据通过交换机来传输。
网络层:主要将下层接收到的数据进行IP地址的封装与解封装。在这一层工作的设备是路由器,常把这一层的数据叫做数据包。
传输层:定义了一些传输数据的协议和端口号(WWW端口号80等),如:TCP(传输控制协议),UDP(用户数据报协议)。主要是将从下层接收的数据进行分段和传输,到达目的地址后再进行重组。常常把这一层叫做段。
会话层:通过传输层(端口号:传输端口与接收端口)建立数据传输的通路。主要在你的系统之间发起会话或者接收会话请求(设备之间需要互相认识可以是IP也可以是MAC或者是主机名)。
表示层:主要是进行对接收的数据进行解释,加密与解密、压缩与解压缩等(也就是把计算机能够识别的东西转换成人能够识别的东西(如图片、声音等)。
应用层:主要是一些终端的应用,比如说FTP(各种文件下载)、WEB(IE浏览)、QQ之类的。
二、网络通信要素
1.IP地址(InetAddress)
网络中设备的标识。
在没有连接互联网的情况,为了让访问本机方便,所以分配了一个默认的IP地址,也就是本地回环地址。本地回环地址:127.0.0.1 主机名:localhost。通过ping 127.0.0.1可以测试网络是否通畅。
import java.net.InetAddress;
import java.net.UnknownHostException;
public class IPDemo {
public static void main(String[] args) throws UnknownHostException{
//获取本地主机IP地址对象
InetAddress ip = InetAddress.getLocalHost();
System.out.println(ip.getHostAddress());
System.out.println(ip.getHostName());
System.out.println("---------------");
//获取其他主机的IP地址对象
ip = InetAddress.getByName("www.baidu.com");
System.out.println(ip.getHostAddress());
System.out.println(ip.getHostName());
}
}
InetAddress类中有一个静态方法: static InetAddress[] getAllByName(String host), 此方法是在给定主机名的情况下,根据系统上配置的名称服务返回其IP地址所组成的数据。这是由于有些主机名对应的 IP地址不唯一,如新浪、百度,都是服务器集群。
2.端口号
用于标识进程(应用程序)的逻辑地址,不同进程的标识。
有效端口:065535,其中01024系统使用或保留端口。
所谓防火墙,其功能就是将发送到某程序端口的数据屏蔽掉以及将从该程序端口发出的数据也屏蔽掉。
3.传输协议
TCP传输数据的过程:
实现底层网络通信
Socket类
客户端要与服务端相连,则客户端需要建立Socket对象。
Socket s = new Socket(“机器名或Ip地址”, 端口号)
然后使用流来进行通信
ServerSocket类
需要调用accept方法接受客户呼叫
得到的又是一个Socket, 它可以与客户进行通信
- UDP连接
/*
* UDP发送端
* 1.建立UDP的socket服务
* 2.将要发送的数据封装到数据包中
* 3.通过UDP的socket服务将数据包发送出去
* 4.关闭socket服务
*/
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class UDPSendDemo {
public static void main(String[] args) throws Exception{
System.out.println("Send start");
//1.UDP socket服务,使用DatagramSocket对象
//如果发送端端口未指定,就会随机分配未被使用的端口
DatagramSocket ds = new DatagramSocket(8888);
//将要发送的数据封装到数据包中
String str = "I am Server!";
//使用DatagramPacket将数据封装到改对象包中
byte[] buf = str.getBytes();
DatagramPacket dp = new DatagramPacket(buf, buf.length, InetAddress.getByName("192.168.1.254"),10000);
//通过UDP的socket服务将数据包发送出去
ds.send(dp);
//关闭资源
ds.close();
}
}
/*
* UDP接收端
* 1.建立UDP的socket服务,因为是接收数据,必须要明确一个端口号
* 2.创建数据包,用于存储接收到的数据,方便用数据包对象的方法解析这些数据
* 3.使用socket服务的receive方法将接收的数据存储到数据包中
* 4.通过数据包的方法解析数据包中的数据
* 5.关闭资源
*/
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class UDPReciDemo {
public static void main(String[] args) throws Exception{
System.out.println("Receive start");
//1.建立UDP socket服务
DatagramSocket ds = new DatagramSocket(10000);
//2.创建数据包
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket(buf,buf.length);
//3.使用接收方法将数据存储到数据包中
ds.receive(dp); //阻塞式的
//4.通过数据包对象的方法,解析其中的数据,比如:地址,端口,数据内容
String ip = dp.getAddress().getHostAddress();
//获取的端口号是发送端的端口号
int port = dp.getPort();
String text = new String(dp.getData(),0,dp.getLength());
System.out.println(ip+":"+port+":"+text);
//关闭资源
ds.close();
}
}
聊天程序(双窗口模式)
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class UDPSend {
public static void main(String[] args) throws Exception{
System.out.println("Send start");
DatagramSocket ds = new DatagramSocket(8080);
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
String line = null;
while((line = bufr.readLine()) != null){
byte[] buf = line.getBytes();
DatagramPacket dp = new
DatagramPacket(buf, buf.length, InetAddress.getByName("192.168.1.100"),10000);
ds.send(dp);
if("886".equals(line))
break;
}
ds.close();
}
}
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class UDPRece {
public static void main(String[] args) throws Exception{
System.out.println("Receive run");
DatagramSocket ds = new DatagramSocket(10800);
while(true){
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket(buf, buf.length);
ds.receive(dp);
String ip = dp.getAddress().getHostAddress();
int port = dp.getPort();
String text = new String(dp.getData(),0,dp.getLength());
System.out.println(ip+":"+port+":"+text);
}
}
}
聊天程序(单窗口模式——群聊)
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class Send implements Runnable{
private DatagramSocket ds;
public Send(DatagramSocket ds){
this.ds = ds;
}
public void run(){
try{
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
String line = null;
while((line = bufr.readLine()) != null){
byte[] buf = line.getBytes();
DatagramPacket dp = new
DatagramPacket(buf, buf.length, InetAddress.getByName("192.168.1.255"),10001);
ds.send(dp);
if("886".equals(line))
break;
}
ds.close();
}catch(Exception e){
e.printStackTrace();
}
}
}
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class Rece implements Runnable{
private DatagramSocket ds;
public Rece(DatagramSocket ds){
this.ds = ds;
}
public void run(){
try{
while(true){
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket(buf, buf.length);
ds.receive(dp);
String ip = dp.getAddress().getHostAddress();
int port = dp.getPort();
String text = new String(dp.getData(),0,dp.getLength());
System.out.println(ip+":"+port+":"+text);
if(text.equals("886"))
System.out.println(ip+" exit");
}
}catch(Exception e){
e.printStackTrace();
}
}
}
import java.io.IOException;
import java.net.DatagramSocket;
public class ChatDemo {
public static void main(String[] args) throws IOException{
DatagramSocket send = new DatagramSocket();
DatagramSocket rece = new DatagramSocket(10001);
Send s = new Send(send);
Rece r = new Rece(rece);
new Thread(s).start();
new Thread(r).start();
}
}
- TCP连接
/*
* 客户端发数据到服务端
* 1.创建TCP客户端Socket服务,使用Socket对象。
* 2.如果连接建立成功,说明数据传输通道已建立。
* 可以通过getOutputStream(),和getInputStream()来获取两个字节流。
* 3.使用输出流,将数据写入。
* 4.关闭资源
*/
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
public class ClientDemo {
public static void main(String[] args) throws UnknownHostException, IOException{
//创建客户端Socket服务
Socket socket = new Socket("192.168.1.100", 10002);
//获取socket流中的输出流
OutputStream out = socket.getOutputStream();
//使用输出流将指定的数据写出去
out.write("tcp demo".getBytes());
/*读取客户端返回的数据,使用Socket读取流
Input in = socket.getInputStream();
byte[] buf= new byte[1024];
int len = in.read(buf);
String text = new String(buf, 0, len);
System.out.println(text);
*/
//断开连接,关闭资源,socket获取的输出流也被关闭
socket.close();
}
}
/*
* 服务端接收客户端发送过来的数据,并打印到控制台上
* 1.创建服务端socket服务,通过SeverSocket对象
* 服务端必须对外提供一个端口,否则客户端无法连接
* 2.获取连接过来的客户端对象
* 3.通过客户端对象获取socket流读取客户端发来的数据,并打印
* 4.关闭资源,关客户端,关服务端
*/
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerDemo {
public static void main(String[] args) throws IOException{
//创建服务端对象
ServerSocket ss = new ServerSocket(10002);
//获取连接过来的客户端对象
Socket s = ss.accept(); //阻塞式
String ip = s.getInetAddress().getHostAddress();
//通过socket对象获取输入流,要读取客户端发来的数据
InputStream in = s.getInputStream();
byte[] buf = new byte[1024];
int len = in.read(buf);
String text = new String(buf, 0, len);
System.out.println(ip+":"+text);
/*使用客户端socket对象的输出流给客户端返回数据
OutputStream out = s.getOutputStream();
out.write("Received".getBytes());
*/
s.close();
ss.close();
}
}
实例一:文本转换TCP客户端和服务端
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
public class TransClient {
public static void main(String[] args) throws UnknownHostException, IOException{
//创建socket客户端对象
Socket s = new Socket("192.168.1.100",10004);
//获取键盘录入
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
//socket输出流
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;
out.println(line);
//读取服务器发回的一行大写数据
String upperStr = bufIn.readLine();
System.out.println(upperStr);
}
s.close();
}
}
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class TransServer {
public static void main(String[] args) throws IOException{
//创建ServerSocket对象
ServerSocket ss = new ServerSocket(10004);
//获取socket对象
Socket s = ss.accept();
//获取ip
String ip = s.getInetAddress().getHostAddress();
System.out.println(ip+"...connected");
//获取socket读取流,并装饰
BufferedReader bufIn = new BufferedReader(new
InputStreamReader(s.getInputStream()));
//获取socket的输出流,并装饰
PrintWriter out = new PrintWriter(s.getOutputStream(), true);
String line = null;
while((line = bufIn.readLine()) != null){
System.out.println(line);
out.println(line.toUpperCase());
}
s.close();
}
}
实例二:TCP协议上传文本文件
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
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(10005);
Socket s = ss.accept();
System.out.println(s.getInetAddress().getHostAddress()+"...connected");
BufferedReader bufIn = new BufferedReader(new
InputStreamReader(s.getInputStream()));
BufferedWriter bufw = new BufferedWriter(new
FileWriter("d:\\demo\\server.txt"));
String line = null;
while((line = bufIn.readLine()) != null){
bufw.write(line);
bufw.newLine();
}
PrintWriter out = new PrintWriter(s.getOutputStream(),true);
out.println("Upload sucessfully!");
bufw.close();
s.close();
ss.close();
}
}
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 UploadClient {
public static void main(String[] args) throws
UnknownHostException, IOException{
Socket s = new Socket("192.168.1.100",10005);
BufferedReader bufr = new BufferedReader(new
FileReader("d:\\demo\\client.txt"));
PrintWriter out = new PrintWriter(s.getOutputStream(),true);
String line = null;
while((line = bufr.readLine()) != null){
out.println(line);
}
//告诉服务器,客户端写完了
s.shutdownOutput();
BufferedReader bufIn = new BufferedReader(new
InputStreamReader(s.getInputStream()));
String str = bufIn.readLine();
System.out.println(str);
bufr.close();
s.close();
}
}
实例三:TCP协议上传图片客户端和服务端
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class UploadPicServer {
public static void main(String[] args) throws
FileNotFoundException, IOException{
//创建TCP的socket服务端
ServerSocket ss = new ServerSocket(10006);
//获取客户端
Socket s = ss.accept();
String ip = s.getInetAddress().getHostAddress();
System.out.println(ip+"...connected");
//读取客户端发来的信息
InputStream in = s.getInputStream();
//将读取到的数据存储到一个文件中
File dir = new File("c:\\pic");
if(!dir.exists()){
dir.mkdirs();
}
File file = new File(dir, ip+".bmp");
FileOutputStream fos = new FileOutputStream(file);
byte[] buf = new byte[1024];
int len = 0;
while((len = in.read(buf)) != -1){
fos.write(buf, 0, len);
}
//获取socket输出流,将上传成功字样发给客户端
OutputStream out = s.getOutputStream();
out.write("Upload sucessfully!".getBytes());
fos.close();
s.close();
ss.close();
}
}
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
public class UploadPicClient {
public static void main(String[] args) throws
UnknownHostException, IOException, FileNotFoundException{
//创建客户端socket
Socket s = new Socket("192.168.1.100", 10006);
//读取客户端要上传的图片文件
FileInputStream fis = new FileInputStream("c:\\0.bmp");
//获取socket输出流,将读到图片数据发送给服务端
OutputStream out = s.getOutputStream();
byte[] buf = new byte[1024];
int len = 0;
while((len = fis.read(buf)) != -1){
out.write(buf, 0, len);
}
//告诉服务端数据发送完毕,让服务端停止读取
s.shutdownOutput();
//读取服务端发回的内容
InputStream in = s.getInputStream();
byte[] bufIn = new byte[1024];
int lenIn = in.read(bufIn);
String text = new String(bufIn, 0, lenIn);
System.out.println(text);
fis.close();
s.close();
}
}
实例四:TCP协议服务端多线程技术
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
public class UploadTask implements Runnable{
private Socket s;
public UploadTask(Socket s){
this.s = s;
}
public void run(){
int count = 0;
String ip = s.getInetAddress().getHostAddress();
System.out.println(ip+"'...connected");
try{
InputStream in = s.getInputStream();
File dir = new File("c:\\pic");
if(!dir.exists()){
dir.mkdirs();
}
File file = new File(dir,ip+".bmp");
while(file.exists()){
file = new File(dir,ip+"("+(++count)+").bmp");
}
FileOutputStream fos = new FileOutputStream(file);
byte[] buf = new byte[1024];
int len = 0;
while((len = in.read(buf)) != -1){
fos.write(buf, 0, len);
}
OutputStream out = s.getOutputStream();
out.write("Upload sucessfully!".getBytes());
fos.close();
s.close();
}catch(Exception e){
e.printStackTrace();
}
}
}
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class UploadPicServerDemo {
public static void main(String[] args) throws
FileNotFoundException, IOException{
ServerSocket ss = new ServerSocket(10006);
while(true){
Socket s = ss.accept();
new Thread(new UploadTask(s)).start();
}
}
}
三、客户端和服务端原理
最常见的客户端:浏览器,IE/chrome
最常见的服务端:服务器,Tomcat
1.自定义服务端
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class MyTomcat {
public static void main(String[] args) throws IOException{
ServerSocket ss = new ServerSocket(9090);
Socket s = ss.accept();
System.out.println(s.getInetAddress().getHostAddress()+"...connected");
InputStream in = s.getInputStream();
byte[] buf = new byte[1024];
int len = in.read(buf);
String text = new String(buf, 0, len);
System.out.println(text);
//给客户端一个反馈信息
PrintWriter out = new PrintWriter(s.getOutputStream(),true);
out.println("<font color = 'red' size = '7'>Welcome</font>");
s.close();
ss.close();
}
}
2.模拟一个浏览器获取信息
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.net.Socket;
public class MyBrowser {
public static void main(String[] args) throws IOException{
Socket s = new Socket("192.168.1.100", 8080);
//模拟浏览器,向tomcat服务器发送符号http协议的请求信息
PrintWriter out = new PrintWriter(s.getOutputStream(),true);
out.println("GET /myweb/1.html HTTP/1.1");
out.println("Accept: */*");
out.println("Host: 192.168.1.100:8080");
out.println("Connection: close");
out.println();
out.println();
InputStream in = s.getInputStream();
byte[] buf = new byte[1024];
int len = in.read(buf);
String str = new String(buf, 0, len);
System.out.println(str);
s.close();
}
}
四、URL & URLConnection
URI:统一资源标示符。
URL:统一资源定位符(uniform resource locator ),使用 java.net.URL 进行网络信息获取。
每个URL都是URI,但不一定每个URI都是URL。这是因为URI还包括一个子类,即统一资源名称(URN),它命名资源但不指定如何定位资源。
读取网页文件内容的步骤:
(1) 创建一个URL类型的对象。
URL url = new URL( “http://www.baidu.com”);
(2) 利用URL类的openStream(),获得对应的InputStream类的对象。
InputStream stream = url.openStream();
(3) 通过 InputStream 或 InputStreamReader 来读取内容。
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
public class URLDemo {
public static void main(String[] args) throws
MalformedURLException, IOException{
String str_url = "http://192.168.1.100:8080/myweb/1.html?name=Luna";
URL url = new URL(str_url);
System.out.println("getProtocol: "+url.getProtocol());
System.out.println("getHost: "+url.getHost());
System.out.println("getPort: "+url.getPort());
System.out.println("getFile: "+url.getFile());
System.out.println("getPath: "+url.getPath());
System.out.println("getQuery: "+url.getQuery());
//获取url对象的Url连接对象
URLConnection conn = url.openConnection();
System.out.println(conn);
String value = conn.getHeaderField("Content-Type");
System.out.println(value);
InputStream in = url.openStream();
//相当于url.openConnection().getInputStream();
byte[] buf = new byte[1024];
int len = in.read(buf);
String text = new String(buf,0,len);
System.out.println(text);
in.close();
}
}
小结——常见网络结构
1.C/S client/server
特点:
该结构的软件,客户端和服务端都需要编写。
开发成本较高,维护较为麻烦。
好处:
客户端在本地可以分担一部分任务。例如,杀毒软件直接对本机文件进行杀毒。
2. B/S browser/server
特点:
该结构的软件,只开发服务器端,不开发客户端,因为客户端直接由浏览器取代。
开发成本相对低,维护更为简单。
缺点:
所有运算都要在服务端完成。