网络通信
现在有这样一个场景,客户机想和服务器进行通信。为了实现两台计算机之间的通信,那么必须用一个网络线路将它们连接起来,如下图:
客户机指请求信息的计算机或程序,服务器指提供信息的计算机或程序。客户机和服务器之间要想进行通信,那么需要用网络将它们连接起来。那么什么是网络呢?
1、计算机网络
1.1、网络的概念
在计算机领域中,网络是信息传输、接收、共享的虚拟平台,通过它把各个点、面、体的信息联系到一起,从而实现这些资源的共享。网络是人类发展史来最重要的发明,提高了科技和人类社会的发展。
计算机网络实现了多个计算机之间互连系统,互相连接的计算机之间能够进行数据交流。网络程序指编写能够与其他计算机进行通信的程序。Java中已经将网络程序所需的东西封装成了类,只要创建这些类的对象,使用一些方法,即使我们不太了解网络知识,也能编写出网络通信程序。但还是有必要了解网络的一些相关概念。
1.2、网络的分类
可以按照不同的分类标准来划分。这里只按覆盖范围来分类,分为局域网、城域网和广域网。
1.2.1、局域网(LAN)
局域网(Local Area Network)是在一个局部的地理范围内(如一个学校、工厂和机关内),将各种计算机、外部设备和数据库等互相联接起来组成的计算机通信网,简称LAN。它可以通过数据通信网或专用数据电路,与远方的局域网、数据库或处理中心相连接,构成一个大范围的信息处理系统。
理解:局域网简称LAN,一般限定在较小的区域内,小于10km的范围,通常采用有线的方式连接起来。
图示:
1.2.2、城域网(MAN)
城域网(Metropolitan Area Network)是在一个城市范围内所建立的计算机通信网,简称MAN。属宽带局域网。由于采用具有有源交换元件的局域网技术,网中传输时延较小,它的传输媒介主要采用光缆,传输速率在100兆比特/秒以上。
理解:城域网简称MAN ,比局域网的覆盖范围大一些,规模局限在一座城市的范围内,10~100km的区域。
如下图:
1.2.3、广域网(WAN)
广域网WAN(Wide Area Network)也叫远程网RCN(Remote Computer Network),它的作用范围最大,一般可以从几十公里至几万公里。一个国家或国际间建立的网络都是广域网。在广域网内,用于通信的传输装置和传输介质可由电信部门提供。目前,世界上最大的信息网络Internet已经覆盖了包括我国在内的180多个国家和地区,连接了数万个网络,终端用户已达数千万.并且以每月15%的速度增长。
理解:广域网简称WAN,覆盖范围最大,网络跨越国界、洲界,甚至全球范围。目前局域网和广域网是网络的热点。局域网是组成其他两种类型网络的基础,城域网一般都加入了广域网。广域网的典型代表是Internet网。
如下图:
1.3、网络协议
网络协议为计算机网络中进行数据交换而建立的规则、标准或约定的集合。例如,网络中一个微机用户和一个大型主机的操作员进行通信,由于这两个数据终端所用字符集不同,因此操作员所输入的命令彼此不认识。为了能进行通信,规定每个终端都要将各自字符集中的字符先变换为标准字符集的字符后,才进入网络传送,到达目的终端之后,再变换为该终端字符集的字符。当然,对于不相容终端,除了需变换字符集字符外还需转换其他特性,如显示格式、行长、行数、屏幕滚动方式等也需作相应的变换。
理解:网络协议为是计算机之间进行通信而建立的一种标准的集合。
要说网络协议,就不得不提最经典的OSI模型。
1.3.1、OSI七层模型
OSI模型,即开放式通信系统互联参考模型(Open System Interconnectionl),是国际标准化组织(ISO)提出的一个试图使各种计算机在世界范围内互连为网络的标准框架,简称OSI。这是一种事实上被TCP/IP 4层模型淘汰的协议。在当今世界上没有大规模使用。
OSI定义了网络互连的七层框架:物理层、数据链路层、网络层、传输层、会话层、表示层、应用层,即ISO开放互连系统参考模型。如下图:
最底层是物理层,最顶层是应用层。每层的作用如下:
- 物理层:物理层规定了激活、维持、关闭通信端点之间的机械特性、电气特性、功能特性以及过程特性。该层为上层协议提供了一个传输数据的物理媒体。在这一层,数据的单位称为比特(bit)。
- 数据链路层:数据链路层在不可靠的物理介质上提供可靠的传输。该层的作用包括:物理地址寻址、数据的成帧、流量控制、数据的检错、重发等。在这一层,数据的单位称为帧(frame)。
- 网络层:网络层负责对子网间的数据包进行路由选择。网络层还可以实现拥塞控制、网际互连等功能。在这一层,数据的单位称为数据包(packet)。网络层协议的代表包括:IP、IPX、RIP、OSPF、ARP、RARP、ICMP、IGMP等。
- 传输层:传输层是第一个端到端,即主机到主机的层次。传输层负责将上层数据分段并提供端到端的、可靠的或不可靠的传输。此外,传输层还要处理端到端的差错控制和流量控制问题。在这一层,数据的单位称为数据段(segment)。传输层协议的代表包括:TCP、UDP、SPX等。
- 会话层:会话层管理主机之间的会话进程,即负责建立、管理、终止进程之间的会话。会话层还利用在数据中插入校验点来实现数据的同步。
- 表示层:表示层对上层数据或信息进行变换以保证一个主机应用层信息可以被另一个主机的应用程序理解。表示层的数据转换包括数据的加密、压缩、格式转换等。
- 应用层:应用层为操作系统或网络应用程序提供访问网络服务的接口。应用层协议的代表包括:Telnet、FTP、HTTP、SNMP等。
1.3.2、IP协议
IP是Internet Protocol的的缩写,网络之间互连的协议,简称网协。网络之间互连的协议也就是为计算机网络相互连接进行通信而设计的协议。在因特网中,它是能使连接到网上的所有计算机网络实现相互通信的一套规则,规定了计算机在因特网上进行通信时应当遵守的规则。任何厂家生产的计算机系统,只要遵守IP协议就可以与因特网互连互通。IP地址具有唯一性,根据用户性质的不同,可以分为5类。另外,IP还有进入防护,知识产权,指针寄存器等含义。IP协议是OSI中第三层网络层的协议。
如今的IP网络使用32位地址,以点分十进制表示,如192.168.0.1。
地址格式为:IP地址=网络地址+主机地址或 IP地址=网络地址+子网地址+主机地址。
网络地址是因特网协会的ICANN分配的,下有负责北美地区的InterNIC、负责欧洲地区的RIPENIC和负责亚太地区的APNIC,目的是为了保证网络地址的全球唯一性。主机地址是由各个网络的系统管理员分配。因此,网络地址的唯一性与网络内主机地址的唯一性确保了IP地址的全球唯一性。
IP地址是每台计算机的唯一标识,类似手机SIM卡的作用,有了IP地址,才能实现在LAN局域网或WAN广域网上通信,如192.168.1.31。IP地址有IPv4和IPv6,IPv4已经分配完毕,下一代的协议会使用IPv6。.
1.3.3、TCP协议
TCP是Transmission Control Protocol的缩写,传输控制协议。TCP是一种面向连接(连接导向)的、可靠的、基于字节流的运输层(Transport layer)通信协议,由IETF的RFC 793说明(specified)。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。TCP协议以固线连接稳基础,提供可靠的数据传输。
特点:
- 面向连接的传输。
- 端到端的通信。
- 高可靠性,确保传输数据的正确性,不出现丢失或乱序。
- 全双工方式传输。
- 采用字节流方式,即以字节为单位传输字节序列。
- 紧急数据传送功能。
1.3.4、UDP协议
UDP是User Datagram Protocol的简称,用户数据报协议,是OSI模型中一种无连接的传输层协议,提供面向事务的简单不可靠信息传送服务,IETF RFC 768是UDP的正式规范。UDP是无线连接通信协议,不保证可靠的数据传输。
特点:
- UDP是一个无连接协议,传输数据之前源端和终端不建立连接,当UDP它想传送时就简单地去抓取来自应用程序的数据,并尽可能快地把它扔到网络上。
- 由于传输数据不建立连接,因此也就不需要维护连接状态,包括收发状态等,因此一台服务机可同时向多个客户机传输相同的消息。
- UDP信息包的标题很短,只有8个字节。
- 吞吐量不受拥挤控制算法的调节,只受应用软件生成数据的速率、传输带宽、源端和终端主机性能的限制。
- UDP使用尽最大努力交付,即不保证可靠交付。
- UDP是面向报文的。
TCP协议与UDP协议的区别:
- TCP协议基于固接连线,UDP协议基于无连接。
- TCP协议对系统资源要求较多,UDP对系统资源要求较少。
- TCP程序结构较复杂,UDP程序结构简单。
- TCP协议是基于字节流模式的,而UDP协议是基于数据报模式。
- TCP协议保证数据正确性,不会丢包。UDP协议可能丢包。
- TCP协议保证数据的顺序,UDP不保证数据的顺序。
1.3.5、TCP/IP协议
TCP/IP是基于TCP和IP这两个最初的协议之上的不同的通信协议的大的集合,定义了电子设备如何接入互联网以及数据之间如何相互传输。它是Internet的核心协议。
TCP/IP的四层模型如下:
细分的话其实是五层的,这里将物理层和数据链路层都纳入网络接口层了。那么常说的四层和五层都是正确的,只是划分粒度不同而已。
OSI模型和TCP/IP模型对比如下:
2、端口和套接字
2.1、端口
端口是英文port的意译,可以认为是设备与外界通讯交流的出口。端口可分为虚拟端口和物理端口,其中虚拟端口指计算机内部或交换机路由器内的端口,不可见。
一般的,一台计算机只有单一的连到网络的物理连接,所有数据都通过此连接对内外送达特定的计算机,这便是端口。这里所说的端口其实是协议端口,并非真实的物理存在,协议端口是由本地操作系统分配给每个进程的,每个协议端口由一个正整数标识,范围在0-65535之间。比如HTTP服务一般使用80端口,FTP服务一般使用21端口。通常1-1023之间的端口用于一些比较出名的网络服务和应用,我们应该尽量将端口设置在1024以上,在不超过范围的情况下,端口号设置的越大越好,以免被其他应用占用了。
图示:
一般客户机是通过不同的端口来连到服务器上的不同服务,比如通过80端口连到HTTP服务,通过21端口连到FTP服务。
2.2、套接字
TCP用主机的IP地址加上主机上的端口号作为TCP连接的端点,这种端点就叫做套接字(socket)或插口。套接字用(IP地址:端口号)表示。
它是网络通信过程中端点的抽象表示,包含进行网络通信必需的五种信息:连接使用的协议,本地主机的IP地址,本地进程的协议端口,远地主机的IP地址,远地进程的协议端口。
可以将套接字socket理解为一个连接装置,用于将端口和应用程序连接起来。Java中将套接字抽象化为类,只需创建Socket类的对象,便可使用套接字。
图示:
套接字相当于连接装置,用于将程序和端口连接起来。
3、URL类
统一资源定位符(Uniform Resource Locator,URL)是对可以从互联网上得到的资源的位置和访问方法的一种简洁的表示,是互联网上标准资源的地址。互联网上的每个文件都有一个唯一的URL,它包含的信息指出文件的位置以及浏览器应该怎么处理它。如:http://www.baidu.com/index.jsp。
Java中是提供了一个java.net.URL类的。api文档说明下:
提供的构造方法:
提供的方法:
常用方法说明如下:
- getAuthority():获取此的授权部分URL,返回String类型。
- getContent():获取此URL的内容,返回Object类型。
- getFile():获取此URL的文件名,返回String类型。
- getHost():获取此URL的主机名,返回String类型。
- getPath():获取URL的路径部分,返回String类型。
- getPort():获取URL的端口号,返回int型。
- getProtocol():获取URL的协议名称,返回String类型。
- getQuery():获取URL的查询部分,返回String类型。
- openConnection():返回一个URLConnection实例,表示与URL引用的远程对象的URL 。
- openStream():打开此URL ,并返回一个InputStream ,以便从该连接读取。
- sameFile(URL other):比较两个URL,不包括片段组件,返回布尔值。
- toString():构造此URL的字符串表示的URL 。
- toURI():返回相当于此URL的URI,返回URI类型。
以上方法在代码中测试:
public static void main(String[] args) throws IOException {
// URL对象的构建方式一
URL u0 = new URL("http://www.baidu.com");
// URL对象的构建方式二,四个参数分别表示:协议名称、主机名、端口号、资源名称
URL u2 = new URL("http", "localhost", 9999, "dog.jpg");
// URL对象的构建方式三,相对
URL u3 = new URL(u0,
"s?ie=utf-8&f=8&rsv_bp=1&rsv_idx=1&tn=baidu&wd=QQ&fenlei=256&rsv_pq=ecf7affb0001b5fe&rsv_t=57a2MEf4OSrYG9jEPzeI8lqm%2F79F07vhJ0xhBIP1dhZewXQ6OxhnhCJaeL8&rqlang=cn&rsv_dl=tb&rsv_enter=1&rsv_sug3=1&rsv_sug1=1&rsv_sug7=100&rsv_sug2=0&rsv_btype=i&prefixsug=QQ&rsp=5&inputT=6249&rsv_sug4=6250");
String authURL = u3.getAuthority();
System.out.println("授权部分URL是:" + authURL);
Object obj = u3.getContent();
System.out.println("上下文是:" + obj.toString());
String fileName = u2.getFile();
System.out.println("此URL对应的资源名称:" + fileName);
System.out.println("此URL的主机名:" + u2.getHost());
System.out.println("此URL的路径部分是:" + u2.getPath());
System.out.println("此URL的端口是:" + u2.getPort());
System.out.println("此URL的协议名称是:" + u3.getProtocol());
System.out.println("此URL的查询部分是:" + u3.getQuery());
// 下载资源
// 构建资源所在路径的URL对象
URL url = new URL("http://pic26.nipic.com/20121213/11579548_163923349131_2.jpg");
// 调用openConnection方法,获取连接
// HttpURLConnection connection = (HttpURLConnection) url.openConnection();
// // 获取输入流
// InputStream inputStream = connection.getInputStream();
// 调用openStream方法,直接获取输入流,推荐用这种
InputStream inputStream = url.openStream();
// 要保存的文件目录
File file = new File("F:/Users/Administrator/Desktop/图片/");
if (!file.exists()) {
file.mkdirs();
}
// 要保存的文件对象
File f = new File(file, "dog.jpg");
// 输出流
OutputStream outputStream = new FileOutputStream(f);
int len = 0;
while ((len = inputStream.read()) != -1) {
// 写入
outputStream.write(len);
}
// 关闭流
outputStream.close();
inputStream.close();
System.out.println("下载完成!");
}
执行,控制台:
桌面:
4、InetAddress类
java.net.InetAddress类是与IP地址有关的类,利用该类可以获取IP地址、主机地址等信息。TCP和UDP都是在IP协议的基础上进行构建的。InetAddress类没有构造器,是无法通过new来创建的,只能通过静态方式来获取它的实例。
api说明如下:
提供的方法如下:
常用方法说明如下:
- getAddress():返回此InetAddress对象的原始IP地址,是一个byte型数组。
- getAllByName(String host):给定主机的名称,根据系统上配置的名称服务返回其IP地址数组,返回InetAddress数组,这是静态方法。
- getByAddress(byte[] addr):返回原始IP地址的InetAddress对象,这是静态的。
- getByAddress(String host, byte[] addr):根据提供的主机名和IP地址创建InetAddress对象,这是静态的。
- getByName(String host):确定主机名称的IP地址,返回InetAddress对象,这是静态的。
- getCanonicalHostName():获取此IP地址的完全限定域名,返回String类型的。
- getHostAddress():返回InetAddress对象中包含的IP地址。
- getHostName():获取此IP地址的主机名。
- getLocalHost():返回包含本地主机地址的InetAddress对象。
代码测试:
public static void main(String[] args) throws IOException {
// 获取本地InetAddress对象
InetAddress inetAddress = InetAddress.getLocalHost();
System.out.println("主机名:" + inetAddress.getHostName());
System.out.println("IP地址:" + inetAddress.getHostAddress());
// 获取指定地址的InetAddress对象
InetAddress inetAddress2 = InetAddress.getByName("www.github.com");
System.out.println("主机名:" + inetAddress2.getHostName());
System.out.println("IP地址:" + inetAddress2.getHostAddress());
System.out.println("完全限定名:" + inetAddress2.getCanonicalHostName());
}
5、SocketAddress类
java.net.SocketAddress类代表的是一个没有协议附件的Socket地址。这个类是抽象类,自身是不能够实例化的,只能依赖于子类。api文档说明如下:
此类不提供构造方法和任何的方法。
5.1、InetSocketAddress类
java.net.InetSocketAddress类是SocketAddress类的直接子类。api中说明如下:
构造方法:
提供的方法如下:
常用方法说明如下:
- getAddress():获取InetAddress对象 。
- getHostName():获取主机名,返回String类型。
- getHostString():如果没有主机名(使用文字创建),则返回主机名或地址的String形式。
- getPort():获取端口号,返回int类型。
测试如下:
public static void main(String[] args) throws IOException {
// 构建SocketAddress对象
InetSocketAddress socketAddress = new InetSocketAddress("localhost", 9990);
// 获取InetAddress对象
InetAddress addr = socketAddress.getAddress();
System.out.println("主机名:" + addr.getHostName());
System.out.println("主机名:" + socketAddress.getHostName());
System.out.println("主机名的String表示:" + socketAddress.getHostString());
System.out.println("端口号:" + socketAddress.getPort());
}
6、TCP通信
TCP通信指利用TCP协议进行通信,需要用到Socket类来编写通信程序、一般有两个程序,一个为客户端程序,一个为服务端程序,这两个的编写方式不一样。
客户端和服务端的程序交互如下图:
6.1、客户端
客户端会用到Socket类,Socket类实现客户端套接字(简称套接字)。套接字是两台机器间通信的端点。Java基于互联网的C/S模式的客户端程序主要依赖此类实现与服务器端的通信,它是Java网络编程的核心组件。Socket组件实例必须绑定IP地址(客户端网络域名)和一个未被绑定在本地机器占用的端口号。常用到输入输出流来和服务器端进行交互。
关于Socket类,api说明如下:
构造方法:
提供的方法如下:
常用的方法说明如下:
- bind(SocketAddress bindpoint):将套接字绑定到本地地址。
- close():关闭此套接字。
- connect(SocketAddress endpoint):将此套接字连接到服务器。
- getInetAddress():返回套接字所连接的地址,返回InetAddress对象。
- getLocalAddress():获取套接字所绑定的本地地址。
- getInputStream():返回此套接字的输入流,返回InputStream对象。
- getOutputStream():返回此套接字的输出流,返回OutputStream对象。
- getLocalPort():返回此套接字绑定到的本地端口号。
- getPort():返回此套接字连接到的远程端口号。
- isBound():返回套接字的绑定状态,返回布尔值。
- isClosed():返回套接字的关闭状态,返回布尔值。
- isConnected():返回套接字的连接状态,返回布尔值。
以下使用Socket类编写一个TCP通信的客户端程序:
/*
* 模拟客户端
*/
public class TcpClient {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
// 实例化Socket对象
Socket socket = new Socket();
// 基于本机和2222端口实例化InetSocketAddress对象,客户端
InetSocketAddress inetSocketAddress = new InetSocketAddress("localhost", 2222);
// 服务端
InetSocketAddress inetSocketAddress2 = new InetSocketAddress("localhost", 3333);
// 输出流
OutputStream outputStream = null;
// 数据输出流
DataOutputStream dataOutputStream = null;
// 输入流
InputStream inputStream = null;
// 数据输入流
DataInputStream dataInputStream = null;
try {
// 套接字绑定到本地地址
socket.bind(inetSocketAddress);
System.out.println("本地套接字是否已绑定?" + socket.isBound());
System.out.println("本地套接字是否已连接到服务端套接字?" + socket.isConnected());
// 套接字连接到服务端地址
socket.connect(inetSocketAddress2);
System.out.println("本地套接字是否已连接到服务端套接字?" + socket.isConnected());
// 客户端获取输出流
outputStream = socket.getOutputStream();
// 数据输出流
dataOutputStream = new DataOutputStream(outputStream);
// 客户端获取输入流
inputStream = socket.getInputStream();
// 数据输入流
dataInputStream = new DataInputStream(inputStream);
while (true) {
System.out.println("请输入信息:");
String message = sc.next();
// 输出流写入信息给服务端
dataOutputStream.writeUTF(message);
// 输入流从服务端读取信息
String serverMessage = dataInputStream.readUTF();
System.out.println("服务端返回的信息是:" + serverMessage);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
执行,控制台:
因为服务端还没有,连接不上,肯定会报错。
6.2、服务端
java.net.ServerSocket类表示服务端的套接字。主要功能是等待与来自客户机的连接请求,可以通过指定的端口来连接套接字,服务器套接字一次只能与一个客户机套接字连接,如果有多台客户机同时发出连接请求,那么服务端套接字会将请求的客户机存到队列中,然后一次取出一个套接字进行连接,如果请求的连接数大于最大容纳数,那么多出的连接请求会被拒绝掉。队列的默认容纳量是50。
api说明如下:
构造方法:
提供的方法如下:
常用方法说明如下:
- accept():侦听要连接到此套接字并接受它,返回Socket对象。
- bind(SocketAddress endpoint):将 ServerSocket绑定到特定地址。
- close():关闭此套接字。
- getInetAddress():返回此服务器套接字的本地地址,返回InetAddress对象。
- getLocalPort():返回此套接字正在侦听的端口号。
- isBound():返回ServerSocket的绑定状态,返回布尔值。
- isClosed():返回ServerSocket的关闭状态,返回布尔值。
以下使用ServerSocket类编写一个TCP通信的服务端程序:
public class TcpServer {
public static void main(String[] args) {
try {
// 实例化ServerSocket对象,指定端口
ServerSocket serverSocket = new ServerSocket(3333);
Socket socket = null;
InputStream inputStream = null;
DataInputStream dataInputStream = null;
OutputStream outputStream = null;
DataOutputStream dataOutputStream = null;
while (true) {
System.out.println("正在等待客户端的连接......");
// accept方法会阻塞,返回一个Socket对象,等待客户机发出连接请求
// 如果客户机一直未发出连接请求,则accept方法后面的代码不会执行
socket = serverSocket.accept();
System.out.println("客户端连接成功!");
// 获取输入流
inputStream = socket.getInputStream();
dataInputStream = new DataInputStream(inputStream);
// 获取输出流
outputStream = socket.getOutputStream();
dataOutputStream = new DataOutputStream(outputStream);
while (1 == 1) {
// 读取客户端发送的信息
String clientMessage = dataInputStream.readUTF();
System.out.println("客户端发送的消息是:" + clientMessage);
// 回复信息给客户端
dataOutputStream.writeUTF("服务端已收到消息!");
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
执行:
因为客户端没有启动,服务端没有接收到连接请求,所以阻塞在accept方法那里了。
再启动客户端程序,客户端控制台:
再看服务端控制台:
客户端已经连接到服务端了。下面从服务端发送消息:
服务端控制台:
那么以上基于TCP协议的客户端可服务端就通信成功了。
7、UDP通信
UDP通信指利用UDP协议进行通信,需要用到DatagramPacket类和DatagramSocket类来编写通信程序。也有两个程序,一个为客户端,一个为服务端。UDP通信和TCP通信不同,基于UDP的信息传递更快,但不保证可靠的传输。使用UDP传递数据时,用户无法确定数据能够正确传到主机,也无法确定达到的顺序和发送的顺序一致,这正是UDP协议的特点。
7.1、客户端
java.net.DatagramPacket表示数据报包,用来实现无连接包投递服务。数据报包中可以包含各种类型的数据对象,每条报文只根据该包中包含的信息(通常是url和端口号)从一台机器路由到另一台机器,从一台机器发送到另一台机器的多个包可能选择不同的路由,也可能按不同的顺序到达。不对包投递作出保证。
api说明如下:
构造方法:
提供的方法如下:
常用方法说明如下:
- getAddress():返回该数据报发送或接收数据报的计算机的IP地址,返回InetAddress对象。
- getData(),获取数据缓冲区,返回byte数组。
- getLength():返回要发送的数据的长度或接收到的数据的长度,返回int型。
- getOffset():返回要发送的数据的偏移量或接收到的数据的偏移量。
- getPort():返回发送数据报的远程主机上的端口号,或从中接收数据报的端口号。
- getSocketAddress():获取该数据包发送到或正在从其发送的远程主机的SocketAddress对象。
- setAddress(InetAddress iaddr):设置该数据报发送到的机器的IP地址。
- setData(byte[] buf):设置此数据包的数据缓冲区。
- setLength(int length):设置此数据包的长度。
- setPort(int iport):设置发送此数据报的远程主机上的端口号。
- setSocketAddress(SocketAddress address):设置该数据报发送到的远程主机的SocketAddress。
java.net.DatagramSocket类是用于发送和接收数据包的套接字。数据报套接字是包投递服务的发送点或接收点,每个在数据报套接字上发送或接收的包都是单独编址或路由的。从一台机器发送到另一台机器的多个包可能选择不同的路由,也可能按不同的顺序到达。
api说明如下:
构造方法:
提供的方法如下:
常用方法说明如下:
- bind(SocketAddress addr):将此DatagramSocket绑定到特定的地址和端口。
- close():关闭此数据报套接字。
- connect(SocketAddress addr):将此套接字连接到远程套接字地址(IP地址+端口号)。
- disconnect():断开插座。
- getInetAddress():返回此套接字连接到的地址。
- getLocalAddress():获取套接字所绑定的本地地址。
- getLocalPort():返回此套接字绑定到的本地主机上的端口号。
- getLocalSocketAddress():返回此套接字绑定到的端点的地址。
- getPort():返回此套接字连接到的端口号。
- isBound():返回套接字的绑定状态,返回布尔值。
- isClosed():返回套接字是否关闭,返回布尔值。
- isConnected():返回套接字的连接状态,返回布尔值。
- receive(DatagramPacket p):从此套接字接收数据报包。
- send(DatagramPacket p):从此套接字发送数据报包。
下面使用DatagramPackage类配合DatagramSocket类编写客户端程序:
/*
* 模拟客户端
*/
public class UdpClient {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
// 实例化SocketAddress对象
SocketAddress socketAddress = new InetSocketAddress("localhost", 4444);
DatagramPacket datagramPacket = null;
DatagramPacket datagramPacket2 = null;
try {
// 实例化DatagramSocket对象
DatagramSocket datagramSocket = new DatagramSocket(5555);
// 套接字连接到服务端地址
datagramSocket.connect(socketAddress);
System.out.println("套接字是否已连接到服务端?" + datagramSocket.isConnected());
while (true) {
System.out.println("请输入要发送的信息:");
String clientMessage = in.next();
// 转为字节数组
byte[] bys = clientMessage.getBytes("utf-8");
// 基于字节数组构建数据报包
datagramPacket = new DatagramPacket(bys, bys.length);
// 此字节数组容器用来接收数据包
byte[] bys2 = new byte[1024];
datagramPacket2 = new DatagramPacket(bys2, bys2.length);
// 套接字发送数据包
datagramSocket.send(datagramPacket);
// 接收数据包
datagramSocket.receive(datagramPacket2);
String res = new String(bys2, "utf-8");
System.out.println("服务端返回的消息是:" + res);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
执行:
7.2、服务端
下面使用DatagramPackage类配合DatagramSocket类编写服务端程序:
/*
* 模拟服务端
*/
public class UdpServer {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
// 这个字节数组用来接收数据包
byte[] bys = new byte[1024];
DatagramPacket datagramPacket = new DatagramPacket(bys, bys.length);
try {
InetAddress inetAddress = InetAddress.getByName("localhost");
// 实例化DatagramSocket对象
DatagramSocket datagramSocket = new DatagramSocket(4444);
System.out.println("准备接收客户端发过来的消息......");
while (true) {
// 套接字接收数据包
datagramSocket.receive(datagramPacket);
// 获取数据
String res = new String(bys, "utf-8");
System.out.println("客户端发送的消息是:" + res);
System.out.println("请输入要回复的消息:");
String s = in.next();
byte[] bys2 = s.getBytes("utf-8");
DatagramPacket datagramPacket2 = new DatagramPacket(bys2, bys2.length);
// 绑定IP地址
datagramPacket2.setAddress(inetAddress);
// 设置端口
datagramPacket2.setPort(5555);
// 套接字发送数据包
datagramSocket.send(datagramPacket2);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
执行,控制台:
在客户端的控制台输入信息:
服务端控制台:
客户端:
服务端:
客户端:
以上就已经完成了基于UDP的客户端程序和服务端程序的通信,事实上,很像我们用的QQ、微信这种社交软件互发消息。
8、总结
网络编程中掌握几种通信协议,以及常用的几个类,并且能够熟练使用Socket套接字来编写基于TCP协议和UDP协议的客户端和服务端程序,工作中可能会使用到。