网络编程

概述

地球村,也译为世界村(global village),对地球的一种比喻说法现代科技的迅速发展,缩小了地球上的时空距离,国际交往日益频繁便利,因而整个地球就如同是茫茫宇宙中的一个小村落。

在以前,人们通过邮寄信件交流,现在人们通过网络包进行交流,人们交流沟通的速度和便捷度比以前高出了成百上千倍不止。

当然,在以前和现在,虽然交流方式变了,但仍然存在一些相似性。

在以前,我们通过信件交流,在寄信前,我们需要:

  • 对方的地址;
  • 对方的姓名;
  • 自己的地址;
  • 邮徽

在当今世界,我们将邮寄的方式换成了网络包,相应的我们需要:

  • 对方的IP地址(对应对方的地址);
  • 对方的端口号(对于对方监听信息的应用);
  • 自己的IP;
  • 相应的网络传输规则;

这么一对比看来,其实无论是以前还是现在,我们沟通交流的方式其实还是很类似的,不如说是科学技术的进步来源于生活,而又高于生活,技术进步又反馈于生活。

再比如,网络包在网络传输中,最常见,最重要的就是TCP协议和UDP协议,他们都属于网络传输协议。这就好比如今的汽车,并不是谁都能开滴滴去提供运输服务赚钱的,你得先要拿到驾照,有了驾照之后你才能上路拉客而不用担心开到一半被交警查车,赔了夫人又折兵。这里的驾照,就相当于TCP/UDP传输协议,要想让我们的网络包在网络世界里传输,就少不了他俩的身影。当然,网络的世界里也不仅仅只有TCP、UDP这两种传输协议,只不过这两货是最通用最重要的!这就好比骑着白马的不一定是唐僧,也有可能是白马王子。又好比在路上跑的不一定是车,也可以是飞机,是战斗机。

这里咱再对他俩做一个细分,举两个例子。TCP协议就像打电话,打电话是两个人的事,首先你得确保电话能够打通,接着你得确认对方有人接听,然后对方也确认沟通的对象,这样才能算一次牢靠的沟通。

而UDP呢,就像是你发短信,只需要编辑好短信(封好网络包),点击完发送后,后面的事就完全不管了,而你的短信也可能会因为某些原因导致对方没接受到。

无论是TCP协议还是UDP协议,他们都要经过计算机网络传输,就好像开车上路一样,没有修好的路光有车,你面前是一片绿色的森林,这车往哪儿开呢?这不就扯嘛!

那么,什么是计算机网络呢?计算机网络,是指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路(有线或无线)连接起来,在网络操作系统,网络管理软件及网络通信协议(http、tcp、udp)的管理和协调下,实现资源共享和信息传递的计算机系统。

你可能会感觉到有点困惑,其实也挺好理解的,咋一看这不就是咱四通八达的交通嘛?道路四通八达,连接着我们要去的地方,在红绿灯等交通规范设施的约束下,咱们秩序井然的开着车,可以有目的的出发(TCP),也可以漫无目的的四处游荡(UDP)。

好了,大白话讲到这里,接下来回到我们的主题网络编程了~

网络编程目的

其实目的很简单,我们学习网络编程,主要是为了跑出我们的“圈子”。在没学网络编程之前,我们都是在自己的电脑上跑程序,可“圈子”太小,总有天会腻烦,这不就想着往圈外跑跑,看看外面的世界是多么美好啊!

总结来说,就一句话,我们学网络编程,就是为了我们的程序能够运行在其他地方而不仅仅限于我们的电脑,能够与其他电脑进行数据交换和通信。那么,为了达到这个目的,我们应该怎么做呢?接下来就讲讲我们的主要内容了:

  • 如何准确的定位网络上的一台主机 192.168.16.124:端口,定位到这个计算机上的某个资源;
  • 找到了这个主机,如何传输数据呢?

