首先我们先了解一下计算机网络的基本知识。

什么是网络?

将不同区域的计算机连接到一起。按照区域的大小可分为局域网,城域网,广域网(互联网)。

地址即IP地址,确定在网络上的一个绝对地址。

端口号:区分计算机上的软件, 占用两个字节 ,端口号范围为: 0-65535, 一共65536个端口号。

1. 在同一个协议下端口号不能重复,不同的协议可以重复

2. 1024以下的端口号尽量不要使用。有可能已被占用,如80–http 21–ftp

**资源定位:**URL 统一资源定位符 URI:统一资源

数据的传输

1、协议:TCP与UDP协议

1.TCP协议:面向连接 ,安全可靠,效率低下,类似于打电话,有三次握手

2.UDP协议:非面向连接,不可靠,效率高,类似于短信

2、传输(包括:封装与拆分),先封装,后拆分。

然后,进行编程需要了解java.net包下 面的如下几个类


1、InetAddress、InetSocketAddress
2、URL
3、TCP:ServerSocket、 Socket
4、UDP:DatagramSocket、DatagramPacket


1、IP地址及端口号

package ip;

/**
* 没有封装端口
*/

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

public class Demo01 {
public static void main(String[] args) throws UnknownHostException {
//使用getLocationHost方法创建InetAddree对象
InetAddress addr = Inet4Address.getLocalHost();
System.out.println(addr.getHostAddress());//返回ip地址
System.out.println(addr.getHostName());//输出计算机名

//根据域名得到InetAddress对象
addr = InetAddress.getByName("www.163.com");
System.out.println(addr.getHostAddress());//返回服务器的ip
System.out.println(addr.getHostName());
//根据ip得到InetAddress对象
addr = InetAddress.getByName("202.201.14.181");
System.out.println(addr.getHostAddress());//返回服务器的ip
System.out.println(addr.getHostName());//地址不存在或者为了安全性DNS域名不能解析 就返回ip 182.140.147.57
}
}

运行结果:


10.12.4.73
WIN-0HGJ9QM2MEG
61.188.191.84
www.163.com
202.201.14.181
202.201.14.181



package ip;

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

/**
* 封装端口:在InetAddress基础上+端口
*/

public class Demo02 {
public static void main(String[] args) throws UnknownHostException {
InetSocketAddress address = new InetSocketAddress("localhost",9999);
//address = new InetSocketAddress(InetAddress.getByName("127.0.0.1"),9999);
System.out.println(address.getHostName());
System.out.println(address.getPort());
InetAddress addr = address.getAddress();//获得地址
System.out.println(addr.getHostAddress());//返回:IP地址
System.out.println(addr.getHostName());//输出计算机名

}
}

运行结果:


localhost
9999
127.0.0.1
localhost


2、URL,统一资源定位符,它是一个具体的URI。

URL由四部分组成:协议,存放资源的主机域名,端口号,资源文件名

​URL(String protocol, String host, int port, String file)​

package URL;

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

public class Demo01 {
public static void main(String[] args) throws MalformedURLException {
//绝对路径构建
URL url = new URL("http://www.baidu.com:80/index.html#bb?uname=liguodong");
//url = new URL("http://www.baidu.com:80/index.html?uname=liguodong");
System.out.println("协议:"+url.getProtocol());
System.out.println("域名:"+url.getHost());
System.out.println("端口:"+url.getPort());
System.out.println("资源:"+url.getFile());
System.out.println("相对路径的资源:"+url.getPath());//用得比较多
System.out.println("緢点:"+url.getRef());//緢点 bb?uname=liguodong
System.out.println("参数:"+url.getQuery());//参数:存在緢点返回null,不存在,返回正确

url = new URL("http://www.baidu.com:80/a/");
url = new URL(url,"b.txt");
System.out.println(url.toString());

}
}

运行结果:


协议:http
域名:www.baidu.com
端口:80
资源:/index.html
相对路径的资源:/index.html
緢点:bb?uname=liguodong
参数:null
​http://www.baidu.com:80/a/b.txt​​​



