主要内容:
一,网络体系结构:
二,网络编程中的网络要素
三,UDP与TCP的详细讲解
四,浏览器和服务端的原理(应用层http协议消息头)
五,网络结构
一,网络体系结构:
① 计算机网络的体系结构(architecture)是计算机网络的各层及其协议的集合。
② 体系结构就是这个计算机网络及其部件所应完成的功能的精确定义。
③ 实现(implementation)是遵循这种体系结构的前提下用何种硬件或软件完成这些功能的问题。
④ 体系结构是抽象的,而实现则是具体的,是真正在运行的计算机硬件和软件。
两个模型:OSI模型,TCP/IP。
OSI模型的传输过程:
二,网络编程中的网络要素
三要素:ip地址,端口,协议。
IP地址
① 网络中每台主机都必须有一个唯一的IP地址。
② 因特网上的IP地址具有全球唯一性。
③ IP地址由32位二进制组成,占4个字节,常用十进制的格式来表示,例如:192.168.0.5。
④ 对应的类-InetAddress。
获取ip地址代码实现:
//获取本机的ip对象
InetAddress ip = InetAddress.getLocalHost();
//获取ip地址
System.out.println("ip地址 "+ip.getHostAddress());
//获取主机名称
System.out.println("主机名称 "+ip.getHostName());
//通过主机名称,获取ip对象,这里用百度为例
InetAddress ip_baidu = InetAddress.getByName("www.baidu.com");
//获取ip地址
System.out.println("ip地址 "+ip_baidu.getHostAddress());
//获取主机名称
System.out.println("主机名称 "+ip_baidu.getHostName());
端口号
① 端口号用来表示该计算机上的应用程序,代表此应用程序逻辑地址。
② 端口号使用一个16位的数字来表示,它的范围是0~65535,1024以下的端口号保留给预定义的服务。例如:http使用80端口。
协议概念
为计算机网络中进行数据交换而建立的规则、标准或约定的集合。
常见的传输协议
UDP:
① 将数据源和目的地封装在数据包中,不需要建立连接,每个数据包的大小限制在64k之内。
② 因为不需要进行连接,所以速度快,但因此也不安全,所以是不可靠协议。
TCP:
① 建立连接,形成传输数据的通道,在连接中能进行大数据量传输。
② 通过三次握手完成连接,是可靠协议,但由于必须要建立连接,因此效率会降低。
三,UDP与TCP的详细讲解
UDP的传输
DatagramSocket(); 发送和接收数据包的套接字
DatagramPacket(); 数据报包,实现无连接包投递服务
创建UDP传输的发送端
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class UDPSendDemo {
public static void main(String[] args) throws IOException {
/*
* 创建UDP传输的发送端
* 思路:
* 1,建立udp的socket服务。
* 2,将要发送的数据封装到数据包中
* 3,通过udp的socket服务将数据包发送出去
* 4,关闭socket服务。
*/
//1,建立udp的socket服务
DatagramSocket socket = new DatagramSocket(8888);
//2,将要发送的数据封装到数据包中
String str = "UDP传输演示:我来了小兄弟";
byte[] buf = str.getBytes();
DatagramPacket dp = new DatagramPacket(buf, buf.length, InetAddress.getLocalHost(), 10000);
//3,通过udp的socket服务将数据包发送出去
socket.send(dp);
//4,关闭socket服务。
socket.close();
}
}
创建UDP传输的接收端
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class UDPReceDemo {
public static void main(String[] args) throws IOException {
/*
* 建立UDP接收端的思路。
* 1,建立udp socket服务,因为是要接收数据,必须要明确一个端口号。
* 2,创建数据包,用于存储接收到的数据。方便用数据包对象解析这些数据。
* 3,使用socket服务的receive方法将接受的数据存储到数据包中
* 4,通过数据包的方法解析数据包中的数据
* 5,关闭资源
*/
//1,建立udp socket服务,因为是要接收数据,必须要明确一个端口号。
DatagramSocket socket = new DatagramSocket(10000);
//2,创建数据包,用于存储接收到的数据。方便用数据包对象解析这些数据。
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket(buf, buf.length);
//3,使用socket服务的receive方法将接受的数据存储到数据包中
socket.receive(dp); //阻塞方法
//4,通过数据包的方法解析数据包中的数据
String ip = dp.getAddress().getHostAddress();
int port = dp.getPort();
String content = new String(dp.getData(),0,dp.getLength());
System.out.println(ip+":"+port+" "+content);
//5,关闭资源
socket.close();
}
}
这时我们,发送端和接受端都创建好了,那我们应该先运行,那段代码呢?
这里我想说的是,我们无论先运行那段代码都可以,只不过如果我们先运行发送端,然后在运行接收端的话,我们会发现,接收端不会接收到任何结果。
这是因为UDP是不需要先建立连接的,所以我们发送数据,不需要知道你接不接收,我们只管发数据就行了。
在这里呢?因为我们要想接受到数据,所以我们先启动,接受端,然后在启动发送端,就可以接手到数据了。
UDP实现聊天程序
思路架构:
聊天室其实就是在一个用户上既可以发送数据,又可以接收数据,那么,就需要两个类分别代表发送和接收,并且这两个要能够同时进行,此时就需要多线程。
发送:用UDP发送数据,那么就需要通过DatagramSocket类帮助发送,也就是任务对象一建立,就需要socket对象,那么我们可以将DatagramSocket作为成员变量并进行封装。
然后线程中的run()如何实现呢?既然是发送端,那么核心任务就是发送数据了:
1.发送的数据是通过键盘进行录入的 2.将数据封装到数据包中 3.将数据包发送出去
接收:和发送一样,接收也同样需要通过DatagramSocket类,创建socket对象
接收数据如何实现?其实原理就和上面的UDP接收端是一样的:
1.接收具体的数据内容 2.创建数据包对象 3.将受到的数据存储到数据包中 4.获取数据
但是,这样的话,接收和发送的数据就都只是一次性的,要让它变为不停的来回发送和接收,那么只需要给条件加一个循环就可以了,以下是代码实现:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
//发送端
class Send implements Runnable{
private DatagramSocket socket = null;
public Send(DatagramSocket socket) {
super();
this.socket = socket;
}
@Override
public void run() {
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
String line = null;
try {
//键盘录入
while((line = bufr.readLine()) != null) {
byte[] buf = line.getBytes();
// 192.168.2.104 是我的ip地址
DatagramPacket dp = new DatagramPacket(buf,buf.length, InetAddress.getByName("192.168.2.104"), 10000);
socket.send(dp);
if(line.equals("886"))
break;
}
bufr.close();
socket.close();
}catch(IOException e) {
}
}
}
//接收端
class Receive implements Runnable{
private DatagramSocket socket = null;
public Receive(DatagramSocket socket) {
super();
this.socket = socket;
}
@Override
public void run() {
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket(buf, buf.length);
try {
while(true) {
socket.receive(dp);
String ip = dp.getAddress().getHostAddress();
int port = dp.getPort();
String content = new String(dp.getData(),0,dp.getLength());
System.out.println(ip+":"+port+" "+content);
if(content.equals("886"))
break;
}
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public class UDPChatDemo {
public static void main(String[] args) throws IOException {
DatagramSocket send = new DatagramSocket(8888);
DatagramSocket receive = new DatagramSocket(10000);
new Thread(new Send(send)).start();
new Thread(new Receive(receive)).start();
}
}
运行结果如下:
在这里我们发现在这个程序当中,我们是自己跟自己聊天,那要怎么才能让其他人跟我们聊天呢?
在Send类中 192.168.2.104 是我的ip地址,要想然别人也能一起聊天,需要把程序先发送给另一个,电脑,并且要保证,自己的电脑,和另外一台电脑在同一个局域网中,然后获取对方的ip,然后将其修改成对方的ip地址就可以了。
那我们怎样才实现多人聊天呢?
原理跟上面一样,但是我们这是填写的ip地址会发生一点小小的变换。
通过cmd 我得到了我的IP地址如下:
在这里有个子网掩码,通过ip地址和子网掩码我们可以得到,在这个局域网里面的广播地址是:
192.168.2.255,然后在改成广播地址,就可以实现群聊了。
(以下是我的猜测)
在这里呢?我突然想到了QQ聊天是不是也是这样的呢?两个qq互加好友,然后相互聊天,构成一个局域网,然后利用UDP进行聊天,如果对方不在线,就先把聊天数据,保留在本地上,然后对方上线后,在聊天数据传输过去。
TCP的传输
TCP需要建立客户端和服务端,但是TCP是面向连接的,只有连接成为通路,才会在客户端和服务端之间进行数据的传输,而这个连接过程就被成为三次握手,简易图解如下
创建TCP的客户端
Socket(); 客户端的套接字
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
public class TCPClientDemo {
public static void main(String[] args) throws IOException {
/*
* Tcp传输,客户端建立的过程。
* 1,创建tcp客户端socket服务。使用的是Socket对象。
* 建议该对象一创建就明确目的地。要连接的主机。
* 2,如果连接建立成功,说明数据传输通道已建立。
* 该通道就是socket流 ,是底层建立好的。 既然是流,说明这里既有输入,又有输出。
* 想要输入或者输出流对象,可以找Socket来获取。
* 可以通过getOutputStream(),和getInputStream()来获取两个字节流。
* 3,使用输出流,将数据写出。
* 4,关闭资源。
*/
//1,创建tcp客户端socket服务。使用的是Socket对象。
Socket socket = new Socket(InetAddress.getLocalHost(),9090);
//2,使用输出流,将数据写出。
OutputStream out = socket.getOutputStream();
byte[] buf = "TCP传输:小老弟我来了".getBytes();
out.write(buf);
//3,关闭资源。
socket.close();
}
}
创建TCP的服务端
ServerSocket(); 服务端的套接字
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class TCPServerDemo {
public static void main(String[] args) throws IOException {
/*
* 建立tcp服务端的思路:
* 1,创建服务端socket服务。通过ServerSocket对象。
* 2,服务端必须对外提供一个端口,否则客户端无法连接。
* 3,获取连接过来的客户端对象。
* 4,通过客户端对象获取socket流读取客户端发来的数据
* 并打印在控制台上。
* 5,关闭资源。关客户端,关服务端。
*/
//1,创建服务端socket服务。通过ServerSocket对象。
ServerSocket ss = new ServerSocket(9090);
//2,获取连接过来的客户端对象
Socket s = ss.accept();//阻塞式
//3,通过获取得到的客户端对象,使用getInputStream()方法,读取
InputStream in = s.getInputStream();
byte[] buf = new byte[1024];
int len = 0;
len = in.read(buf);
String ip = s.getInetAddress().getHostAddress();
int port = s.getPort();
String content = new String(buf,0,len);
System.out.println(ip+":"+port+" "+content);
//4,关闭资源
ss.close();
}
}
这时我们,客户端和服务器都创建好了,那我们应该先运行,那段代码呢?
如果,我们先启动客户端,那么会报错,因为TCP是面向连接的,在传输前,需要建立连接,所以我们应该先启动,服务端,然后在启动客户端。
TCP客户端与服务端进行交互(分析)
客户端输入字母数据,发送给服务端,服务端收到后显示在控制台,并将该数据转成大写返回给客户端
直到客户端输入over.转换结束.
创建一个英文大写转换服务器.
分析:
有客户端和服务端,使用tcp传输
客户端:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.Socket;
public class TransClient {
public static void main(String[] args) throws IOException {
Socket socket = new Socket(InetAddress.getLocalHost(),9090);
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
//true的作用是,刷新缓冲区,将PrintWriter中的内容刷到socket.getOutputStream()里面
//如果没有true的话,改成 PrintWriter out = new PrintWriter(socket.getOutputStream());
//在下面代码中out.println(line);执行时,我们需要在这句代码下面,写上out.flush();
//如果不写的话,socket.getOutputStream()流里面没有内容,服务器那端将会一直等待
//从而使程序就会无法得到我们想要的结果。
PrintWriter out = new PrintWriter(socket.getOutputStream(),true);
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line = null;
while((line = bufr.readLine()) != null) {
if(line.equals("over"))
break;
//如果我们将out.println(line); 改成out.print(line); 也会出现问题
//原因如下:在服务器那端使用的是in.readLine()来读取数据,而readLine()读取数据结束的标记是"\r\n"(windows下是这样的)
//所以使用out.print(line)时,因为没有将"\r\n"写进去,就会让服务器那端使用的是in.readLine()一直等待,等到读到结束标记符为止。
//out.print(line)改成out.print(line+"\r\n");也可以使程序正常运行
out.println(line);
System.out.println(in.readLine());
}
socket.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 ss = new ServerSocket(9090);
Socket s = ss.accept();
String ip = s.getInetAddress().getHostAddress();
System.out.println(ip+"..............connected");
BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream()));
//跟客户端那边解释一样
PrintWriter out = new PrintWriter(s.getOutputStream(),true);
String line = null;
while((line = in.readLine()) != null) {
System.out.println(ip+": "+line);
//跟客户端那边解释一样
out.println(line.toUpperCase());
}
ss.close();
}
}
运行结果图如下:
TCP上传文本客户端
需求:客户端向服务器上传文件。
在这里呢?需要用到IO流技术,可查看IO流一,IO流二。
客户端:
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.Socket;
public class UploadFileClient {
public static void main(String[] args) throws IOException{
Socket socket = new Socket(InetAddress.getLocalHost(),9090);
BufferedReader bufr = new BufferedReader(new FileReader("file.txt"));
PrintWriter out = new PrintWriter(socket.getOutputStream(),true);
String line = null;
while((line = bufr.readLine()) != null) {
out.println(line);
}
//告诉服务端,客户端写完了
socket.shutdownOutput();
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
System.out.println(in.readLine());
bufr.close();
socket.close();
}
}
服务端:
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 UploadFileServer {
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(9090);
Socket s = ss.accept();
String ip = s.getInetAddress().getHostAddress();
System.out.println(ip+".............connected");
BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream()));
BufferedWriter bufw = new BufferedWriter(new FileWriter("file_1.txt"));
String line = null;
while((line = in.readLine()) != null) {
bufw.write(line);
bufw.flush();
}
//true在这里作用是,自动刷新缓冲区
PrintWriter out = new PrintWriter(s.getOutputStream(),true);
out.println("上传成功");
bufw.close();
ss.close();
}
}
四,浏览器和服务端的原理(应用层http协议消息头)
我们在将这个模块时需要用到Tomcat,所以我们需要安装Tomcat,如果你还没有安装好可以点点这个小东西
然后我们在apache-tomcat-9.0.22\webapps\ROOT这个目录下面创建一个文件夹MYWORK,并在里面创建一个1.html,1.html的内容如下:
<html>
<head>
<title>这是我的网页</title>
</head>
<body>
<h1>欢迎光临</h1>
<font size='5' color="red">这是一个tomcat服务器中的资源。是一个html网页。</font>
</body>
</html>
自定义服务端,
使用已有的客户端IE,了解一下客户端给服务端发了什么请求?
MyTomcat代码如下:
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class MyTomcat {
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
ServerSocket ss = new ServerSocket(9090);
Socket s = ss.accept();
InputStream in = s.getInputStream();
int len = 0;
byte[] buf = new byte[1024];
while((len = in.read(buf)) != -1) {
System.out.print(new String(buf,0,len));
}
ss.close();
}
}
然后我们启动MyTomcat,再在游览器(ie游览器)里面输入http://192.168.5.103:9090/ (格式:http://ip地址:端口号/)
然后我们接收到的内容是:
GET / HTTP/1.1 //请求行 请求方式 /后面跟的是 请求的资源路径 http的协议版本
//请求消息头 . 属性名:属性值
Accept: text/html, application/xhtml+xml, image/jxr, */*
Accept-Language: zh-CN
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko
Accept-Encoding: gzip, deflate
Host: 192.168.5.103:9090
Connection: Keep-Alive
//空行
//请求体。
自定义服务端
了解,服务端对于收到客户端的请求,然后返回什么数据。
MyIE代码如下:
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.Socket;
public class MyIE {
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
Socket socket = new Socket(InetAddress.getLocalHost(),8080);
PrintWriter out = new PrintWriter(socket.getOutputStream(),true);
out.println("GET /MYWORK/1.html HTTP/1.1");
out.println("Accept: text/html, application/xhtml+xml, image/jxr, */*");
out.println("Accept-Language: zh-CN");
out.println("User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko");
out.println("Accept-Encoding: gzip, deflate");
out.println("Host: 192.168.5.103:8080");
out.println("Connection: Keep-Alive");
out.println();
out.println();
InputStream in = socket.getInputStream();
int len = 0;
byte[] buf = new byte[1024];
while((len = in.read(buf)) != -1) {
System.out.print(new String(buf,0,len));
}
socket.close();
}
}
输出结果如下:
//服务端发回应答消息。
HTTP/1.1 200 //应答行,http的协议版本 应答状态码 应答状态描述信息
应答消息属性信息。 属性名:属性值
Accept-Ranges: bytes
ETag: W/"199-1564134739143"
Last-Modified: Fri, 26 Jul 2019 09:52:19 GMT
Content-Type: text/html
Content-Length: 199
Date: Sun, 28 Jul 2019 03:57:09 GMT
//空行
//应答体。
<html>
<head>
<title>这是我的网页</title>
</head>
<body>
<h1>欢迎光临</h1>
<font size='5' color="red">这是一个tomcat服务器中的资源。是一个html网页。</font>
</body>
</html>
那我们要怎么才能只收到 应答体 的下面部分,我们就需要薪的类 URL ,URLConnection
源码如下:
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
public class URLdemo {
public static void main(String[] args) throws IOException {
String str_url = "http://192.168.5.103:8080/MYWORK/1.html";
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());
//InputStream in = url.openStream();
//获取url对象的Url连接器对象。将连接封装成了对象:java中内置的可以解析的具体协议的对象+socket.
URLConnection conn = url.openConnection();
InputStream in = conn.getInputStream();
int len = 0;
byte[] buf = new byte[1024];
len = in.read(buf);
System.out.print( new String(buf,0,len));
}
}
输出结果:(这样游览器就可以解析产生界面)
<html>
<head>
<title>这是我的网页</title>
</head>
<body>
<h1>欢迎光临</h1>
<font size='5' color="red">这是一个tomcat服务器中的资源。是一个html网页。</font>
</body>
</html>
五,网络结构
1,C/S client/server
特点:
该结构的软件,客户端和服务端都需要编写。
可发成本较高,维护较为麻烦。
好处:
客户端在本地可以分担一部分运算。
2,B/S browser/server
特点:
该结构的软件,只开发服务器端,不开发客户端,因为客户端直接由浏览器取代。
开发成本相对低,维护更为简单。
缺点:所有运算都要在服务端完成。