一、网络编程入门
1、网络编程概述
- 计算机网络
是指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来,在网络操作系统,网络管理软件及网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统 - 网络编程
在网络通信协议下,实现网络互连的不同计算机上运行的程序间可以进行数据交换
2、网络编程三要素
2.1、IP地址
IP地址(Internet Protocol Address)是指互联网协议地址,又译为网际协议地址。 IP地址是IP协议提供的一种统一的地址格式,它为互联网上的每一个网络和每一台主机分配一个逻辑地址,以此来屏蔽物理地址的差异
IP地址:是网络中设备的唯一标识
IP地址分为两大类
IPv4:是给每个连接在网络上的主机分配一个32bit地址。按照TCP/IP规定,IP地址用二进制来表示,每个IP地址长32bit,也就是4个字节。例如一个采用二进制形式的IP地址是“11000000 10101000 00000001 01000010”,这么长的地址,处理起来也太费劲了。为了方便使用,IP地址经常被写成十进制的形式,中间使用符号“.”分隔不同的字节。于是,上面的IP地址可以表示为“192.168.1.66”。IP地址的这种表示法叫做“点分十进制表示法”,这显然比1和0容易记忆得多
IPv6:由于互联网的蓬勃发展,IP地址的需求量愈来愈大,但是网络地址资源有限,使得IP的分配越发紧张。为了扩大地址空间,通过IPv6重新定义地址空间,采用128位地址长度,每16个字节一组,分成8组十六进制数,这样就解决了网络地址资源数量不够的问题
DOS常用命令:
- ipconfig:查看本机IP地址
- ping IP地址:检查网络是否连通
特殊IP地址:
- 127.0.0.1:是回送地址,可以代表本机地址,一般用来测试使用
2.2、端口号
端口号(port number) 所谓端口,就好像门牌号一样,客户端可以通过IP地址找到对应的服务器端,但是服务器端是有很对端口的,每个应用程序对应一个端口号,通过类似门牌号的端口号,客户端才能真正的访问到服务器。为了对端口号进行区分,将每个端口进行了编号,这就是端口号。
TCP与UDP段结构中端口地址都是16比特,可以有在0—-65535范围内的端口号。对于这65536个端口号有以下的使用规定 :
(1)端口号小于256的定义为常用端口,服务器一般都是通过常用端口号来识别的。任何TCP/IP实现所提供的服务都用1—-1023之间的端口号,是由ICANN来管理的;端口号从1024—-49151是被注册的端口,也成为“用户端口”,被IANA指定为特殊服务使用;
(2)客户端只需保证该端口号在本机上是唯一的就可以了。客户端端口号因存在时间很短暂又称临时端口号;
(3)大多数TCP/IP实现给临时端口号分配1024—-5000之间的端口号。大于5000的端口号是为其他服务器预留的
2.3、通信协议
网络通信协议就是一种网络通信的语言,为连接不同操作系统和不同硬件体系结构的互联网络提供通信支持,是一种网络通用的语言
UDP协议
- 用户数据报协议(User Datagram Protocol)
- UDP是无连接通信协议:即在数据传输时,数据的发送端和接受端不建立逻辑链接。
- 简单来说,当一计算机向另外一台计算机发送数据时,发送端不会确认接收端是否存在,就会发出数据,同样接收端在接收数据时,也不会向发送端反馈是否收到数据。
- 由于使用UDP协议消耗的资源小,通信效率高,所以通常会用于音频,视频和普通数据的传输
TCP协议
- 传输控制协议(Transmission Control Portocol)
- TCP协议是面向链接的通信协议,即传输数据之前,在发送端和接收端之间建立逻辑链接,然后再传输数据它提供了两台计算机之间可靠无差错的数据传输。
- 在TCP链接中必须要明确客户端和服务端,由客户端向服务端发出链接请求,每次链接的创建都需要经过“三次握手”
- 三次握手:TCP协议中,在发送数据的准备阶段,客户端于服务端之间的三次交互,以保证链接的可靠
- 第一次握手:客户端向服务端发出链接请求,等待服务器确认
- 第二次握手:服务端向客户端回送一个相应,通知客户收到了链接请求
- 第三次握手:客户端再次向服务器端发送确认信息,确认链接
- 三次握手:TCP协议中,在发送数据的准备阶段,客户端于服务端之间的三次交互,以保证链接的可靠
3、InetAddress 【应用】
InetAddress类用来封装我们前面讨论的数字式的IP地址和该地址的域名。 你通过一个IP主机名与这个类发生作用,IP主机名比它的IP地址用起来更简便更容易理解。 InetAddress类内部隐藏了地址数字。
InetAddress类没有明显的构造函数。为生成一个InetAddress对象,必须运用一个可用的工厂方法。
工厂方法(factory method)仅是一个类中静态方法返回一个该类实例的约定。
3.1、InetAddress对象的获取
InetAddress的构造函数不是公开的(public),所以需要通过它提供的静态方法来获取,有以下的方法:
方法 | 说明 |
---|---|
static InetAddress[] getAllByName(String host) | |
static InetAddress getByAddress(byte[] addr) |
| |
| static InetAddress getByAddress(String host,byte[] addr) | |
| static InetAddress getByName(String host) | 常用方法 getByName,通过主机名获得InetAddress对象,可以是IP地址
getLocalHost,获得本机的InetAddress对象 |
| static InetAddress getLocalHost() | |
3.3 InetAddress的方法
返回值 | 方法 | 说明 |
---|---|---|
String | getCanonicalHostName() | 获取此IP地址的完全限定域名。 |
String | getHostAddress() | 返回文本显示中的IP地址字符串。 |
String | getHostName() | 获取此IP地址的主机名。 |
int | hashCode() | 返回此IP地址的哈希码。 |
boolean | isAnyLocalAddress() | 检查通配符地址中的InetAddress的实用程序。 |
boolean | isLinkLocalAddress() | 检查InetAddress是否是链接本地地址的实用程序。 |
boolean | isLoopbackAddress() | 检查InetAddress是否是一个环回地址的实用程序。 |
boolean | isMCGlobal() | 检查多播地址是否具有全局范围的实用程序。 |
boolean | isMCLinkLocal() | 检查组播地址是否具有链路范围的实用程序。 |
boolean | isMCNodeLocal() | 检查多播地址是否具有节点范围的实用程序。 |
boolean | isMCOrgLocal() | 检查组播地址是否具有组织范围的实用程序。 |
boolean | isMCSiteLocal() | 检查多播地址是否具有站点范围的实用程序。 |
boolean | isMulticastAddress() | 检查InetAddress是否是IP组播地址的实用程序。 |
boolean | isReachable(int timeout) | 测试该地址是否可达。 |
boolean | isReachable(NetworkInterface netif, int ttl, int timeout) | 测试该地址是否可达。 |
boolean | isSiteLocalAddress() | 检查InetAddress是否是站点本地地址的实用程序。 |
String | toString() | 将此IP地址转换为 String 。 |
二、UDP通信程序
1、UDP发送数据
Java中的UDP通信
- UDP协议是一种不可靠的网络协议,它在通信的两端各建立一个Socket对象,但是两个Socket只是发送,接受数据的对象,因此对于基于UDP协议的通信双方而言,没有所谓的客户端和服务器的概念
- Java提供了DatagrameSocket类作为基于UDP协议的Socket
- 构造方法 | 方法名 | 说明 | | —- | —- | | DatagramSocket() | 创建数据报套接字,并将其绑定到本机地址上的任意可用端口 | | DatagramPacket(byte[] buf,int len,lentAddress add,int port) | 创建数据包,发送长度为len的数据包到指定主机的指定端口 | | DatagramSocket(int port) | 创建数据报套接字,并将其绑定到本机地址指定端口上 |
- 实例方法 | 方法名 | 说明 | | —- | —- | | void send(DatagramPacket p) | 发送数据包 | | void close() | 关闭数据报套接字 | | void receive(DatagramPacket p) | 从此套接字接受数据包 |
- 发送数据的步骤
- 创建发送端的Socket对象—(DatagramSocket)
- 创建数据包,并把数据打包
- 调用DatagramSocket对象的方法发送数据
- 关闭发送端
- 代码演示 ```java import java.io.IOException; import java.net.*;
public class DatagramSocket_sent { public static void main(String[] args) throws IOException {
//创建发送端的DatagramSocket对象
DatagramSocket ds = new DatagramSocket();
//构建一个数据包
byte[] bytes = "这是一个由UDP协议发出的字符串".getBytes();
DatagramPacket dp = new DatagramPacket(bytes, bytes.length, InetAddress.getByName("localhost"),9999);
//由DatagramSocket对象发送数据包dp
ds.send(dp);
//关闭套接字
ds.close();
} }
<a name="9xGzm"></a>
## 2、UDP接受数据
- 接收数据的步骤
- 创建接收端的Socket对象(DatagramSocket)
- 创建一个数据包,用于接收数据
- 调用DatagramSocket对象的方法接收数据
- 解析数据包,并把数据在控制台显示
- 关闭接收端
- 数据包构造方法
| 方法名 | 说明 |
| --- | --- |
| DatagramPacket(byte[] buf, int len) | 创建一个DatagramPacket用于接收长度为len的数据包 |
- 相关方法
| 方法名 | 说明 |
| --- | --- |
| byte[] getData() | 返回数据缓冲区 |
| int getLength() | 返回要发送的数据的长度或接收的数据的长度 |
- 示例代码
```java
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class DatagramSocket_receive {
public static void main(String[] args) throws IOException {
//new DatagramSocket套接字并指定端口来接收数据
DatagramSocket ds = new DatagramSocket(9999);
//new DatagramPacket 数据包来接受存放数据
byte[] bytes = new byte[1024];
DatagramPacket dp = new DatagramPacket(bytes, bytes.length);
//接收数据
ds.receive(dp);
//获取数据包中字节数组中接收到的数据
byte[] data = dp.getData();
//将存放字符的字节数组转换成字符串
String s = new String(data, 0, dp.getLength());
//输出接收的字符串
System.out.println("接收的数据为:"+s);
//关闭接收端
ds.close();
}
}
三、TCP通信程序
1、TCP发送数据—>客户端
Java中的TCP通信
- Java对基于TCP协议的网络通信提供了良好的封装,使用Socket对象来戴白哦两端的通信端口,并通过Socket产生的IO流来进行网络通信。
- Java为客户端提供了Socket类,为服务端提供了ServerSocket类
构造方法 | 方法名 | 说明 | | —- | —- | | Socket(InetAddress address,int port) | 创建流套接字并将其连接到指定主机指定端口 | | Socket(String host,int port) | 创建流套接字并将其连接到指定IP指定端口 |
- 相关方法 | 方法名 | 说明 | | —- | —- | | InputStream getInputStream() | 返回此套接字的输入流 | | OutPutStream getOutPutStream() | 返回此套接字的输出流 | | void close() | 关闭socket释放资源 |
客户端实现步骤
- 创建Socket对象
- 通过Socket对象获得出入输出流
- 输入流:服务器—->本机
- 输出流:本机—->服务器
- 操作完成关闭输入输出流和socket对象释放资源
- 代码示例(给服务器端发送信息,并在控制台输出服务器端返回的数据)
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
public class ClientDemo {
public static void main(String[] args) throws IOException {
//new 客户端 Socket对象 并指定连接到的主机和端口
Socket socket = new Socket("localhost",9999);
//获得Socket的输出流对象 发送数据 本机-->服务端
OutputStream os = socket.getOutputStream();
os.write("通过TCP协议发送的数据".getBytes(StandardCharsets.UTF_8));
//获得Socket的输入流对象 接收数据 服务端-->本机
InputStream is = socket.getInputStream();
byte[] bytes = new byte[1024];
int len = is.read(bytes);
String data = new String(bytes, 0, len);
System.out.println("服务器返回数据:"+data);
//释放资源
is.close();
os.close();
socket.close();
}
}
2、TCP接收数据—>服务端
TCP协议的连接只能由客户端主动发起,服务端需要监听指定端口,等待连接。
ServerSocket对象的accept()方法可以监听指定端口,并在客户端请求是返回Socket对象
通过ServerSocket返回的Socket对象获取输入输出流和客户端之间交换数据
- 构造方法(ServerSocket)
方法名 | 说明 |
---|---|
ServerSocket(int port) | 创建绑定到指定端口的苏武器套接字 |
- 相关方法(ServerSocket)
方法名 | 说明 |
---|---|
Socket accept( ) | 监听要连接到此Socket并接收它返回链接的Socket对象 |
服务器端实现步骤
- 创建服务器套接字ServerSocket对象,并指定端口
- 调用ServerSocket对象的accept方法监听要连接到此的客户端,并返回连接的Socket对象
- 通过建立连接后获得的Socket对象获得连接的输入输出流和客户端实现数据交换
- 完成操作后关闭输入输出流、Socket、ServerSocket释放资源
- 示例代码(服务器端接收客户端发送的信息,并把接收到的信息返回给客户端)
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
public class ServerDemo {
public static void main(String[] args) throws IOException {
//创建服务器端ServerSocket对象,并指定端口
ServerSocket serverSocket = new ServerSocket(9999);
//监听此端口并接受返回Socket对象
Socket socket = serverSocket.accept();
//获取链接的输入流 读数据 客户端-->服务端
InputStream is = socket.getInputStream();
byte[] bytes = new byte[1024];
int len = is.read(bytes);
String data = new String(bytes, 0, len);
System.out.println("服务器------> 客户端:"+data);
//获取流的输出流 服务端-->客户端
OutputStream os = socket.getOutputStream();
//返回客户端发送过来的数据
String sentData = "数据已收到---->" + data;
os.write(sentData.getBytes(StandardCharsets.UTF_8));
//释放资源
is.close();
os.close();
socket.close();
}
}