package URL;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.URL;

/**
* 获取资源:源代码
*/
public class Demo02 {
public static void main(String[] args) throws IOException {
URL url = new URL("http://www.baidu.com");//有时,如果没有资源,主页即是默认资源。
//获取资源
//这种方式会出现乱码出现乱码的情况分别是字节数不够或者编码不统一,这种情况明显是编码不统一。
//因此可以使用直接将平台的编码改掉,或者使用转换流
/*InputStream is = url.openStream();
byte[] flush = new byte[1024];
int len=0;
while(-1!=(len=is.read(flush)))
{
System.out.println(new String(flush,0,len));
}
is.close();*/

BufferedReader br = new BufferedReader(
new InputStreamReader(url.openStream(),"utf-8"));
BufferedWriter bw = new BufferedWriter(
new OutputStreamWriter(new FileOutputStream("baidu.html"),"utf-8"));

String msg = null;
while((msg=br.readLine())!=null)
{
System.out.println(msg);
System.out.println();
bw.append(msg);
bw.newLine();
}
bw.flush();
bw.close();
br.close();
}
}

3、UDP通信:以数据为中心,非面向连接,不安全,数据可能丢失 效率高

​DatagramPacket:此类表示数据报包。​

​DatagramSocket:类表示用来发送和接收数据报包的套接字。​

整体设计

1.客户端

1.1创建客户端

1.2创建数据(字节数组)

1.3打包(DatagramPacket+服务器地址及端口号)

1.4发送

1.5释放数据

2.服务器端

2.1创建服务器(DatagramSocket类+制定端口号)

2.2准备接收容器(字节数组)

2.3包(接收数据)

2.4分析

2.5释放资源

package UDP;

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

/**
* 服务端
* 1、创建服务端+端口
* 2、准备接收容器
* 3、封装成包
* 4、接收数据
* 5、分析数据
* 6、释放资源
*
* DatagramPacket(byte[] buf, int length)
* 构造 DatagramPacket,用来接收长度为 length 的数据包。
*
* DatagramPacket(byte[] buf, int length, InetAddress address, int port)
* 构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号。
*
*/
public class MyServer {
public static void main(String[] args) throws IOException {
//1、创建服务端+端口
DatagramSocket server = new DatagramSocket(8888);

//2、准备接收容器
byte[] container = new byte[1024];

//3、封装成包 DatagramPacket(byte[] buf, int length)构造接收数据包
DatagramPacket packet = new DatagramPacket(container, container.length);

//4、接收数据
server.receive(packet);

//5、分析数据
byte[] data = packet.getData();
int len = packet.getLength();
System.out.println(new String(data,0,len));

//6、释放资源
server.close();
}
}
package UDP;

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

/**
* 客户端
*1、创建客户端+端口
*2、准备数据
*3、打包(发送地点及端口)
*4、发送
*5、释放
*
*/
public class MyClient {
public static void main(String[] args) throws IOException {
//1、创建客户端+端口
DatagramSocket client = new DatagramSocket(6666);

//2、准备数据
String msg = "模拟UDP过程";
byte[] data = msg.getBytes();

//3、打包(指定发送的地点及端口)
//DatagramPacket(byte[] buf, int length, SocketAddress address) 构造发送数据报包
DatagramPacket packet = new DatagramPacket(data, data.length,
new InetSocketAddress("localhost",8888));

//4、发送
client.send(packet);

//5、释放
client.close();
}
}

非面向连接的协议,当服务器打开,服务器能收到数据,当服务器关闭,客户端运行也不会报错,数据也相应的被丢失了。


如果我们发送的数据不是字节数组,而是一个浮点数,因此我们需要进行数据类型转换。本例值得回味。

package UDP;

import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;

