基础系列【十七】--Socket基础
Socket–套间字
基础知识
1.网络分层:
为了解决网络之间通信的复杂问题 人为将网络分层 在不同层解决不同的问题 有不同的协议
网络协议:网络中主机之间通信的规范
OSI七层:
应用层: HTTP FTP SMTP TELNET ...
表示层:(传输的数据量)
会话层:
传输层: TCP UDP(传输的方式)
网络层: IP(传输的前提)
数据链路层:保证数据的传输
物理层 :物理连接
TCP/IP四层:
应用层
传输层
网际层
网络层
2.计算机网络中的基本的要素
IP地址(门牌号):
计算在网络中的唯一标识
ipv4 - 32位
127.0.0.1 -- 指向当前机器
255.255.255.255 -- 广播地址
ipv6 - 128位
主机名
因为ip地址难于记忆,可以为不同的主机起不同的名字 来唯一标识 这个唯一标识主机的名字 成为主机名 和 ip地址具有映射关系
localhost 代表本机
域名
全网认可的主机名 域名不能随意用 需要注册 先到先得 收取一定费用
域名解析
DNS服务器:域名解析服务器 接受域名 解析为 ip 返回
Hosts文件:windows自带的机制 可以在本地做主机名 和ip 的映射
在windows中使用主机名时 会先查找hosts文件 得到主机名和ip的映射 得到就用 得不到再求访问公网上的dns服务器翻译
C:\Windows\System32\drivers\etc\hosts
端口号(窗户号):
每个计算机 都有 若干个 可以供外界连接的窗口 每个窗口都有独一无二的编号 程序可以监听指定的端口号 外界通过ip连接到当前机器后 将数据发送 给指定端口号 从而 让正在监听该端口的程序收到数据。
范围:0~65535 其中0~1024是被系统占用了的,我们不能使用。
协议:网络中主机之间通信的规范
IP TCP/UDP HTTP(网络通信协议) POP3 SMTP(邮箱) TELNET FTP(文件上传下载) ...
InetAddress
代表网络地址的对象
java.net.InetAddress -此类表示互联网协议 (IP) 地址
没有提供构造方法
提供了静态方法来获取:
static InetAddress getLocalHost() //获取本机ip地址对象
static InetAddress getByName(String host) //根据给定ip或主机名 获取ip地址对象
String getHostName() //获取当前对象代表的主机的主机名
String getHostAddress() //获取当前对象代表的主机的ip地址
代码演示
package cn.tedu.net;
import java.net.InetAddress;
/**
* java网络开发 - InetAddress - 代表网络地址
*/
public class Demo01 {
public static void main(String[] args) throws Exception {
//1.获取自己的ip对象
// InetAddress addr1 = InetAddress.getLocalHost();
// String hostName = addr1.getHostName();
// String hostAddress = addr1.getHostAddress();
// System.out.println(hostName + "~" + hostAddress);
//2.获取其他的ip对象
InetAddress addr2 = InetAddress.getByName("www.baidu.com");
String hostName = addr2.getHostName();
String hostAddress = addr2.getHostAddress();
System.out.println(hostName + "~" + hostAddress);
}
}
UDP协议
不需要建立连接
将数据及源和目的地封装为数据包
每个数据报的大小限制在64k内
因为无连接,是不可靠协议
优点:快
缺点:会丢数据
TCP协议
建立连接,形成传输数据的通道
在连接中进行大数据量传输,靠流来进行操作
三次握手,是可靠协议
必须先建立连接,效率会稍低
优点:不丢数据
缺点:效率相对较低
Socket编程
什么是Scoket编程:
利用Java提供的TCP/UDP协议开发的编程接口,
一套API,让我们通过调用API实现TCP/UDP通信。
Socket实现UDP
java.net.DatagramSocket - UDP协议的编程接口类,负责收发数据包
构造方法:
DatagramSocket()//通常用来发送
DatagramSocket(int port)//通常用来接收
其他方法:
void send(DatagramPacket p) //发送数据包
void receive(DatagramPacket p) //接收数据包
void close() //关闭套接字
java.net.DatagramPacket - UDP的数据包,内含 数据 数据来源 数据目的地
构造方法:
DatagramPacket(byte[] buf, int length)//一般用做接收
DatagramPacket(byte[] buf, int length, InetAddress address, int port) //一般用作发送
其他方法:
InetAddress getAddress()
int getLength()
byte[] getData()
int getPort()
代码演示
/**
* java网络通信 - UDP - 数据包发送
*/
public class Demo02 {
public static void main(String[] args) throws Exception {
//1.创建UDP套接字
DatagramSocket socket = new DatagramSocket();
//2.创建UDP数据包
byte[] data = "hello UDP~".getBytes();
DatagramPacket p = new DatagramPacket(data, data.length,InetAddress.getByName("127.0.0.1"),44444);
//3.通过UDP套接字 发送 UDP数据包
socket.send(p);
//4.关闭套接字
socket.close();
}
}
/**
* java网络通信 - UDP - 数据包接收
*/
public class Demo03 {
public static void main(String[] args) throws Exception {
//1.创建UDP套接字
DatagramSocket socket = new DatagramSocket(44444);
//2.接收数据
byte [] data = new byte [1024];
DatagramPacket p = new DatagramPacket(data, data.length);
socket.receive(p);
//3.获取其中的信息
String ip = p.getAddress().getHostAddress();
int port = p.getPort();
int length = p.getLength();
// byte[] getData();返回接收数据包中缓存的数据
String str = new String(p.getData());
System.out.println(ip+"~"+port+"~"+length+"~"+str);
//4.关闭套接字
socket.close();
}
}
案例:实现单人聊天
/*
* 单机聊天
*/
public class DemoT2 {
// 负责发送数据
public static void main(String[] args) {
Scanner scanner = null;
DatagramSocket socket = null;
DatagramPacket p = null;
try {
while (true) {
scanner = new Scanner(System.in);
System.out.println("请输入你要发送的数据:");
String msg = scanner.nextLine();
// 创建UDP网络开发接口类,负责数据的收发
socket = new DatagramSocket();
byte[] data = msg.getBytes();
// 创建UDP的数据包,包含数据 数据的长度 数据的目的地和端口
p = new DatagramPacket(data, data.length, InetAddress.getByName("127.0.0.1"), 8888);
// 发送数据包
socket.send(p);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 关闭资源
if (scanner != null) {
scanner.close();
}
if (socket != null) {
socket.close();
}
}
}
}
class Rev {
// 负责接收数据
public static void main(String[] args) {
DatagramSocket socket = null;
try {
// 创建UDP网络开发接口类,负责数据的收发
socket = new DatagramSocket(8888);
while (true) {
byte[] data = new byte[1024];
// 创建UDP的数据包,包含数据 数据的长度 数据的目的地和端口
DatagramPacket p = new DatagramPacket(data, data.length);
// 接收数据包中的数据
socket.receive(p);
// 将数据包中内部缓冲数组中的数据输出
String str = new String(p.getData());
// 获取客户端的ip
String ip = p.getAddress().getHostAddress();
// 获取服务端监听的端口号
int port = p.getPort();
System.out.println(ip+"通过"+port+"发送:"+str);
}
} catch (Exception e) {
e.printStackTrace();
} finally{
socket.close();
}
}
}
//想要实现广播:需把ip地址改成255.255.255.255即可
public class DemoT2 {
public static void main(String[] args) {
new Thread(new Receiver()).start();
new Thread(new Sender()).start();
/***接收者启动成功!
***发送者启动成功!
----------------------
请输入接受者ip和端口:
localhost:44444
请输入消息:
loving
----------------------
----------------------
请输入接受者ip和端口:
######客户端接收到cn.bing.com[127.0.0.1]发送的消息:loving*/
}
}
class Receiver implements Runnable{
@Override
public void run() {
try {
DatagramSocket socket = new DatagramSocket(44444);
System.out.println("***接收者启动成功!");
while(true){
byte [] data = new byte[1024];
DatagramPacket p = new DatagramPacket(data, data.length);
socket.receive(p);
String ip = p.getAddress().getHostAddress();
String hostname = p.getAddress().getHostName();
String msg = new String(p.getData());
System.out.println("######客户端接收到"+hostname+"["+ip+"]"+"发送的消息:"+msg);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
class Sender implements Runnable{
@Override
public void run() {
DatagramSocket socket = null;
Scanner scanner = null;
try {
socket = new DatagramSocket();
scanner = new Scanner(System.in);
System.out.println("***发送者启动成功!");
while(true){
System.out.println("----------------------");
System.out.println("请输入接受者ip和端口:");
String line = scanner.nextLine();
String ip = line.split(":")[0];
int port = Integer.parseInt(line.split(":")[1]);
System.out.println("请输入消息:");
String msg = scanner.nextLine();
DatagramPacket packet = new DatagramPacket(msg.getBytes(), msg.getBytes().length,InetAddress.getByName(ip),port);
socket.send(packet);
System.out.println("----------------------");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (socket!=null) {
socket.close();
}
if(scanner!=null){
scanner.close();
}
}
}
}
Socket实现TCP
需要经历三次握手建立连接
用流来传输大量数据
有数据包重发机制 实现可靠的数据传输
可以双向传输数据
TCP基于连接工作 连接具有两端 称为服务器端 和 客户端
把主动发起请求的一端 称为客户端 被动等待请求 接收连接的一方称之为 服务器端
代表soket地址的类:
SocketAddress - InetSocketAddress
构造方法:
InetSocketAddress(InetAddress addr, int port)
InetSocketAddress(String hostname, int port)
其他方法:
InetAddress getAddress()
String getHostName()
int getPort()
客户端:
java.net.Socket
构造方法:
Socket()
Socket(InetAddress address, int port)
Socket(String host, int port)
其他方法:
void connect(SocketAddress endpoint) //开始连接服务器
void connect(SocketAddress endpoint, int timeout) //开始连接服务器,额外指定超时时间
InetAddress getInetAddress() //获取连接的地址
int getPort() //获取连接的端口
InputStream getInputStream() //获取输入流
OutputStream getOutputStream() //获取输出流
void shutdownInput() //结束对输入流的读取,不会关闭流,只是无法再从流中读取数据
void shutdownOutput() //结束对输出流的输入,不会关闭流,只是无法再向流中写出数据
boolean isInputShutdown() //判断输入流是否已经被置于结束状态
boolean isOutputShutdown() //判断输出流是否已经被置于结束状态
void close() //关闭套接字 自动关闭底层输入输出流
服务端:
java.net.ServerSocket
构造方法:
ServerSocket()
ServerSocket(int port)
其他方法:
Socket accept() //开始接受连接 一旦调用 就进入阻塞状态 直到有一个客户端连接过来 才会解除阻塞继续运行
void close() //关闭套接字 自动关闭底层输入输出流
InetAddress getInetAddress() //获取ip
int getLocalPort() //获取本地监听端口
C-S端通信模型
C-S的全称为(Client-Server):客户端-服务器端
客户端与服务端通信模型如下:
服务端创建ServerSocket
通过调用ServerSocket的accept方法监听客户端的连接
客户端创建Socket并指定服务端的地址以及端口来建立与服务端的连接
当服务端accept发现客户端连接后,获取对应该客户端的Socket
双方通过Socket分别获取对应的输入与输出流进行数据通讯
通讯结束后关闭连接
粘包问题
因为TCP协议本身是传输层的协议
只负责数据的收发 并不会关心数据本身的意义
所以当 连续发送多段数据时 TCP底层的流 会按照自身需要 将数据封装到数据包中发送
此时 同一段数据可能被 拆分到多个包中 也可能多段数据被放在同一个包中
所以接收端 无法根据包来识别数据的分段
经过tcp传输后 数据的分段的信息相当于丢失了 无法区分出数据应该从哪里分段
这个问题 称之为tcp协议在应用中产生了粘包问题
如何判断边界
解决方法:
使用特定分隔符 标识数据之间的间隔
缺点:如果数据中本身就包含特定分隔符 则需要 进行转义
发送固定长度的数据
缺点:数据长度受到制约 对于短数据需要发送大量空数据浪费资源 对于长数据 有可能无法发送
使用协议:
利用协议规定数据传输的格式 来携带辅助信息解决粘包问题
**协议 是通信双方互相约定的通信交往的相关机制(通信的方式、数据的格式) 。
私有协议--自定义的协议,只能在一定范围内使用。
公共协议:是由国际组织 定义的通用的协议 称之为 公有协议。在企业开发中 两种协议都有所应用。
相对来说 公有协议 通用 可靠
有协议 私密性更好 定制能力更强
下一篇:
HTML-JS-CSS-JQ