1、网络编程概述
1.1、概述
Java是Internet上的语言,它从语言级上提供了对网络应用程序的支持,程序员能够很容易开发常见的网络应用程序。Java提供的网络类库,可以实现无痛的网络连接,联网的底层细节被隐藏在 Java 的本机安装系统里,由JVM进行控制。并且Java实现了一个跨平台的网络库,程序员面对的是一个统一的网络编程环境。
计算机网络:
把分布在不同地理区域的计算机与专门的外部设备用通信线路互连成一个规模大,功能强的网络系统,从而使众多的计算机可以方便地互相传递信息,共享硬件,软件,数据信息等资源。
网络编程的目的:
直接或间接地通过网络协议与其他计算机实现数据交流,进行通讯。
网络编程中有两个主要的问题:
1.如何准确的定位网络上的一台或多台主机,定位主机上的特定的应用
2.找到主机后如何可靠高效地进行数据传输
网络编程 != 网页编程(web开发)
可以去了解B/S架构和C/S架构
1.2、网络通信的两个要素
如何实现网络中的主机互相通信?
通信双方的地址:
ip
端口号
一定的规则:(也就是常说的网络通信协议,有两套参考模型)
OSI参考模型:模型过于理想化,未能在因特网上进行广泛推广
TCP/IP参考模型:TCP/IP协议,事实上的国际标准(在实际情况下用的比较多)
小总结:
1.网络编程中有两个主要的问题:
如何准确的定位网络上一台或多台主机;定位主机上的特定的应用
找到主机后如何可靠高效的进行数据传输
2.网络编程中的两个要素:
IP和端口号
提供网络通信协议。TCP/IP参考模型(应用层,传输层,网络层,物理+数据链路层)
1.3、InetAddress
IP地址:InetAddress
唯一的标识internet上的计算机(通信实体)
本地回环地址(hostAddrress):127.0.0.1 主机名(hostName):localhost
IP地址分类方式之一:ipv4,ipv6
IPV4:4个字节组成,4个0~255。大概42亿个, 30亿都在北美,亚洲4亿。2011年初已经用尽。以点分十进制表示,如192.168.0.1
IPV6:128位(16个字节),写成8个无符号整数,每个整数用四个十六进制位表示,数之间用冒号 隔开,如:2001:0db8:3c4d:0015:0000:0000:1a2f:1a2b
IP地址分类方式2:公网地址(万维网使用) 和 私有地址(局域网使用)。
192.168.开头的就是私有地址,范围即为 192.168.0.0 ~ 192.168.255.255,专门为组织机构内部使用
【查看jdk帮助文档=>InetAddress类,代表IP】
特点:不便于记忆,我们尝试用域名来访问:www.baidu.com
这里可以自行了解DNS域名解析
(电脑本机的hosts文件地址:c:windowssystem32driversetchosts)
【InetAddressTest】
1 package com.gzq.ip;
2
3 import java.net.InetAddress;
4 import java.net.UnknownHostException;
5
6 /**
7 * 测试IP
8 * @author GaoZQ
9 * @date 2021/3/17 20:58
10 */
11 public class TestInetAddress {
12 public static void main(String[] args) {
13 try {
14 InetAddress inetAddress1 = InetAddress.getByName("127.0.0.1");
15 System.out.println(inetAddress1);
16 InetAddress inetAddress3 = InetAddress.getByName("localhost");
17 System.out.println(inetAddress3);
18 InetAddress inetAddress4 = InetAddress.getLocalHost();
19 System.out.println(inetAddress4);
20
21 InetAddress inetAddress2= InetAddress.getByName("www.baidu.com");
22 System.out.println(inetAddress2);
23
24 //常用方法
25 System.out.println(inetAddress2.getAddress());
26 //获取规范主机名
27 System.out.println(inetAddress2.getCanonicalHostName());
28 //获取主机地址
29 System.out.println(inetAddress2.getHostAddress());
30 //获取主机名,域名
31 System.out.println(inetAddress2.getHostName());
32
33 } catch (UnknownHostException e) {
34 e.printStackTrace();
35 }
36 }
37 }
1.4、端口号
端口号标识正在计算机上运行的进程(程序)
不同的进程的有不同的端口号,来区分软件
被规定为一个16位的整数0~65535
TCP和UDP各有65535个端口,单个协议下端口不能冲突
端口分类:
公认端口:1~1023.被预先定义的服务通信占用端口
HTTP默认端口:80
HTTPs默认端口:443
FTP默认端口:21
Telnet默认端口:23
注册端口:1024~49151、分配给用户进程或应用程序
tomcat默认端口:8080
MySQL默认端口:3306
Oracle默认端口:1521
动态、私有端口:49152~65535
端口号与IP地址的组合,得出一个网络套接字:Socket,所以说一些网络编程也被称为Socket编程
package com.gzq.ip;
import java.net.InetSocketAddress;
/**
* @author GaoZQ
* @date 2021/3/17 21:15
*/
public class TestInetSockAddress {
public static void main(String[] args) {
InetSocketAddress inetSocketAddress1 = new InetSocketAddress("127.0.0.1",8080);
InetSocketAddress inetSocketAddress2 = new InetSocketAddress("localhost",8080);
System.out.println(inetSocketAddress1);
System.out.println(inetSocketAddress2);
System.out.println(inetSocketAddress1.getAddress());
System.out.println(inetSocketAddress1.getHostName());
System.out.println(inetSocketAddress1.getPort());
}
}
1.5、网络通信协议
协议:约定俗成
网络通信协议:计算机网络中实现通信必须有一些约定,即通信协议,对速率,传输代码,代码结构,传输控制步骤,出错控制等制定标准。
问题:网络协议太复杂?
计算机网络通信涉及内容很多,比如指定源地址和目标地址,加密解密,压缩解压缩,差错控制,浏=流量控制,路由控制,如何实现如此复杂的网络协议呢?
通信协议分层的思想:
在制定协议时,把复杂成份分解成一些简单的成份,再将它们复合起来。最常用的复合方式就是层次方式,即同层间可以通信,上一层调用下一层,而与下一层不再发生关系。各层互不影响,利于系统的开发和拓展。
TCP和UDP对比
TCP协议:
使用TCP协议前,必须建立TCP连接,形成传输数据通道
传输前,采用三次握手,点对点通信,是可靠的
TCP协议进行通信的两个应用进程:客户端,服务端
在连接中可进行大数据量的传输
传输完毕之后需要释放已建立的连接,效率较低
UDP协议
将数据,源,目的封装成数据包,不需要建立连接
每个数据报的大小限制在64K内
发送方不管对方是否准备好,接收方收到也不确认,故事不可靠的
可以广播发送
发送数据结束时,无需释放资源,开销小,速度快。
2、TCP网络编程
2.1、案例一
需求:客户端发送信息给服务端,服务端将数据显示在控制台上。
package com.gzq.tcpDemo;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
/**
* @author GaoZQ
* @date 2021/3/17 21:42
*/
public class TcpClientDemo01 {
public static void main(String[] args) {
try {
//1.要知道服务器的地址
InetAddress serverIP = InetAddress.getByName("127.0.0.1");
//2.需要知道端口号
int port = 9999;
//3.创建一个socket连接
Socket socket = new Socket(serverIP, port);
//4.发送消息
OutputStream outputStream = socket.getOutputStream();
outputStream.write("你好,服务端".getBytes(StandardCharsets.UTF_8));
outputStream.close();
socket.close();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
package com.gzq.tcpDemo;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
/**
* @author GaoZQ
* @date 2021/3/17 21:42
*/
public class TcpServerDemo01 {
public static void main(String[] args) {
try {
//1.我得有一个地址
ServerSocket serverSocket = new ServerSocket(9999);
//2.等待客户端连接
Socket accept = serverSocket.accept();
//3.读取客户端的消息
InputStream inputStream = accept.getInputStream();
/**
byte[] bytes = new byte[1024];
int len = 0;
while ((len = inputStream.read(bytes)) != -1){
String s = new String(bytes, 0, len);
System.out.println(s);
}
*/
//管道流
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
byte[] bytes = new byte[1024];
int len = 0;
while((len = inputStream.read(bytes)) != -1){
byteArrayOutputStream.write(bytes,0,len);
}
System.out.println(byteArrayOutputStream.toString());
//关闭资源
byteArrayOutputStream.close();
inputStream.close();
accept.close();
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
}
}
}
2.2案例二
需求:客户端发送文件给服务器,服务端将文件保存在本地。
我们需要准备一个图片,放在项目目录下:
package com.gzq.tcpDemo;
import java.io.*;
import java.net.InetAddress;
import java.net.Socket;
/**
* @author GaoZQ
* @date 2021/3/21 13:20
*/
public class TcpClientDemo02 {
public static void main(String[] args) {
Socket socket = null;
OutputStream os = null;
FileInputStream fis = null;
try {
socket = new Socket(InetAddress.getByName("127.0.0.1"), 9000);
os = socket.getOutputStream();
//文件流
File file = new File("a.txt");
fis = new FileInputStream(file);
byte[] buffers = new byte[1024];
int len;
while((len = fis.read(buffers)) != -1){
os.write(buffers,0,len);
}
//通知服务器我已经传输完毕
socket.shutdownOutput();
//确定服务器接收完毕才能够断开连接
InputStream is = socket.getInputStream();
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
len = 0;
while((len = is.read(buffer)) != -1){
byteArrayOutputStream.write(buffer,0,len);
}
System.out.println(byteArrayOutputStream.toString());
byteArrayOutputStream.close();
is.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
package com.gzq.tcpDemo;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
/**
* @author GaoZQ
* @date 2021/3/21 13:20
*/
public class TcpServerDemo02 {
public static void main(String[] args) {
try {
ServerSocket serverSocket = new ServerSocket(9000);
Socket clientServer = serverSocket.accept();
InputStream is = clientServer.getInputStream();
//文件输出
FileOutputStream fos = new FileOutputStream(new File("b.txt"));
byte[] bytes = new byte[1024];
int len;
while ((len = is.read(bytes)) != -1){
fos.write(bytes,0,len);
}
//通知客户端接收完毕
OutputStream os = clientServer.getOutputStream();
os.write("接收完毕,可以断开".getBytes(StandardCharsets.UTF_8));
os.close();
fos.close();
is.close();
clientServer.close();
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
3、UDP网络编程
3.1、说明
DatagramSocket 和 DatagramPacket 两个类实现了基于UDP协议的网络程序。
UDP 数据报通过数据报套接字 DatagramSocket 发送和接收,系统不保证UDP数据报一定能够安 全送到目的地,也不确定什么时候可以抵达。
DatagramPacket 对象封装了UDP数据报,在数据报中包含了发送端的IP地址和端口号以及接收端 的IP地址和端口号。
UDP协议中每个数据报都给出了完整的地址信息,因此无需建立发送方和接收方的连接。如同发快 递包裹一样。
3.2、案例一
package com.gzq.udpDemo;
import java.io.IOException;
import java.net.*;
import java.nio.charset.StandardCharsets;
/**
* @author GaoZQ
* @date 2021/3/21 14:15
*/
public class UdpClientDemo01 {
public static void main(String[] args) {
try {
DatagramSocket datagramSocket = new DatagramSocket();
InetAddress localhost = InetAddress.getByName("localhost");
int port = 9090;
String msg = "你好啊,兄弟";
DatagramPacket datagramPacket = new DatagramPacket(
msg.getBytes(StandardCharsets.UTF_8),
0,
msg.getBytes(StandardCharsets.UTF_8).length,
localhost,
port
);
datagramSocket.send(datagramPacket);
datagramSocket.close();
} catch (SocketException e) {
e.printStackTrace();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
package com.gzq.udpDemo;
import java.io.IOException;
import java.net.*;
import java.nio.charset.StandardCharsets;
/**
* @author GaoZQ
* @date 2021/3/21 14:15
*/
public class UdpServerDemo01 {
public static void main(String[] args) throws IOException {
DatagramSocket socket = new DatagramSocket(9090);
byte[] bytes = new byte[1024];
DatagramPacket datagramPacket = new DatagramPacket(bytes, 0, bytes.length);
socket.receive(datagramPacket);
System.out.println(new String(datagramPacket.getData()));
socket.close();
}
}
3.3、案例二:在线咨询
package com.gzq.udpDemo.chat;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
/**
* @author GaoZQ
* @date 2021/3/21 14:29
*/
public class UdpReceiveDemo01 {
public static void main(String[] args) throws IOException {
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 s = new String(data, 0, data.length);
if ("bye".equals(s)){
break;
}
System.out.println(s);
}
socket.close();
}
}
package com.gzq.udpDemo.chat;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.*;
import java.nio.charset.StandardCharsets;
/**
* @author GaoZQ
* @date 2021/3/21 14:29
*/
public class UdpSenderDemo01 {
public static void main(String[] args) throws IOException {
DatagramSocket socket = new DatagramSocket(8888);
//准备数据:从控制台读取
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
while(true){
String data = reader.readLine();
byte[] bytes = data.getBytes(StandardCharsets.UTF_8);
DatagramPacket packet = new DatagramPacket(bytes,0,bytes.length,
new InetSocketAddress("localhost",6666));
socket.send(packet);
if ("bye".equals(data)){
break;
}
}
socket.close();
}
}
问题:现在需要两遍需要接受和发送,我们可以使用多线程来解决!
package com.gzq.udpDemo.chat;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketException;
import java.nio.charset.StandardCharsets;
/**
* @author GaoZQ
* @date 2021/3/21 14:47
*/
public class TalkSend {
public static void main(String[] args) {
}
}
class TalkReceiver implements Runnable{
private DatagramSocket socket = null;
private int port;
private String msgFrom;
public TalkReceiver(int port,String msgFrom) {
this.port = port;
this.msgFrom = msgFrom;
try {
socket = new DatagramSocket(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();
String s = new String(data, 0, data.length);
System.out.println(msgFrom + ":" + s);
if ("bye".equals(s)){
break;
}
} catch (IOException e) {
e.printStackTrace();
}
}
socket.close();
}
}
class TalkSender implements Runnable{
private DatagramSocket socket = null;
private BufferedReader reader = null;
private int fromPort;
private String toIP;
private int toPort;
public TalkSender(int fromPort, String toIP, int toPort) {
this.fromPort = fromPort;
this.toIP = toIP;
this.toPort = toPort;
try {
socket = new DatagramSocket(fromPort);
reader = new BufferedReader(new InputStreamReader(System.in));
} catch (SocketException e) {
e.printStackTrace();
}
}
@Override
public void run() {
while(true){
try {
String data = reader.readLine();
byte[] bytes = data.getBytes(StandardCharsets.UTF_8);
DatagramPacket packet = new DatagramPacket(bytes,0,bytes.length, new InetSocketAddress(this.toIP,this.toPort));
socket.send(packet);
if ("bye".equals(data)){
break;
}
} catch (IOException e) {
e.printStackTrace();
}
}
socket.close();
}
}