/**
* 89.12 数据的发送要字节数组,因此将double类型转成字节数组
* 客户端
*1、创建客户端+端口
*2、准备数据 double类型转成字节数组 字节数组输出流 类型转换问题
*3、打包(发送地点及端口)
*4、发送
*5、释放
*/
public class Client {
public static void main(String[] args) throws IOException {
//1、创建服务器+端口
DatagramSocket client = new DatagramSocket(6666);
//2、准备数据
double num = 89.12;

byte[] data = convert(num);

//3、打包(发送的地点及端口)
DatagramPacket packet = new DatagramPacket(data, data.length,
new InetSocketAddress("localhost",8888));
//4、发送
client.send(packet);
//5、释放
client.close();
}

/**
* 字节数组输出流ByteArrayOutputStream+DataOutputStream数据输出流
* @param num
* @return
* @throws IOException
*/
public static byte[] convert(double num) throws IOException
{
byte[] data = null;
//有新增方法,不能使用多态
ByteArrayOutputStream bos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(bos);

dos.writeDouble(num);
dos.flush();

//获取数据
data = bos.toByteArray();
dos.close();
bos.close();//此方法关闭 ByteArrayOutputStream无效。

return data;
}
}
package UDP;

import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
/**
* 89.12 数据+类型 用 IO流
* 服务端
* 1、创建服务端+端口
* 2、准备接受容器
* 3、封装成包
* 4、接受数据
* 5、分析数据 字节数组-->double
* 6、释放资源
*/
public class Server {
public static void main(String[] args) throws IOException {
//1、创建服务端+端口
DatagramSocket server = new DatagramSocket(8888);
//2、准备接受容器
byte[] container = new byte[1024];
//3、封装成包 DatagramPacket(byte[] buf, int length);
DatagramPacket packet = new DatagramPacket(container, container.length);
//4、接受数据
server.receive(packet);
//5、分析数据
double data = convert(packet.getData());
System.out.println(data);

//6、释放资源
server.close();

}

/**
* 字节数组输入类ByteArrayInputStream+DataInputStream数据输入流
* @param data
* @return
* @throws IOException
*/
public static double convert(byte[] data) throws IOException
{
DataInputStream dis = new DataInputStream(new ByteArrayInputStream(data));
double num = dis.readDouble();
dis.close();
return num;
}
}

4、TCP通信: 面向连接,安全可靠,效率低下

​ServerSocket:此类实现服务器套接字。​

​Socket:此类实现客户端套接字。​

package TCP;

import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

/**
* 必须先启动再连接
* 1、创建服务器 指定端口 ServerSocket(int port)
* 2、接收客户端的连接 阻塞式
* 3、发送数据+接收数据
*/
public class Server {
public static void main(String[] args) throws IOException {
//1、创建服务器 指定端口
ServerSocket server = new ServerSocket(8888);//不同协议端口可以重复,同一协议端口不能重复
//2、接收客户端的连接
Socket socket = server.accept();//这里会等待连接
System.out.println("一个客户端建立连接");

//2、发送数据
String msg = "欢迎使用";
//3、输出流
/*BufferedWriter bw = new BufferedWriter(
new OutputStreamWriter(socket.getOutputStream()));
bw.write(msg);
bw.newLine();//一定要加行结束符,不然读不到数据
bw.flush();*/

DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
dos.writeUTF(msg);
dos.flush();
}
}
package TCP;

import java.io.DataInputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
/**
* 1、创建客户端 必须指定服务器+端口 此时就在连接
* Socket(String host,int port)
* 2、接收数据+发送数据
*/
public class Client {
public static void main(String[] args) throws UnknownHostException, IOException {
//1、创建客户端 必须指定服务器+端口 此时就在连接
Socket client = new Socket("localhost",8888);//指定服务器的地址与端口,客户端的端口由服务器自动分配

//2、接收数据
/*BufferedReader br = new BufferedReader(
new InputStreamReader(client.getInputStream()));
String echo = br.readLine();//逐行执行,阻塞式方法
System.out.println(echo);*/

DataInputStream dis = new DataInputStream(client.getInputStream());
String echo = dis.readUTF();
System.out.println(echo);
}
}

使用TCP时,必须要打开服务器,不然运行客服端会出异常。并且客户端必须要指定服务器的地址与端口,客户端的端口由服务器自动分配。