报文、协议、Mac地址 1.报文段
报文段是指TCP/IP协议网络传输中,起着路由导航作用
用以查询各个网络路由网段、IP地址、交换协议等IP数据包
报文段充当整个TCP/IP协议数据包的导航路由功能
报文段在传输过程中会不断的封装成组、包、帧来传输
封装方式就是添加一些控制信息组成的首部,即报文头
2.传输协议
一种规定,约束
简单来说:A > B的传输数据,B能识别,反之B > A的传输数据A也能识别。这就是协议
3.Mac地址
Media Access Control或者Medium Access Control
译为媒体访问控制,或称物理地址、硬件地址
用来定义网络设备的位置
形如:44-45-53-54-00-00 与身份证类似
IP、端口以及远程服务器
IP地址
互联网协议地址(Internet Protocol Address)
是 分配给网络上使用网际协议(Internet Protocol ,IP)的设备数字 标签
常见IP地址分为IPV4 与 IPV6
IP地址由32位二进制数组成,常以xxx.xxx.xxx.xxx形式表现,每组xxx代表小于或等于255的10进制数
IPV4:
如:208.80.152.2
分为A、B、C、D、E五大类,其中E类属于特殊保留地址
总数量:42亿个,最终于2011年2月2日用尽
如果主机号全是1,那么这个地址为直接广播地址
IP地址”255.255.255.255”为受限广播地址
IPV6:
总共有128位长,IPV6地址的表达形式,一般采用32个十六位进制数。也可以想象为1632个
由两个逻辑部分组成:一个64位的网络前缀和一个64位的主机地址,主机地址通常根据物理地址自动生成,叫做EUI-64(或者64位扩展唯一标识)
2001:0db8:85a3:0000:1319:8a2e:0370:7344
IPV4转换为IPV6一定可行,IPV6转换为IPV4不一定可行
端口
如果把IP地址比作一间房子,端口就是出入这间房子的门或窗户
不同门窗户后有不同的人(代表着不同程序的进程),房子中的用户与外界交流的出口
外界鸽子(信息)飞到不同窗户也就是给不同的人(程序进程)传递信息
从0到1023号端口以及1024到49151号端口都是特殊端口
计算机之间依照互联网传输层TCP/IP协议的协议通信,不同协议对应不同端口
49152到65535号端口属于”动态端口”范围,没有端口可以被正式的注册占用
数据传输层次:
3.远程服务器:
局域网:一般而言,家里的环境以及公司相互电脑之间环境都属于局域网
我与你们之间的电脑属于互联网,而非局域网
默认的:我的电脑无法直接链接到你们的电脑:不在同一局域网,可通过远程服务器通信
Socket UDP UDP是什么
User Datagram Protocol
用户数据报协议,又称用户数据报文协议
是一个简单的面向数据报 的传输层 协议,正式规范为RFC 768
用户数据协议,非连接协议
为什么不可靠
一旦把应用程序发给网络层的数据发送出去,就不保留数据备份
UDP在IP数据报的头部仅仅加入了复用和数据校验(字段)
发送端产生数据,接收端从网络中抓取数据
结构简单,无校验,速度快,容易丢包,可广播
UDP能做什么
DNS,TFTP,SNMP
视频,音频,普通数据(无关紧要数据)
UDP包最大长度
16位 -> 2字节 存储长度信息
2^16 -1 = 64k -1 = 65536 -1 = 65535
自身协议占用:32 + 32位 = 64位 = 8字节,而在IP层进行封装后的IP包头占去20字节。
65535 - 8 - 20 = 65507 byte
UDP核心API API-DatagramSocket
用于接收与发送UDP的类
负责发送某一个UDP包,或者接收UDP包
不同于TCP,UDP并没有合并到Socket API中
DatagramSocket()创建简单实例,不指定端口与IP
DatagramSocket(int port)创建监听某端口的实例
DatagramSocket(int port,InetAddress localAddr)创建固定端口指定IP的实例
receive(DatagramPacket d) : 接收
send(DatagramPacket d) :发送
setSoTimeout(int timeout):设置超时,毫秒
close()关闭,释放资源
API-DatagramPacket
用于处理报文
将byte数组、目标地址、目标端口等数据包封装成报文或者将报文拆卸成byte数组
是UDP的发送实体,也是UDP的接收实体
DatagramPacket(byte[] buf,int offset,int length,InetAddress address,int port):
前面3个参数指定buff的使用区间
后面2个参数指定目标机器地址,与端口(发送端地址,端口 )
DatagramPacket(byte[] buf,int length,SocketAddress address)
前面2个参数指定buff的使用区间
SocketAddress 相当于InetAddress + port
setData(byte[] buf,int offset,int length)
setData(byte[] buf)
setLength(int length)
getData()、getOffset()、getLength()
setAddress(InetAddress iaddr)、setPort(int port)
getAddress()、getPort()
setSocketAddress(SocketAddress address)
getSocketAddress()
UDP单播、广播、多播 单播:1对1
多播(组播):1对多,一次给一个组发
广播:给所有设备发送
IP地址类别
广播地址
255.255.255.255 为受限广播地址
C网广播地址一般为:xxx.xxx.xxx.255(192.168.1.255)
D类IP地址为多播预留
IP地址构成
广播地址运算 比如:
根据子网掩码:
255.255.255.192 -> 11111111.11111111.11111111.11000000
可划分网段:最后八位2个1,即:2^2 = 4 个
063、64127、128191、192255
由于例子IP在第一个网段,所以广播地址:192.168.124.63
广播通信问题
如图,主机一发送广播后主机二不能收到,因为不在同一个网段,广播地址不一样。
局域网搜索案例
UDP接收消息并回送功能实现
UDP接收者:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 public class UDPProvider { public static void main (String[] args) throws IOException { System.out.println("UDPProvider started..." ); DatagramSocket datagramSocket = new DatagramSocket(20000 ); final byte [] buf = new byte [512 ]; DatagramPacket receivePacket = new DatagramPacket(buf, buf.length); datagramSocket.receive(receivePacket); String ip = receivePacket.getAddress().getHostAddress(); int port = receivePacket.getPort(); int dataLength = receivePacket.getLength(); String receiveDate = new String(receivePacket.getData(), 0 , dataLength); System.out.println("UDPProvider receive from ip:" +ip+"\tport:" +port+"\tdata:" +receiveDate); String responseData = "Receive data with len :" + dataLength; byte [] receiveDateBytes = responseData.getBytes(); DatagramPacket responsePacket = new DatagramPacket(receiveDateBytes, receiveDateBytes.length, receivePacket.getAddress(), receivePacket.getPort()); datagramSocket.send(responsePacket); System.out.println("UDPProvider finished..." ); datagramSocket.close(); } }
UDP搜索者:搜索提供方
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 public class UDPSearcher { public static void main (String[] args) throws IOException { System.out.println("UDPSearcher started..." ); DatagramSocket datagramSocket = new DatagramSocket(); String requestData = "Only the dead do not make mistakes." ; byte [] requestDateBytes = requestData.getBytes(); DatagramPacket requestPacket = new DatagramPacket(requestDateBytes, requestDateBytes.length); requestPacket.setAddress(InetAddress.getLocalHost()); requestPacket.setPort(20000 ); datagramSocket.send(requestPacket); final byte [] buf = new byte [512 ]; DatagramPacket receivePacket = new DatagramPacket(buf, buf.length); datagramSocket.receive(receivePacket); String ip = receivePacket.getAddress().getHostAddress(); int port = receivePacket.getPort(); int dataLength = receivePacket.getLength(); String receiveDate = new String(receivePacket.getData(), 0 , dataLength); System.out.println("UDPSearcher receive from ip:" +ip+"\tport:" +port+"\tdata:" +receiveDate); System.out.println("UDPSearcher finished..." ); datagramSocket.close(); } }
UDP局域网广播发送实现
UDP局域网回送消息实现
己方,监听20000端口,并回送指定30000端口数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 public class UDPProvider { public static void main (String[] args) throws IOException { String sn = UUID.randomUUID().toString(); Provider provider = new Provider(sn); provider.start(); System.in.read(); provider.exit(); } private static class Provider extends Thread { private final String sn; private boolean done = false ; private DatagramSocket datagramSocket = null ; public Provider (String sn) { super (); this .sn = sn; } @Override public void run () { super .run(); System.out.println("UDPProvider started..." ); try { datagramSocket = new DatagramSocket(20000 ); while (!done){ final byte [] buf = new byte [512 ]; DatagramPacket receivePacket = new DatagramPacket(buf, buf.length); datagramSocket.receive(receivePacket); String ip = receivePacket.getAddress().getHostAddress(); int port = receivePacket.getPort(); int dataLength = receivePacket.getLength(); String receiveData = new String(receivePacket.getData(), 0 , dataLength); System.out.println("UDPProvider receive from ip:" +ip+"\tport:" +port+"\tdata:" +receiveData); int responsePort = MessageCreator.parsePort(receiveData); if (responsePort != -1 ){ String responseData = MessageCreator.buildWithSn(sn); byte [] receiveDateBytes = responseData.getBytes(); DatagramPacket responsePacket = new DatagramPacket(receiveDateBytes, receiveDateBytes.length, receivePacket.getAddress(), responsePort); datagramSocket.send(responsePacket); } } }catch (IOException e){ e.printStackTrace(); }finally { close(); } System.out.println("UDPProvider finished..." ); } private void close () { if (datagramSocket != null ){ datagramSocket.close(); datagramSocket = null ; } } void exit () { done = true ; close(); } } }
搜索端,发送20000端口数据暗号,监听30000端口回复
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 public class UDPSearcher { private static final int LISTEN_PORT = 30000 ; public static void main (String[] args) throws IOException, InterruptedException { Listener listen = listen(); sendBroadcast(); System.in.read(); listen.getDevicesAndClose(); } private static Listener listen () throws InterruptedException { System.out.println("UDPSearcher start listen..." ); CountDownLatch countDownLatch = new CountDownLatch(1 ); Listener listener = new Listener(LISTEN_PORT, countDownLatch); listener.start(); countDownLatch.await(); return listener; } private static void sendBroadcast () throws IOException { System.out.println("UDPSearcher started..." ); DatagramSocket datagramSocket = new DatagramSocket(); String requestData = MessageCreator.buildWithPort(LISTEN_PORT); byte [] requestDateBytes = requestData.getBytes(); DatagramPacket requestPacket = new DatagramPacket(requestDateBytes, requestDateBytes.length); requestPacket.setAddress(InetAddress.getByName("255.255.255.255" )); requestPacket.setPort(20000 ); datagramSocket.send(requestPacket); datagramSocket.close(); System.out.println("UDPSearcher finished..." ); } private static class Device { final int port; final String ip; final String sn; public Device (int port, String ip, String sn) { this .port = port; this .ip = ip; this .sn = sn; } @Override public String toString () { return "Device{" + "port=" + port + ", ip='" + ip + '\'' + ", sn='" + sn + '\'' + '}' ; } } private static class Listener extends Thread { private final int listenPort; private final CountDownLatch countDownLatch; private final List<Device> devices = new ArrayList<>(); private boolean done = false ; private DatagramSocket ds; public Listener (int listenPort,CountDownLatch countDownLatch) { super (); this .listenPort = listenPort; this .countDownLatch = countDownLatch; } @Override public void run () { super .run(); countDownLatch.countDown(); try { ds = new DatagramSocket(listenPort); while (!done){ final byte [] buf = new byte [512 ]; DatagramPacket receivePacket = new DatagramPacket(buf, buf.length); ds.receive(receivePacket); String ip = receivePacket.getAddress().getHostAddress(); int port = receivePacket.getPort(); int dataLength = receivePacket.getLength(); String receiveData = new String(receivePacket.getData(), 0 , dataLength); System.out.println("UDPSearcher receive from ip:" +ip+"\tport:" +port+"\tdata:" +receiveData); String sn = MessageCreator.parseSn(receiveData); if (sn!=null ){ Device device = new Device(port, ip, sn); devices.add(device); } } }catch (Exception e){ }finally { close(); } System.out.println("UDPSearcher listener finished." ); } private void close () { if (ds != null ){ ds.close(); ds = null ; } } void exit () { done = true ; close(); } List<Device> getDevicesAndClose () { done = true ; close(); return devices; } } }
约定一个通信方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 public class MessageCreator { private static final String SN_HEADER = "收到暗号,我是(SN):" ; private static final String PORT_HEADER = "这是暗号,请回电端口(port):" ; public static String buildWithPort (int port) { return PORT_HEADER + port; } public static int parsePort (String data) { if (data.startsWith(PORT_HEADER)){ return Integer.parseInt(data.substring(PORT_HEADER.length())); } return -1 ; } public static String buildWithSn (String sn) { return SN_HEADER + sn; } public static String parseSn (String data) { if (data.startsWith(SN_HEADER)){ return data.substring(SN_HEADER.length()); } return null ; } }
Socket TCP TCP是什么
Transmission Control Protocol(TCP)
TCP是传输控制协议 ,是一种面向连接、可靠、基于字节流 的传输层通信协议,由IETF的RFC 793定义
TCP机制
三次握手、四次挥手
具有校验机制、可靠、数据传输稳定
TCP能做什么
聊天消息传输、推送
单人语音、视频聊天等
几乎所有UDP能做到的事都能做,但需要考虑复杂性、性能问题
无法进行广播、多播等操作
TCP核心API
socket():创建一个Socket
bind():绑定一个Socket到一个本地地址和端口上
connect():连接到远程套接字
accept():接受一个新的连接
write():把数据写入到Socket输出流
read():从socket输入流读取数据
TCP连接可靠性-三次握手
比如打电话:客户端打给服务端说:可以听到我说话吗,然后服务端回复:能听到你说话,你能听到我说话吗,客户端再回复:可以听到。这时,三次握手结束,它们就建立了连接。
TCP四次挥手
首先:发送端向接收端说:我想和你断开连接了,接收端回复说OK你可以断开对我的连接了;这时发送端的输出操作可以断开了,读取操作还保留着,服务端会把还没送答的消息一一送达给接收端,送达完成后,接收端会跟发送端说:他想要关闭连接了。发送端回复说:Ok,你可以断开连接了。
传输可靠性
分片发送,排序,顺序发送,顺序组装
丢弃、超时
重发机制-定时器
案例实操 TCP传输初始化配置/传输数据 Tools.java:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class Tools { public static int byteArrayToInt (byte [] b) { return b[3 ] & 0xFF | (b[2 ] & 0xFF ) << 8 | (b[1 ] & 0xFF ) << 16 | (b[0 ] & 0xFF ) << 24 ; } public static byte [] intToByteArray(int a) { return new byte []{ (byte ) ((a >> 24 ) & 0xFF ), (byte ) ((a >> 16 ) & 0xFF ), (byte ) ((a >> 8 ) & 0xFF ), (byte ) (a & 0xFF ) }; } }
Client:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 public class Client { private static final int PORT = 20000 ; private static final int LOCAL_PORT = 20001 ; public static void main (String[] args) throws IOException { Socket socket = createSocket(); initSocket(socket); socket.connect(new InetSocketAddress(Inet4Address.getLocalHost(), PORT), 3000 ); System.out.println("已发起服务器连接,并进入后续流程~" ); System.out.println("客户端信息:" + socket.getLocalAddress() + " P:" + socket.getLocalPort()); System.out.println("服务器信息:" + socket.getInetAddress() + " P:" + socket.getPort()); try { todo(socket); } catch (Exception e) { System.out.println("异常关闭" ); } socket.close(); System.out.println("客户端已退出~" ); } private static Socket createSocket () throws IOException { Socket socket = new Socket(); socket.bind(new InetSocketAddress(Inet4Address.getLocalHost(), LOCAL_PORT)); return socket; } private static void initSocket (Socket socket) throws SocketException { socket.setSoTimeout(2000 ); socket.setReuseAddress(true ); socket.setTcpNoDelay(true ); socket.setKeepAlive(true ); socket.setSoLinger(true , 20 ); socket.setOOBInline(true ); socket.setReceiveBufferSize(64 * 1024 * 1024 ); socket.setSendBufferSize(64 * 1024 * 1024 ); socket.setPerformancePreferences(1 , 1 , 0 ); } private static void todo (Socket client) throws IOException { OutputStream outputStream = client.getOutputStream(); InputStream inputStream = client.getInputStream(); byte [] buffer = new byte [256 ]; ByteBuffer byteBuffer = ByteBuffer.wrap(buffer); byteBuffer.put((byte ) 126 ); char c = 'a' ; byteBuffer.putChar(c); int i = 2323123 ; byteBuffer.putInt(i); boolean b = true ; byteBuffer.put(b ? (byte ) 1 : (byte ) 0 ); long l = 298789739 ; byteBuffer.putLong(l); float f = 12.345f ; byteBuffer.putFloat(f); double d = 13.31241248782973 ; byteBuffer.putDouble(d); String str = "Hello你好!" ; byteBuffer.put(str.getBytes()); outputStream.write(buffer, 0 , byteBuffer.position() + 1 ); int read = inputStream.read(buffer); System.out.println("收到数量:" + read); outputStream.close(); inputStream.close(); } }
Server:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 public class Server { private static final int PORT = 20000 ; public static void main (String[] args) throws IOException { ServerSocket server = createServerSocket(); initServerSocket(server); server.bind(new InetSocketAddress(Inet4Address.getLocalHost(), PORT), 50 ); System.out.println("服务器准备就绪~" ); System.out.println("服务器信息:" + server.getInetAddress() + " P:" + server.getLocalPort()); for (; ; ) { Socket client = server.accept(); ClientHandler clientHandler = new ClientHandler(client); clientHandler.start(); } } private static ServerSocket createServerSocket () throws IOException { ServerSocket serverSocket = new ServerSocket(); return serverSocket; } private static void initServerSocket (ServerSocket serverSocket) throws IOException { serverSocket.setReuseAddress(true ); serverSocket.setReceiveBufferSize(64 * 1024 * 1024 ); serverSocket.setPerformancePreferences(1 , 1 , 1 ); } private static class ClientHandler extends Thread { private Socket socket; ClientHandler(Socket socket) { this .socket = socket; } @Override public void run () { super .run(); System.out.println("新客户端连接:" + socket.getInetAddress() + " P:" + socket.getPort()); try { OutputStream outputStream = socket.getOutputStream(); InputStream inputStream = socket.getInputStream(); byte [] buffer = new byte [256 ]; int readCount = inputStream.read(buffer); ByteBuffer byteBuffer = ByteBuffer.wrap(buffer, 0 , readCount); byte be = byteBuffer.get(); char c = byteBuffer.getChar(); int i = byteBuffer.getInt(); boolean b = byteBuffer.get() == 1 ; long l = byteBuffer.getLong(); float f = byteBuffer.getFloat(); double d = byteBuffer.getDouble(); int pos = byteBuffer.position(); String str = new String(buffer, pos, readCount - pos - 1 ); System.out.println("收到数量:" + readCount + " 数据:" + be + "\n" + c + "\n" + i + "\n" + b + "\n" + l + "\n" + f + "\n" + d + "\n" + str + "\n" ); outputStream.write(buffer, 0 , readCount); outputStream.close(); inputStream.close(); } catch (Exception e) { System.out.println("连接异常断开" ); } finally { try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } System.out.println("客户端已退出:" + socket.getInetAddress() + " P:" + socket.getPort()); } } }