javaweb开发指的是网页编程 B/S架构(通过浏览器访问)
网络编程:TCP/IP C/S架构(客户端—qq)

网络通信的要素

如何实现网络通信:

  1. 通信双方的地址:IP
  2. 端口号

所以通过IP+端口和具体规则就能定位到网络上具体的计算机上具体的应用

规则:

http、ftp、smtp、tcp、udp…

网络模型

TCP/IP四层参考模型(实际应用)

  • 应用层
  • 传输层
  • 网络层
  • 数据链路层

OSI七层参考模型(理想化,并未真正应用)

  • 应用层
    • HTTP
    • TFTP
    • FTP
    • NFS
    • WAIS
    • SMTP
  • 表示层
    • Telnet
    • Rlogin
    • SNMP
    • Gopher
  • 会话层
    • SMTP
    • DNS
  • 传输层
    • TCP
    • UDP
  • 网络层
    • IP
    • ICMP
    • ARP
    • PARP
    • AKP
    • UUCP
  • 数据链路层
    • FDDI
    • Ethnernet
    • Arpanet
    • PDN
    • SLIP
    • PPP
  • 物理层
    • IEEE 802.1A
    • IEEE 802.2到IEEE 802.11

总结

1.网络编程中有两个主要问题

  • 如何准确定位到网络上的一台或者多台主机
  • 找到主机之后如何进行通信

2.网络编程中的要素

  • IP和端口号 ip
  • 网络通信协议 udp、tcp

3.万物皆对象

IP

ip地址:InetAddress

  • 唯一定位一台网络上计算机

  • 127.0.0.1:本地localhost

  • ip地址分类

    • ip地址分类(IPv4/IPv6)

      • IPv4:由4个字节组成,每个字节长度0-255(1byte=8bit=$2^8$)42亿IPv4,30亿都在北美,亚洲4亿,2011年就用尽;
      • IPv6:128位,8个无符号整数
      1
      2001:0bb2:aaaa:0015:0000:0000:1aaa:1312
    • 公网和私网分类

      • ABCD类地址如何来,一条线折两半,一半A一半B,再折线
      • 192.168.xx.xx专门给组织内部使用的
      • 域名:记忆IP问题!
        • IP:www.vip.com

在java中,我们通过InetAddress方法获取IP地址协议相关的信息,该类没有构造方法,无法new,只能由静态方法调用。具体代码案例如下:

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
package com.everweekup.Net.Tcp;

import java.net.InetAddress;
import java.net.UnknownHostException;

public class InetAddressTest {
// 测试IP
public static void main(String[] args) {
// 查询本机地址
InetAddress inetAddress1 = null;
try {
inetAddress1 = InetAddress.getByName("127.0.0.1"); // /127.0.0.1
System.out.println(inetAddress1);

InetAddress inetAddress3 = InetAddress.getByName("localhost");
System.out.println(inetAddress3); // localhost/127.0.0.1

InetAddress inetAddress4 = InetAddress.getLocalHost();
// 得到主机拥有者和IP
System.out.println(inetAddress4); // LAPTOP-VGIQAEFL/192.168.10.1


} catch (UnknownHostException e) {
e.printStackTrace();
}

// 查看网站ip地址
// 这里用try catch是为了捕获异常
InetAddress inetAddress2 = null;
try {
inetAddress2 = InetAddress.getByName("www.baidu.com");
System.out.println(inetAddress2); // www.baidu.com/183.232.231.172

// 常用方法
System.out.println(inetAddress2.getAddress()); // [B@677327b6 返回字节数组
System.out.println(inetAddress2.getCanonicalHostName()); //183.232.231.172 规范名字--IP
System.out.println(inetAddress2.getHostAddress()); //183.232.231.172 IP
System.out.println(inetAddress2.getHostName()); //www.baidu.com 百度域名

} catch (UnknownHostException e) {
e.printStackTrace();
}


}
}

端口

