网络协议:
1、网络协议分层:
OSI模型是国际标准化组织ISO创立的。这是一个理论模型,并无实际产品完全符合OSI模型。制定OSI模型只是为了分析网络通讯方便而引进的一套理论。也为以后制定实用协议或产品打下基础。
OSI模型总共分7层,各层的作用:从上到下
A、 应用层:指网络操作系统和具体的应用程序,对应WWW服务器、FTP服务器等应用软件。
B、 表示层:数据语法的转换、数据的传送等。
C、 会话层:建立起两端之间的会话关系,并负责数据的传送。
D、传输层:负责错误的检查与修复,以确保传送的质量,是TCP工作的地方。
E、 网络层:提供了编址方案,IP协议工作的地方(数据包)。包括路由器设备,路由器就是帮助如何选择IP通信线路的硬件设备,本质上是一个电脑,具有双网卡。进行路线的选择。
F、 数据链路层:将由物理层传来的未经处理的位数据包装成数据帧。包括交换机、网卡等设备。
G、物理层:对应网线、光纤、接口等物理设备。
传输层向上才是跟编程有关系,向下都是跟硬件设备有关系,不涉及编程。
2、IP地址及含义:
IP地址有32位。通常被分割为4个8位的二进制。
传输层协议:
1、TCP和UDP协议:
TCP(传输控制协议)和UDP(用户报文协议)协议属于传输层协议。
A、 TCP提供IP环境下的数据可靠传输,它提供的服务包括数据流传送、可靠性、有效流控、全双工操作和多路复用。通过面向连接、端到端和可靠的数据包发送。面向连接就是在正式通信前必须要与对方建立起连接,比如,你给人打电话,必须等线路接通了,对方拿起话筒才能相互通话。
B、 UDP是用户数据报协议,是OSI参考模型中一种无连接的传输层协议,提供面向事务的简单不可靠信息传送服务。是与TCP相对应的协议。是面向非连接的协议,不与对方建立连接,而是直接把数据包发送过去。
2、端口号的概念:
一般指TCP/IP协议中的端口,端口号的范围是0到65535。通过“IP地址+端口号”来区分不同的服务。
3、TCP协议与UDP协议的区别:
1、 TCP协议需要创建连接,而UDP协议则不需要。
2、 TCP是可靠的传输协议,而UDP是不可靠的。
3、 TCP适合传输大量的数据,而UDP适合传输少量数据。
4、 TCP的速度慢,而UDP的速度快。
应用层协议:都是建立在TCP和UDP之上的。
1、HTTP协议:
HTTP协议叫做超文本传输协议,是用于从WWW服务器传输超文本到本地浏览器的传送协议。它可以使浏览器更加高效,使网络传输减少。它不仅保证计算机正确快速的传输超文本文档,还确定传输文档中的哪一部分,以及哪一部分内容首先显示。HTTP是一个应用层协议,由请求和响应构成,是一个标准的客户端服务器模型,是一个无状态协议。
2、FTP协议:
FTP是TCP/IP协议组中的协议之一,是文件传输协议。该协议是Internet文件传送的基础,它由一系列规格说明文档组成,目标是提高文件的共享性,提供非直接远程计算机,使存储介质对用户透明和可靠高效的传输数据。
3、SMTP协议:
SMTP即是简单邮件传输协议,是一种提供可靠且有效电子邮件传输的协议。SMTP是建立在FTP文件传输服务上的一种邮件服务,主要用于传输系统之间的邮件信息并提供与来信有关的通知。
TCP通信:
1、 Socket原理
2、 Socket通信模型
Socket原理:
1、Socket简介:
Socket通常叫做“套接字”,用于描述IP地址和端口,是一个通信链的句柄。在Internet上的主机一般运行了多个服务软件,同时提供几种服务。每种服务都打开一个Socket,并绑定到一个端口上,不同的端口对应于不同的服务。
应用程序通常通过“套接字”向网络发出请求或者应答网络请求。Socket和ServerSocket类位于java.net包中。ServerSocket用于服务端,Socket是建立网络连接时使用的。在连接成功时,应用程序两端都会产生一个Socket实例,操作这个实例,完成所需的会话。
2、获取本地地址和端口号:
Java.net.Socket为套接字,其提供了很多方法,其中我们可以通过Socket获取本地的地址以及端口。
1、---intgetLocalPort()
该方法用于获取本地使用的端口号。在客户端就是客户端本地,服务器端就是服务器端地址。
2、---InetAddressgetLocalAddress()
该方法用于获取套接字绑定的本地地址。
使用InetAddress获取本地的地址方法:
1、---StringgetCanonicalHostName()
获取此IP地址的完全限定域名
2、---StringgetHostAddress()
返回IP地址字符串(以文本表现形式)
3、获取远端地址和端口号:
通过Socket获取远端的地址以及端口号:
---intgetPort()
该方法用于获取远端使用的端口号
---InetAddress.getInetAddress()
该方法用于获取套接字绑定的远端地址
4、获取网络输入流和网络输出流:
通过Socket获取输入流与输出流,这两个方法是使用Socket通讯的关键方法
---InputStreamgetInputStream()
该方法用于返回此套接字的输入流
---OutputStreamgetOutputStream()
该方法用于返回此套接字的输出流
5、Close方法:
Socket通信模型:
1、Server端ServerSocket监听:
Java.net.ServerSocket是运行于服务器端应用程序中。通常创建ServerSocket需要指定服务端口号,之后监听Socket的连接:
ServerSocketserver=new ServerSocket(8080);只要一new就会绑定一个端口号,这个端口号只能运行一遍,再运行第2遍就会说这个端口号已经被占用了。
Socketsocket=server.accept();accept()方法是阻塞式方法,等待客户端的连接
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PipedWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class TCPServer {
ServerSocket ss;
public void start() throws Exception{
ss=new ServerSocket(8000);
//main方法所在的线程是主线程,由main方法执行的方法就是主线程执行的方法,监听客户端的连接
while(true) {//不断的监听是否有客户端连接进来
Socket s=ss.accept();//连接进来就有一个Socket对象
ClientHandler handler=new ClientHandler(s);//将客户端交给线程
Thread t=new Thread(handler);
//启动客户端处理线程,当前线程返回去监听
t.start();//开启子线程,主线程继续监听
}
}
class ClientHandler implements Runnable{//使用内部类控制起来方便,可以共享外部类的资源
Socket socket;
public ClientHandler(Socket s) {
socket=s;
}
public void run() {
try {
InputStream in=socket.getInputStream();
OutputStream out=socket.getOutputStream();
BufferedReader reader=new BufferedReader(new InputStreamReader(in,"utf-8"));
PrintWriter writer=new PrintWriter(new OutputStreamWriter(out,"utf-8"),true);
//true是自动刷出
String str=reader.readLine();
System.out.println("从客户收到:"+str);
writer.println("是你啦");//回给客户一个
socket.close();
}catch(Exception e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws Exception{
TCPServer server=new TCPServer();
server.start();//主线程start,启动子线程的run(),主线程start之后又返回去监听客户端
}
}
2、Client端Socket连接:
当服务端创建ServerSocket并通过accept()方法侦听后,我们就可以通过在客户端应用程序中创建Socket来向服务端发起连接。
需要注意的是,创建Socket的同时就发起连接,若连接异常会抛出异常。
Socket socket=newSocket(“localhost”,8080);
参数1:服务器的IP地址或者域名 参数2:端口号
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
public class TCPClientDemo {
public static void main(String[] args) throws Exception{
//连接到服务器,如果没有找到(也就是IP.port错了)就会抛出异常,如果连接成功就创建Socket对象
Socket s=new Socket("localhost",8000);//服务器的IP地址和端口号
System.out.println("客户端:");
System.out.println(s.getLocalAddress());
System.out.println(s.getLocalPort());
System.out.println(s.getInetAddress());
System.out.println(s.getPort());
//工厂方法
OutputStream out=s.getOutputStream();//返回套接字的输出流
InputStream in=s.getInputStream();//返回套接字的输入流
BufferedReader reader=new BufferedReader(new InputStreamReader(in,"utf-8"));
//BufferedReader每次读取一行
PrintWriter writer=new PrintWriter(new OutputStreamWriter(out,"utf-8"),true);
//具有自动刷新的缓冲字符输出流
writer.println("小丽呀?");//必须是println()
String str=reader.readLine();//接收到回车为止
System.out.println("收到:"+str);
s.close();
}
}
import java.io.BufferedReader;
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 TCPServerDemo {
public static void main(String[] args) throws Exception {
//绑定TCP服务端口8000
ServerSocket ss=new ServerSocket(8000);//如果TCP 8000端口占用了就会抛出异常
//开始监听服务端口,accept是阻塞方法,等待有客户端的连接,没有连接一直阻塞,有连接,成功以后,结束阻塞返回Socket对象
Socket s=ss.accept();
System.out.println("服务器:");
System.out.println(s.getLocalAddress());//获取服务器端的地址
System.out.println(s.getLocalPort());//获取服务器端的端口号
System.out.println(s.getInetAddress());//获取远程地址
System.out.println(s.getPort());//获取远程端口号
OutputStream out=s.getOutputStream();
InputStream in=s.getInputStream();
BufferedReader reader=new BufferedReader(new InputStreamReader(in,"utf-8"));
PrintWriter writer=new PrintWriter(new OutputStreamWriter(out,"utf-8"),true);
String str=reader.readLine();//等待消息一直到回车为止
System.out.println(str);
writer.println("我是呀!");
s.close();
}
}
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PipedWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class TCPServer {
ServerSocket ss;
public void start() throws Exception{
ss=new ServerSocket(8000);
//main方法所在的线程是主线程,由main方法执行的方法就是主线程执行的方法,监听客户端的连接
while(true) {//不断的监听是否有客户端连接进来
Socket s=ss.accept();//连接进来就有一个Socket对象
ClientHandler handler=new ClientHandler(s);//将客户端交给线程
Thread t=new Thread(handler);
//启动客户端处理线程,当前线程返回去监听
t.start();//开启子线程,主线程继续监听
}
}
class ClientHandler implements Runnable{//使用内部类控制起来方便,可以共享外部类的资源
Socket socket;
public ClientHandler(Socket s) {
socket=s;
}
public void run() {
try {
InputStream in=socket.getInputStream();
OutputStream out=socket.getOutputStream();
BufferedReader reader=new BufferedReader(new InputStreamReader(in,"utf-8"));
PrintWriter writer=new PrintWriter(new OutputStreamWriter(out,"utf-8"),true);
//true是自动刷出
String str=reader.readLine();
System.out.println("从客户收到:"+str);
writer.println("是你啦");//回给客户一个
socket.close();
}catch(Exception e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws Exception{
TCPServer server=new TCPServer();
server.start();//主线程start,启动子线程的run(),主线程start之后又返回去监听客户端
}
}
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
public class TCPClientDemo {
public static void main(String[] args) throws Exception{
//连接到服务器,如果没有找到(也就是IP.port错了)就会抛出异常,如果连接成功就创建Socket对象
Socket s=new Socket("localhost",8000);//服务器的IP地址和端口号
System.out.println("客户端:");
System.out.println(s.getLocalAddress());
System.out.println(s.getLocalPort());
System.out.println(s.getInetAddress());
System.out.println(s.getPort());
//工厂方法
OutputStream out=s.getOutputStream();//返回套接字的输出流
InputStream in=s.getInputStream();//返回套接字的输入流
BufferedReader reader=new BufferedReader(new InputStreamReader(in,"utf-8"));
//BufferedReader每次读取一行
PrintWriter writer=new PrintWriter(new OutputStreamWriter(out,"utf-8"),true);
//具有自动刷新的缓冲字符输出流
writer.println("小丽呀?");//必须是println()
String str=reader.readLine();//接收到回车为止
System.out.println("收到:"+str);
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;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
public class TalkServer {
private List<PrintWriter> users;//登陆用户队列
private BlockingQueue<String> queue;//消息队列
private ServerSocket ss;
//启动TCP监听,和等待客户端连接
public void start() throws Exception{//启动监听
users=new ArrayList<PrintWriter>();
queue=new LinkedBlockingQueue<String>(100);//队列中最多放100个消息
ss=new ServerSocket(8000);
//先启动小兵
Thread bing=new Thread(new Bing());
bing.start();
//再进行客户连接监听
while(true) {
Socket s=ss.accept();//等待用户的登录
PrintWriter out=new PrintWriter(s.getOutputStream(),true);//开启自动刷出
BufferedReader in=new BufferedReader(new InputStreamReader(s.getInputStream()));
users.add(out);//已经登录的用户的输出流
//用户的输出流交给ClientHandler处理
new Thread(new ClientHandler(in)).start();
}
}
//负责接收每个用户的消息,保存到队列中
class ClientHandler implements Runnable{//负责接收用户消息
BufferedReader in;
public ClientHandler(BufferedReader in) {
this.in=in;
}
public void run() {//从客户流里面读一个消息放到队列里,读错了就不读了,线程就结束
while(true) {
try {
//从客户端读取消息
String str=in.readLine();
//网络断开的时候,readLine会返回null
if(str==null) {
break;
}
//发送到queue中
queue.put(str);
}catch(Exception e) {
e.printStackTrace();
break;
}
}
try {
in.close();
}catch(Exception e) {
}
}
}
//小兵负责消息的转发,从队列中取消息发送到user
class Bing implements Runnable{
public void run() {
//从队列中读取消息,转发给每个客户
while(true) {
try {
String msg=queue.take();
for(PrintWriter out:users) {
out.println(msg);
}
}catch(InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) throws Exception{
TalkServer server=new TalkServer();
//启动服务器
server.start();
}
}
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
public class TalkClient1 {
Socket socket;
//启动客户端:连接到服务器,启动两个线程
public void start() throws Exception{
socket=new Socket("localhost",8000);
new Sender(socket.getOutputStream()).start();
new Receiver(socket.getInputStream()).start();
}
//从控制台读取消息,发送到服务器
class Sender extends Thread{
OutputStream out;
public Sender(OutputStream out){
this.out=out;
}
public void run() {
//从控制台读消息,发送到服务器
Scanner in=new Scanner(System.in);
//out是发送到服务器的流
PrintWriter out=new PrintWriter(this.out,true);
String ip=socket.getLocalAddress().getHostAddress();//获得本机IP
while(true) {
String str=in.nextLine();
out.println(ip+":"+str);
}
}
}
//从服务器接收消息,写到控制台
class Receiver extends Thread{
InputStream in;
public Receiver(InputStream in) {
this.in=in;
}
public void run() {
//从服务器接收消息,写到控制台
BufferedReader in=new BufferedReader(new InputStreamReader(this.in));
while(true) {
try {
String str=in.readLine();
if(str==null) {//如果null,网络断开了
break;
}
System.out.println(str);
}catch(Exception e) {
e.printStackTrace();
break;
}
}
try {
socket.close();
}catch(Exception e) {
}
}
}
public static void main(String[] args) throws Exception {
TalkClient1 client=new TalkClient1();
client.start();
}
}
UDP通信:
1、 DatagramPacket
2、 DatagramSocket
3、 UDP穿透
DatagramPacket:数据报文
1、构建接收包:
DatagramPacket:UDP数据报基于IP建立的,每台主机有65535个端口号可以使用,数据报中字节数限制为65536-8
构造接受包:
1、---DatagramPacket(byte[] buf,int length):
将数据包中length长的数据装进buf数组
2、---DatagramPacker(byte[] buf,int offstet,int length):
将数据包从offset开始、length长的数据装进buf数组
DatagramSocket:发送和接收
1、服务端接收:
DatagramSocket用于接收和发送UDP的Socket实例
---DatagramSocket(int port):
创建实例,并固定监听port端口的报文,通常用于服务端。
---receive(DatagramSocket d):
接收数据报文到d中。receive方法产生“阻塞”。
2、 客户端发送
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class UDPClient {
public static void main(String[] args) throws Exception{
//准备数据,相当于信件内容
String str="您好!";
byte[] buf=str.getBytes("utf-8");
//创建数据封包,包含发送目标地址,相当于包信封
InetAddress ip=InetAddress.getByName("localhost");
//InetAddress表示互联网IP地址,根据域名来获得IP地址
DatagramPacket data=new DatagramPacket(buf,buf.length,ip,8899);
//packet data、packet length、目标地址、目标端口号
//发送UDP数据,投递
DatagramSocket socket=new DatagramSocket();
socket.send(data);
//准备接受服务器返回的数据
buf=new byte[1024];
data=new DatagramPacket(buf,1024);
socket.receive(data);
int length=data.getLength();
str=new String(buf,0,length,"utf-8");
System.out.println(str);
}
}
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class UDPServer {
public static void main(String[] args) throws Exception{
//创建空白空间
byte[] buf=new byte[1024];
//创建空接收数据包,如果收到数据会填充到数组
DatagramPacket data=new DatagramPacket(buf,1024);
//开始准备接收数据
DatagramSocket socket=new DatagramSocket(8899);
//开始监听,receive()方法是阻塞方法,有数据收到就结束Block
socket.receive(data);
//接收数据的数量
int length=data.getLength();
String str=new String(buf,0,length,"utf-8");//buf是将byte转为String的数组,从0到length
System.out.println(str);
//从接收到的数据包中获得客户端的IP和port
InetAddress clientIP=data.getAddress();
int port=data.getPort();
System.out.println(clientIP+":"+port);
//原路送回消息,发送消息到客户端
//准备数据
buf="我是小丽呀!".getBytes("utf-8");
//封装信封,地址是客户端地址
data=new DatagramPacket(buf,buf.length,clientIP,port);
//发送
socket.send(data);
}
}
UDP穿透:
1、 UDP穿透参考实例
1、 内网客户端机器A、B,向服务器S发送数据包C2SRegister。
允许S--->A,S--->B
2、 A首先向B的NAT端口发送数据包 C2CHoleStart
允许B--->A
3、 A然后向服务器发送数据包C2SHoleRequest,请求服务器通知B。
4、 服务器向B发送数据包S2CHoleCommand,指令B向A的NAT端口发送数据包C2CHoleAnswer.
允许A--->B
5、 A收到B的数据包,表示穿透成功
6、 A发送正式消息数据包C2CTextMessage给B
7、 A,B各自发送心跳包C2CHeartbeat,C2SHeartbeat维持端口活跃,保持通道通畅。