- 网络通讯的三要素:
- 一、 IP
- IP地址
Internet上的每台主机(Host)都有一个唯一的IP地址。IP地址的长度为32位,分为4段,每段8位,用十进制数字表示,每段数字范围为0~255,段与段之间用句点隔开。例如159.226.1.1。(四个字节)
- IP地址类别
IP地址是由网络号和主机号组成
IP地址是由网络号和主机号组成
A类地址:8位网络位,24位主机位 政府单位
B类地址:16位网络位,16位主机位 事业单位(学校、银行..)
C类地址:24位网络位,8位主机位 私人使用.. - 特殊的IP地址
127.0.0.1 表示 本机回环地址- 二、 Port端口号
- Port地址
- 二、 Port端口号
如果把IP地址比作一间房子 ,端口就是出入这间房子的门。真正的房子只有几个门,但是一个IP地址的端口 可以有65536(即:2^16)个之多!端口是通过端口号来标记的,端口号只有整数,范围是从0 到65535(2^16-1)
- Port分类(0~65535)
公认端口(WellKnownPorts):从0到1023,它们紧密绑定(binding)于一些服务。
注册端口(RegisteredPorts):从1024到49151。它们松散地绑定于一些服务。
动态和/或私有端口(Dynamicand/orPrivatePorts):从49152到65535
- 常用端口
21 ---> FTP
80 ---> HTTP
443 ---> HTTPS- 三、协议
- TCP:
TransmissionControl Protocol 传输控制协议TCP是一种面向连接(连接导向)的、可靠的、基于字节流的运输层(Transportlayer)通信协议。
特点:
面向连接的协议,数据传输必须要建立连接,所以在TCP中需要连接时间。
传输数据大小限制,一旦连接建立,双方可以按统一的格式传输大的数据。
一个可靠的协议,确保接收方完全正确地获取发送方所发送的全部数据。
- UDP:
User Datagram Protocol的简称,中文名是用户数据包协议,是 OSI 参考模型中一种无连接的传输层协议,提供面向事务的简单不可靠信息传送服务。
特点:
每个数据报中都给出了完整的地址信息,因此无需要建立发送方和接收方的连接。
UDP传输数据时是有大小限制的,每个被传输的数据报必须限定在64KB之内。
UDP是一个不可靠的协议,发送方所发送的数据报并不一定以相同的次序到达接收方。
- TCP和UDP
- UDP通讯协议的特点:
将数据封装为数据包,面向无连接。
每个数据包大小限制在64K中
因为无连接,所以不可靠
因为不需要建立连接,所以速度快
UDP 通讯是不分服务端与客户端的,只分发送端与接收端。
比如: 物管的对讲机, QQ聊天、 游戏...
- UDP通讯协议的特点:
/*
发送端的使用步骤:
1. 建立udp的服务。
2. 准备数据,把数据封装到数据包中发送。 发送端的数据包要带上ip地址与端口号。
3. 调用udp的服务,发送数据。
4. 关闭资源。
*/
// 发送方
public class Sender {
public static void main(String[] args) throws IOException {
// 创建UDP服务
DatagramSocket socket = new DatagramSocket();
// 准备数据,把数据封装到数据包
String dataStr = "您好,请查收信息哦";
// 创建一个数据包
DatagramPacket packet = new DatagramPacket(dataStr.getBytes(), dataStr.getBytes().length, InetAddress.getLocalHost(), 60900);
// 调用UDP服务发送数据包
socket.send(packet);
// 关闭UDP服务
socket.close();
}
}
//接收端
/*
接收端的使用步骤
1. 建立udp的服务
2. 准备空 的数据 包接收数据。
3. 调用udp的服务接收数据。
4. 关闭资源
*/
public class Revice {
public static void main(String[] args) throws IOException {
// 创建UDP服务
DatagramSocket socket = new DatagramSocket(60900);
// 准备空的数据包接收数据
byte[] buff = new byte[1024];
DatagramPacket packet = new DatagramPacket(buff, buff.length);
// 调用UDP服务接收数据
socket.receive(packet);
System.out.println("接收到数据:" + new String(buff, 0, packet.getLength()));
socket.close();
}
}
- TCP通讯协议特点:
TCP是基于IO流进行数据 的传输 的,面向连接。
TCP进行数据传输的时候是没有大小限制的。
TCP是面向连接,通过三次握手的机制保证数据的完整性。 可靠协议。
TCP是面向连接的,所以速度慢。
TCP通讯是区分客户端与服务端 的。
比如: 打电话、 QQ的文件传输、 迅雷下载....
TCP协议下的Socket:
Socket(客户端) , TCP的客户端一旦启动马上要与服务端进行连接。
ServerSocket(服务端类)
/*
tcp的客户端使用步骤:
1. 建立tcp的客户端服务。
2. 获取到对应的流对象。
3.写出或读取数据
4. 关闭资源。
*/
// TCP的客户端
public class ChatClient {
public static void main(String[] args) {
try {
// 创建TCP服务
Socket socket = new Socket(InetAddress.getLocalHost(), 20000);
// 获取socket的输出流对象
OutputStream outputStream = socket.getOutputStream();
// 利用输出流对象写入数据
outputStream.write("客户端说:您好,我是客户端".getBytes());
// 获取到输入流对象,读取服务端返回的数据
InputStream inputStream = socket.getInputStream();
byte[] buffer = new byte[1024];
int length = inputStream.read(buffer);
System.out.println("服务端说:" + new String(buffer, 0, length));
// 关闭客户端
outputStream.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
// TCP服务端
public class ChatService {
public static void main(String[] args) {
try {
// 建立TCP的服务端,并监听一个端口
ServerSocket serverSocket = new ServerSocket(20000);
// 接受客户端的连接 该方法也是一个阻塞型的方法,没有客户端与其连接时,会一直等待下去。
Socket socket = serverSocket.accept();
// 获取输入流对象,读取客户端发送的数据
InputStream inputStream = socket.getInputStream();
byte[] buffer = new byte[1024];
int length = 0;
length = inputStream.read(buffer);
System.out.println("客户端说:" + new String(buffer, 0, length));
// 获取socket的输出流对象,向客户端发送数据
OutputStream outputStream = socket.getOutputStream();
outputStream.write("服务端说:您好我是服务端".getBytes());
// 关闭服务端
serverSocket.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
- Socket
- 套接字(socket)概念
套接字(socket)是通信的基石,是支持TCP/IP协议的网络通信的基本操作单元。它是网络通信过程中端点的抽象表示,包含进行网络通信必须的五种信息:连接使用的协议,本地主机的IP地址,本地进程的协议端口,远地主机的IP地址,远程进程的协议端口。
应用层通过传输层进行数据通信时,TCP会遇到同时为多个应用程序进程提供并发服务的问题。多个TCP连接或多个应用程序进程可能需要通过同一个 TCP协议端口传输数据。为了区别不同的应用程序进程和连接,许多计算机操作系统为应用程序与TCP/IP协议交互提供了套接字(Socket)接口。应用层可以和传输层通过Socket接口,区分来自不同应用程序进程或网络连接的通信,实现数据传输的并发服务。
-
建立socket连接
建立Socket连接至少需要一对套接字,其中一个运行于客户端,称为ClientSocket,另一个运行于服务器端,称为ServerSocket。
套接字之间的连接过程分为三个步骤:服务器监听,客户端请求,连接确认。- 服务器监听:服务器端套接字并不定位具体的客户端套接字,而是处于等待连接的状态,实时监控网络状态,等待客户端的连接请求。
- 客户端请求:指客户端的套接字提出连接请求,要连接的目标是服务器端的套接字。为此,客户端的套接字必须首先描述它要连接的服务器的套接字,指出服务器端套接字的地址和端口号,然后就向服务器端套接字提出连接请求。
- 连接确认:当服务器端套接字监听到或者说接收到客户端套接字的连接请求时,就响应客户端套接字的请求,建立一个新的线程,把服务器端套接字的描述发给客户 端,一旦客户端确认了此描述,双方就正式建立连接。而服务器端套接字继续处于监听状态,继续接收其他客户端套接字的连接请求。
SOCKET连接与TCP连接
创建Socket连接时,可以指定使用的传输层协议,Socket可以支持不同的传输层协议(TCP或UDP),当使用TCP协议进行连接时,该Socket连接就是一个TCP连接。Socket连接与HTTP连接
由于通常情况下Socket连接就是TCP连接,因此Socket连接一旦建立,通信双方即可开始相互发送数据内容,直到双方连接断开。但在实际网络应用中,客户端到服务器之间的通信往往需要穿越多个中间节点,例如路由器、网关、防火墙等,大部分防火墙默认会关闭长时间处于非活跃状态的连接而导致 Socket 连接断连,因此需要通过轮询告诉网络,该连接处于活跃状态。
而HTTP连接使用的是“请求—响应”的方式,不仅在请求时需要先建立连接,而且需要客户端向服务器发出请求后,服务器端才能回复数据。
很 多情况下,需要服务器端主动向客户端推送数据,保持客户端与服务器数据的实时与同步。此时若双方建立的是Socket连接,服务器就可以直接将数据传送给客户端;若双方建立的是HTTP连接,则服务器需要等到客户端发送一次请求后才能将数据传回给客户端,因此,客户端定时向服务器端发送连接请求,不仅可以 保持在线,同时也是在“询问”服务器是否有新的数据,如果有就将数据传给客户端。Scoket连接和HTTP连接的区别:
HTTP协议是基于TCP连接的,是应用层协议,主要解决如何包装数据。Socket是对TCP/IP协议的封装,Socket本身并不是协议,而是一个调用接口(API),通过Socket,我们才能使用TCP/IP协议。
HTTP连接:短连接,客户端向服务器发送一次请求,服务器响应后连接断开,节省资源。服务器不能主动给客户端响应(除非采用HTTP长连接技术)
Socket连接:长连接,客户端跟服务器端直接使用Socket进行连接,没有规定连接后断开,因此客户端和服务器段保持连接通道,双方可以主动发送数据,一般多用于游戏.Socket默认连接超时时间是30秒,默认大小是8K(理解为一个数据包大小)。
// 练习1: 使用TCP实现客户端与服务端一问一答聊天
// 客户端类
public class Client {
public static void main(String[] args) throws IOException {
// 建立TCP的客户端服务
Socket socket = new Socket(InetAddress.getLocalHost(), 20000);
// 获取socket的输出流对象
OutputStreamWriter writer = new OutputStreamWriter(socket.getOutputStream());
// 获取socket的输入流对象
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
// 获取键盘的输入流对象,读取数据
BufferedReader keyReader = new BufferedReader(new InputStreamReader(System.in));
// 不断的读取键盘录入的数据然后把数据输出
String lineStr = null;
while ((lineStr = keyReader.readLine()) != null) {
System.out.println();
writer.write(lineStr + "\r");
// 刷新数据
writer.flush();
// 读取服务器返回的数据
lineStr = reader.readLine();
System.out.println("服务器返回的数据是:" + lineStr);
}
// 关闭socket
socket.close();
}
}
// 服务端类
public class Service {
public static void main(String[] args) throws IOException {
// 建立tcp的服务端
ServerSocket serverSocket = new ServerSocket(20000);
// 接收服务端的连接,并获取一个socket
Socket socket = serverSocket.accept();
// 获取到socket的输入流对象
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
// 获取到socket的输出流对象
OutputStreamWriter writer = new OutputStreamWriter(socket.getOutputStream());
// 获取键盘的输入流对象
BufferedReader keyReader = new BufferedReader(new InputStreamReader(System.in));
// 读取客户端的数据
String lineStr = null;
while ((lineStr = reader.readLine()) != null) {
System.out.println("服务器接收到的数据:" + lineStr);
System.out.println("请输入回复客户端的数据");
lineStr = keyReader.readLine();
writer.write(lineStr + "\r");
writer.flush();
}
// 关闭资源
serverSocket.close();
}
}
// 练习2:编写一个服务器,可以让多个客户端可以下载图片
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashSet;
// 练习:编写一个服务器,可以让多个客户端下载图片
// 模拟服务器类
public class ImageService extends Thread {
Socket socket;
// 使用该集合是用于存储ip地址的,由于重复元素不会被添加,间接记录下载的人数
HashSet< String> ipSet = new HashSet<String>();
@Override
public void run() {
try {
// 获取socke的输出流对象
OutputStream outputStream = socket.getOutputStream();
// 获取图片的输入流对象
FileInputStream fileInputStream = new FileInputStream("/Users/mofeini/Desktop/to/4.jpg");
// 读取图片,把数据写入到指定路径
byte[] buffer = new byte[1024];
int length = 0;
while ((length = fileInputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, length);
}
String ip = socket.getInetAddress().getHostAddress();
if(ipSet.add(ip)) {
System.out.println("第" + ipSet.size() + "下载了图片" );
}
// 关闭资源
fileInputStream.close();
socket.close();
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
public ImageService(Socket socket) {
this.socket = socket;
}
public static void main(String[] args) throws IOException {
// 建立tcp连接,并监听端口
ServerSocket serverSocket = new ServerSocket(20002);
while (true) {
// 确定连接
Socket socket = serviceSocket.accept();
new ImageService(socket).start();
}
}
}
// 下载图片的客户端
public class ImageClient {
public static void main(String[] args) throws IOException {
// 建立tcp服务
Socket socket = new Socket(InetAddress.getLocalHost(), 20002);
// 获取socket的输入流对象
InputStream inputStream = socket.getInputStream();
// 获取文件的输出流对象
FileOutputStream fileOutputStream = new FileOutputStream("/Users/mofeini/Desktop/to/download.jpg");
// 边读取图片,边下载图片
byte[] buffer = new byte[1024];
int length = 0;
while ((length = inputStream.read(buffer)) != -1) {
fileOutputStream.write(buffer, 0, length);
}
// 关闭资源
fileOutputStream.close();
socket.close();
}
}