端口表示计算机上的一个程序的进程;

  • 好比一栋楼,是一个大的IP,每户人家就是一个端口;

  • 不同的进程有不同的端口号!用来区分软件!

  • 被规定0~65535

  • TCP、UDP端口每一个都有65535个端口,tcp用了80端口,udp也用了80端口,两者不冲突。单个协议下端口号不能冲突。

  • 端口分类

    • 共有端口0~1023 (这些端口会被内置的进程或者服务使用,不要占用)

      • HTTP:80
      • HTTPS:443
      • FTP:21
      • SSH:22
      • Telent:23 (远程监听)
    • 程序注册端口:1024~49151,分配给用户或者程序

      • Tomcat:8080
      • MySQL: 3306
      • Oracle: 1521
    • 动态、私有端口:49152~65535

      • 63342:idea默认关于网页的端口
      1
      2
      3
      4
      5
      # windows下终端操作
      netstat -ano:查看所有的端口
      netstat -ano | findstr "5900":管道流查询,查看指定端口
      tasklist|findstr "" :查看指定端口的进程
      ctrl+shift+esc:打开任务管理器
      1
        
    • InetSocketAddress代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    package com.everweekup.Net.Tcp;

    import java.net.InetSocketAddress;

    public class TestInetSocketAddress {
    public static void main(String[] args) {
    // new InetSocketAddress
    InetSocketAddress SocketAddress = new InetSocketAddress("127.0.0.1", 8080);
    System.out.println(SocketAddress); // /127.0.0.1:8080

    System.out.println(SocketAddress.getAddress()); // /127.0.0.1
    System.out.println(SocketAddress.getHostName()); // www.xmind.net 可以在windows的hosts文件配置 hostname 映射的主机名
    System.out.println(SocketAddress.getPort()); // 8080 端口

    }
    }

主机hosts配置

通信协议

协议:约定,就好比我们现在说的是普通话。

网络通信协议:速率、传输码率、代码结构、传输控制…

问题:非常的复杂?

大事化小:分层!

TCP/IP协议簇:实际上是一组协议

重要:

  • TCP协议:用户传输协议
  • UDP:用户数据报协议

出名的协议:

  • TCP:
  • IP:网络互联协议

TCP 和 UDP对比:

TCP:打电话

  • 连接、稳定
  • 三次握手,四次挥手
1
2
3
4
5
6
7
8
9
A:你瞅啥? 第一次:告诉B,我们能交流?
B:瞅你咋地? 第二次:回应,并确定A是否还在
A:干一场! 第三次:最少需要三次才能保证稳定连接

A要和B断开了
A:告诉B我要断开了
B:我知道你要断开了
B:你真的断开了吗?
A:我真的断开了!
  • 客户端、服务端
  • 传输完成,释放连接,效率低

UDP:发短信

  • 不连接、不稳定
  • 客户端、服务端:没有明确界限,可以是客户端给客户端发
  • 不管有没有准备好,都可以发给你
  • 导弹攻击
  • DDOS:洪水攻击!发一堆无用信息包,堆积端口,饱和攻击,导致网络瘫痪

TCP

客户端

  1. 通过socket连接服务器
  2. 发送消息
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
package com.everweekup.Net;

import java.io.IOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;

