网络协议:TCP协议和UDP协议

网络协议:

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维持端口活跃,保持通道通畅。

猜你喜欢

转载自blog.csdn.net/LuckyMeizai/article/details/80431341