1、计算机网络
网络 是指将地理位置不同的具有地理功能的计算机及其外部设备 通过通信线路链接起来,在网络操作系统,网络管理软件及网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统。
网络编程 在网络通信协议下,实现网络互联的不同的计算机上运行的程序间可以进行数据交换
2、网络编程的三要素
-
IP地址 :网络中每一台计算机得唯一的标识号
-
端口:端口就是通过唯一标识锁定的设备上的应用程序的标识。
-
协议:对数据的传输格式 传输速率 传输的步骤等做的统一的规定,将这种规则称为通信协议,双方都必须要遵守的,才能完成数据的交换。 常见的通信协议: UDP和 TCP
3、IP地址 – InetAddress
在 Java 中,使用 java.net 包下的 InetAddress 类表示互联网协议的 IP 地址。
下面列出了通过 InetAddress 类的静态方法获取 InetAddress 对象的方法。
InetAddress[] | getAllByName(String host) :在给定主机名的情况下,根据系统上配置的名称服务 host 返回其 IP 地址所组成的数组。 |
---|---|
InetAddress | getByAddress(byte[] addr) :给定字节数组形式的 IP 地址,返回 InetAddress 对象。 |
InetAddress | getByAddress(String host, byte[] addr) :根据提供的主机名 host 和字节数组形式的 IP 地址 addr,创建 InetAddress 对象。 |
InetAddress | getByName(String host) :给定主机名 host,返回 InetAddress 对象。 |
InetAddress | getLocalHost() :返回本地主机 InetAddress 对象。 |
InetAddress 类的其他常用方法有以下几种:
byte[] | getAddress() :返回此 InetAddress 对象的原始 IP 地址。 |
---|---|
String | getCanonicalHostName() :返回此 IP 地址的完全限定域名。完全限定域名是指主机名加上全路径,全路径中列出了序列中所有域成员。 |
String | getHostAddress() :返回 IP 地址字符串。 |
String | getHostName() :返回此 IP 地址的主机名。 |
public static void main(String[] args) throws UnknownHostException {
// 所有与网络编程先关的类 都位于java.net包下
InetAddress address = InetAddress.getLocalHost();
System.out.println(address.getHostName());//DESKTOP-RK7RI6L
InetAddress address1 = InetAddress.getByName("10.96.106.111");
System.out.println(address1.getHostAddress());// 获取IP地址字符串
}
4、端口
是程序在设备上的标识号
端口号:是用两个字节表示的整数,他的取值范围 0—65535 其中 0–1023之间的端口号主要用于一些网络服务和应用。在自己编写网络程序的时候 尽量使用1024以上的端口
5、 协议
- UDP协议: 用户数据报协议
UDP协议是无连接的通信协议,在数据传输时,数据的发送端和接收端不建立逻辑链接。
优点:UDP协议消耗资源少,通信的效率高。
- TCP:传输控制协议
TCP协议是面向链接的通信协议,在数据传输之前,必须在发送端和接收端之间建立逻辑链接,然后才能传输数据。可以提供两台设备之间的可靠无差错通信。
TCP协议区分发送端和接收端。
TCP协议的链接的建立:
TCP协议的链接需要经过“三次握手”
TCP协议断开链接是 需要经过“四次挥手”
6、 UDP协议通信程序
UDP是属于一种不可靠的协议 ,在通信的两端 需要建一个Socket(网络套接字)对象。但是这两个Socket只是发送和接收数据的对象,因此对于基于UDP协议的通信而言,没有所谓的客户端和服务器的概念。
DataGramSocket 基于UDP协议的Socket
- 此类表示用于发送和接收数据报数据包的套接字。
- 数据报套接字是分组传送服务的发送或接收点。
构造方法:
DatagramSocket() 构造数据报套接字并将其绑定到本地主机上的任何可用端口。 | |
---|---|
protected | DatagramSocket(DatagramSocketImpl impl) 使用指定的DatagramSocketImpl创建一个未绑定的数据报套接字。 |
| DatagramSocket(int port) 构造数据报套接字并将其绑定到本地主机上的指定端口。 |
| DatagramSocket(int port, InetAddress laddr) 创建一个数据报套接字,绑定到指定的本地地址。 |
| DatagramSocket(SocketAddress bindaddr) 创建一个数据报套接字,绑定到指定的本地套接字地址。 |
void | receive(DatagramPacket p) 从此套接字接收数据报包。 |
---|---|
void | send(DatagramPacket p) 从此套接字发送数据报包。 |
DatagramPacket:
- 该类表示数据报包。数据报包用于实现无连接分组传送服务。
DatagramPacket(byte[] buf, int length) 构造一个 DatagramPacket 用于接收长度的数据包 length 。 |
---|
DatagramPacket(byte[] buf, int length, InetAddress address, int port) 构造用于发送长度的分组的数据报包 length 指定主机上到指定的端口号。 |
DatagramPacket(byte[] buf, int offset, int length) 构造一个 DatagramPacket 用于接收长度的分组 length ,指偏移到缓冲器中。 |
DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port) 构造用于发送长度的分组数据报包 length 具有偏移 ioffset 指定主机上到指定的端口号。 |
DatagramPacket(byte[] buf, int offset, int length, SocketAddress address) 构造用于发送长度的分组数据报包 length 具有偏移 ioffset 指定主机上到指定的端口号。 |
DatagramPacket(byte[] buf, int length, SocketAddress address) 构造用于发送长度的分组的数据报包 length 指定主机上到指定的端口号。 |
发送数据的步骤:
1 创建发送端的Socket对象 DataGramSocket
2 创建数据 并将数据打包 DatagramPacket
3 调用DataGramSocket 的send方法 发送数据
4 关闭发送端 close
public class SendSocket {
public static void main(String[] args) throws IOException {
// 1 创建发送端的Socket对象 DataGramSocket
DatagramSocket ds = new DatagramSocket();
// 2 创建数据 并将数据打包 DatagramPacket
byte[] packet= "hello UDP".getBytes(StandardCharsets.UTF_8);
DatagramPacket dp = new DatagramPacket(packet,packet.length, InetAddress.getByName("127.0.0.1"),10086);
// 3 调用DataGramSocket 的send方法 发送数据
ds.send(dp);
// 4 关闭发送端 close
ds.close();
}
}
接收数据步骤
1 创建接收端你的Socket对象:DataGramSocket
2 创建一个数据报包 用于接收数据DatagramPacket
3 调用DataGramSocket的reveive接收
4 解析数据报包,把信息输出
5 关闭接收端
public class SendSocket {
public static void main(String[] args) throws IOException {
// 1 创建发送端的Socket对象 DataGramSocket
DatagramSocket ds = new DatagramSocket();
// 2 创建数据 并将数据打包 DatagramPacket
byte[] packet= "hello UDP".getBytes(StandardCharsets.UTF_8);
DatagramPacket dp = new DatagramPacket(packet,packet.length, InetAddress.getByName("127.0.0.1"),10086);
// 3 调用DataGramSocket 的send方法 发送数据
ds.send(dp);
// 4 关闭发送端 close
ds.close();
}
}
TCP
TCP是一个可靠的网络协议,在通信两端各建立一个socket 对象,在通信两端形成网络的虚拟链接 两端程序通过虚拟链接进行通信
1、客户端 socket
该类实现客户端套接字(也称为“套接字”)。 套接字是两台机器之间通讯的端点。
Socket() 创建一个未连接的套接字,并使用系统默认类型的SocketImpl。 |
---|
| ` ` | `Socket(InetAddress address, int port)` 创建流套接字并将其连接到指定IP地址的指定端口号。 |
| | `Socket(String host, int port)` 创建流套接字并将其连接到指定主机上的指定端口号。
| `InputStream` | `getInputStream()` 返回此套接字的输入流。
| `OutputStream` | `getOutputStream()` 返回此套接字的输出流。 |
// 客户端 发送数据 接受服务器的反馈
public class ClinetDemo1 {
public static void main(String[] args) throws IOException {
Socket socket = new Socket("127.0.0.1",10000);
//写数据
OutputStream os = socket.getOutputStream();
os.write("hello TCP 我来了!".getBytes(StandardCharsets.UTF_8));
//接受服务端的反馈 读数据
InputStream is = socket.getInputStream();
byte[] bytes = new byte[1024];
int len = is.read(bytes);
System.out.println("服务端的反馈信息:"+new String(bytes,0,len));
//释放资源 关闭socket 同时也就关闭了 OutputStream 和 InputStream
socket.close();
}
}
2、服务端 serverSocket
这个类实现了服务器套接字。 服务器套接字等待通过网络进入的请求。 它根据该请求执行一些操作,然后可能将结果返回给请求者。
ServerSocket() 创建未绑定的服务器套接字。 |
---|
ServerSocket(int port) 创建绑定到指定端口的服务器套接字。 |
Socket | accept() 侦听要连接到此套接字并接受它。 |
---|---|
void | close() 关闭此套接字。 |
// 服务端 收到客户端的数据后要各处反馈信息
public class ServerScoketDemo1 {
public static void main(String[] args) throws IOException {
ServerSocket server = new ServerSocket(10000);
Socket socket = server.accept();
//读数据
InputStream is = socket.getInputStream();
byte[] bytes = new byte[1024];
int len = is.read(bytes);
System.out.println("客户端发送的数据为:" +new String(bytes,0,len));
//给出客户端反馈
OutputStream os = socket.getOutputStream();
os.write("欢迎你的到来".getBytes(StandardCharsets.UTF_8));
socket.close();
server.close();
}
}
3、 升级改造
需求:
客户端: 数据来自于键盘 直到录入bye,发送数据结束
服务端 接受到客户端的数据 并给出反馈
public class ClientDemo2 {
public static void main(String[] args) throws IOException {
Socket client = new Socket("127.0.0.1",9999);
//数据来源于键盘
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
// 写出数据
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(client.getOutputStream()));
//数据来源于键盘
BufferedReader br1 = new BufferedReader(new InputStreamReader(client.getInputStream()));
// 写出数据
BufferedWriter bw1 = new BufferedWriter(new OutputStreamWriter(System.out));
String line;
while((line=br.readLine())!=null) {
if ("bye".equals(line)) {
break;
}
bw.write(line);
bw.newLine();
bw.flush();
String line1 = br1.readLine();
bw1.write(line1);
bw1.newLine();
bw1.flush();
}
client.close();
}
}
public class ServerSocketDemo2 {
public static void main(String[] args) throws IOException {
ServerSocket server = new ServerSocket(9999);
Socket socket = server.accept();
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
BufferedReader br1 = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw1 = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
String line;
while((line=br.readLine())!=null){
bw.write(line);
bw.newLine();
bw.flush();
String line1 = br1.readLine();
bw1.write(line1);
bw1.newLine();
bw1.flush();
}
}
}
4 使用多线程来改造
一个线程充当客户端 另一个线程充当服务端