// 客户端
public class TcpClientDemo01 {
public static void main(String[] args) {
Socket socket = null;
OutputStream os = null;

try {
// 1.要知道服务器的地址
InetAddress serverIP = InetAddress.getByName("127.0.0.1");
// 2.端口号
int port = 9999;
// 3. 创建一个socket连接
socket = new Socket(serverIP, port);
// 4.发送消息 IO流
// 消息流出
os = socket.getOutputStream();

os.write("你好,欢迎光临小店".getBytes()); // 发送完,服务端接受
} catch (Exception e) {
e.printStackTrace();
}finally{
if (os != null){
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (socket != null){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

}
}

服务器

  1. 建立服务的端口serverSocket
  2. 等待用户的连接 accept
  3. 接收用户的消息
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
package com.everweekup.Net;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class TcpServerDemo01 {
public static void main(String[] args) {
ServerSocket serverSocket = null; // 提升作用域,避免后面关闭时警告
Socket socket = null;
InputStream is = null;
ByteArrayOutputStream baos = null;

// java所有的网络编程又叫做套接字编程
try {
// 1.我得有一个地址
serverSocket = new ServerSocket(9999);
// 不停收消息
while (true){
// 2.等待客户端连接
// 连接建立后socket就是客户端的socket
socket = serverSocket.accept();
// 3.读取客户端的消息
is = socket.getInputStream();
// 管道流
baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len;
while ((len = is.read(buffer)) != -1){
baos.write(buffer, 0, len);
}

System.out.println(baos.toString());
}

} catch (IOException e) {
e.printStackTrace();
}finally{
// 关闭资源
if (baos != null){
try {
baos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (is != null){
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (socket != null){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (serverSocket != null){
try {
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}

文件上传

读取文件,将其变成一个流,然后传出去。

客户端

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
package com.everweekup.Net;

import java.io.*;
import java.net.InetAddress;
import java.net.Socket;

public class TcpClientFileDemo01 {
public static void main(String[] args) throws Exception {
// 1.创建一个socket连接
Socket socket = new Socket(InetAddress.getByName("127.0.0.1"), 9000);
// 2.创建一个输出流
OutputStream os = socket.getOutputStream();

// 3.文件流
FileInputStream fils = new FileInputStream(new File("G:\\Desktop\\Learn\\77天Java学习\\java_l\\src\\com\\everweekup\\Net\\wallhaven-6od3px.jpg"));

// 4.写出文件
byte[] buffer = new byte[1024];

int len;

while((len = fils.read(buffer)) != -1){
os.write(buffer, 0 ,len);
}
// 通知服务器我已经发完了
socket.shutdownOutput(); // 我已经传输完了

// 确定服务器接受完毕,才能断开连接
InputStream inputStream = socket.getInputStream();
// String 接受字符串用byte管道,消息只有通过管道才能被识别
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer2 = new byte[1024];
int len2;
while((len2=inputStream.read(buffer2)) != -1){
baos.write(buffer2, 0, len2);
}

System.out.println(baos.toString());


baos.close();
inputStream.close();

// 5.关闭 先写后关
fils.close();
os.close();
socket.close();
}
}

服务端

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
package com.everweekup.Net;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

public class TcpServerFileDemo01 {
public static void main(String[] args) throws Exception {
// 1.创建服务端口
ServerSocket serverSocket = new ServerSocket(9000);
// 2.监听客户端连接
Socket socket = serverSocket.accept(); // 阻塞式监听,会一直等待客户端连接
// 3. 获取输入流
InputStream is = socket.getInputStream();

// 4.文件输出
FileOutputStream fos = new FileOutputStream(new File("receive1.jpg"));

// 通知客户端我接收完毕了
OutputStream os = socket.getOutputStream();
os.write("我接收完毕,你可以断开了".getBytes());

byte[] buffer = new byte[1024];
int len;

while((len=is.read(buffer)) != -1){
fos.write(buffer, 0, len);
}

// 5.关闭资源
fos.close();
is.close();
socket.close();
serverSocket.close();
}
}

TomCat

服务端

  • 自定义S
  • Tomcat服务器S:Java后台开发

客户端

  • 自定义 C
  • 浏览器 B

安装

Tomcat下载地址

解压缩包后进入,启动运行:(bat是windows的可执行程序,sh是linux的)

image-20210411010716380

可以看到终端出现乱码,主要是因为windows和tomcat的编码格式不一样,我们可以修改。

去tomcat根目录下的conf目录修改logging的编码配置:

修改后保存重启,发现输出不再乱码:

应用

我们去到tomcat的根目录下的webapps文件夹,创建一个文件夹,在里面写一些文件,再在浏览器输入8080(tomcat默认端口)访问这些文件,实现模拟资源的访问。

首先打开的是tomcat的主页,主页资源可以在webapps里找到:

接着访问我们的资源:

可以看到,资源成功访问,不过是乱码的,这就交给你自己去配置了~

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
package com.everweekup.Net.Udp;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.nio.charset.StandardCharsets;

// 不需要连接服务器
public class UdpClientDemo01 {
public static void main(String[] args) throws Exception {
// 1.建立一个socket
DatagramSocket socket = new DatagramSocket();
// 3.发送给谁
InetAddress localhost = InetAddress.getByName("localhost");
int port = 9090;

// 2.建个包\
String message = "你好,服务器";
/*
1.数据
2.数据长度的起始
3.要发送给谁
*/
DatagramPacket packet = new DatagramPacket(message.getBytes(), 0, message.getBytes().length, localhost, port);

//4.发送包
socket.send(packet);

// 5.关闭流
}
}

消息接收端

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
package com.everweekup.Net.Udp;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

// UDP 没有server和client的概念
// 还是要等待客户端连接
public class UdpServerDemo01 {
public static void main(String[] args) throws Exception {
// 开放端口
DatagramSocket socket = new DatagramSocket(9090);
// 接受数据包
byte[] buffer = new byte[1024];
DatagramPacket packet = new DatagramPacket(buffer, 0, buffer.length);

socket.receive(packet); // 阻塞接收
// 包可以查看从哪里来的
System.out.println(packet.getAddress().getHostAddress());
System.out.println(new String (packet.getData(), 0, packet.getLength()));

// 关闭连接

}
}

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
package com.everweekup.Net.Udp.Chat;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketException;

public class UdpSenderDemo01 {
public static void main(String[] args) throws Exception {
DatagramSocket socket = new DatagramSocket(8888);

// 准备数据
// 从控制台读取数据消息变成buffer
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));

while (true){
String data = reader.readLine();
// 要转成字节才能读取数据
byte[] datas = data.getBytes();

// datagrampacket利用方法重载机制,所以后面的hostname和port有多种写法
DatagramPacket packet = new DatagramPacket(datas, 0, datas.length, new InetSocketAddress("localhost", 6666));

socket.send(packet);

if (data.equals("bye")){
break;
}
}
socket.close();
}
}

循环接收

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
package com.everweekup.Net.Udp.Chat;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

public class UdpReceiverDemo01 {
public static void main(String[] args) throws Exception {
DatagramSocket socket = new DatagramSocket(6666);

while (true) {
// 准备接收包裹
byte[] container = new byte[1024];

DatagramPacket packet = new DatagramPacket(container, 0, container.length);
socket.receive(packet);// 阻塞式接收包裹

// 断开连接设置
byte[] data = packet.getData();
String receiveData = new String(data, 0, data.length);
System.out.println(receiveData);

if (receiveData.equals("bye")){
break;
}
}

socket.close();
}
}

思考:为什么最后输入“bye”,receiver程序仍不会停止呢?

UDP多线程在线咨询

在线咨询:两个人都可以式发送方或者接收方

首先,先创建两个线程,一个send线程,一个receive线程:

send线程代码:

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
package com.everweekup.Net.Udp.Chat;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketException;

// 线程要集成runnable接口
public class TalkSend implements Runnable{

DatagramSocket socket = null;
BufferedReader reader = null;

private int fromPort;
private String toIP;
private int toPort;


// 初始化数据放在构造器里


public TalkSend(int fromPort, String toIP, int toPort) {
this.fromPort = fromPort;
this.toIP = toIP;
this.toPort = toPort;

try {
socket = new DatagramSocket(this.fromPort);
// 准备数据
// 从控制台读取数据消息变成buffer
reader = new BufferedReader(new InputStreamReader(System.in));
} catch (SocketException e) {
e.printStackTrace();
}
}

// 只管不停发送消息
@Override
public void run() {
while (true){
try{
String data = reader.readLine();
// 要转成字节才能读取数据
byte[] datas = data.getBytes();

// datagrampacket利用方法重载机制,所以后面的hostname和port有多种写法
DatagramPacket packet = new DatagramPacket(datas, 0, datas.length, new InetSocketAddress(this.toIP, this.toPort));

socket.send(packet);

if (data.equals("bye")){
break; }
}catch(Exception e){
e.printStackTrace();
}
}
socket.close();
}
}

receive线程代码:

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
package com.everweekup.Net.Udp.Chat;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

public class TalkReceiverDemo01 implements Runnable {

DatagramSocket socket = null;

private int port;
private String msgFrom;

public TalkReceiverDemo01(int port, String msgFrom) {
this.port = port;
this.msgFrom = msgFrom;
try {
socket = new DatagramSocket(this.port);
} catch (SocketException e) {
e.printStackTrace();
}
}

@Override
public void run() {

while (true) {
try {
// 准备接收包裹
byte[] container = new byte[1024];

DatagramPacket packet = new DatagramPacket(container, 0, container.length);
socket.receive(packet);// 阻塞式接收包裹

// 断开连接设置
byte[] data = packet.getData();
// data.length != packet.getLength()
//int packet.getLength(): 返回接收到的数据的长度。
System.out.println("接收空间长度"+data.length); // 1024 因为之前定义data长度预留了1024的空间 ---> 如果我发送超过1024长度的数据,会报错吗?
// 经检验,超过1024的数据,将只截取到1024,超过部分丢失
System.out.println("实际接收的数据长度"+packet.getLength()); // 3

String receiveData = new String(data, 0, packet.getLength());
System.out.println(this.msgFrom+":"+receiveData);

// 1.第一种方式: 要注意,发送过来的消息可能有空格,需要先去除字符串两边的空客,否则下面逻辑判断错误一直不会停止
// receiveData = receiveData.trim();

if (receiveData.equals("bye")){
System.out.println("收到!");
break;
}
} catch (IOException e) {
e.printStackTrace();
}
}

socket.close();
}
}

接下来就是开始多线程,创建Student和Teacher对象,各自分别启动两个线程,实现对话:

Student:

1
2
3
4
5
6
7
8
9
package com.everweekup.Net.Udp.Chat;

public class TalkStudent {
public static void main(String[] args) {
// 开启两个线程,既有可能是发送方,也可能是接收方
new Thread(new TalkSend(7777, "localhost", 9999)).start();
new Thread(new TalkReceiverDemo01(8888, "老师")).start();
}
}

Teacher:

1
2
3
4
5
6
7
8
package com.everweekup.Net.Udp.Chat;

public class TalkTeacher {
public static void main(String[] args) {
new Thread(new TalkSend(5555, "localhost", 8888)).start();
new Thread(new TalkReceiverDemo01(9999, "学生")).start();
}
}

URL

统一资源定位符:用于定位资源,定位互联网上的某个资源

1
协议://ip地址:端口//项目名/资源  # 可以少,但是不能多
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
package com.everweekup.Net.Url;

import java.net.MalformedURLException;
import java.net.URL;

public class URLDemo01 {
public static void main(String[] args) throws MalformedURLException {
URL url = new URL("http://localhost:8080/helloworld/index.jsp?username=hj&password=123");
/*
http
localhost
8080
/helloworld/index.jsp
/helloworld/index.jsp?username=hj&password=123
username=hj&password=123
*/
System.out.println(url.getProtocol()); // 协议名
System.out.println(url.getHost()); // 主机ip
System.out.println(url.getPort()); // 端口
System.out.println(url.getPath()); // 文件地址
System.out.println(url.getFile()); // 文件全路径
System.out.println(url.getQuery()); // 参数

}
}

参考链接

https://blog.51cto.com/yanjiu/2149866

https://www.kuangstudy.com/