Java: 网络编程

/ 0评 / 0

一、网络基本概念

 

1、网络的模型与要素

最近一直在看Android的网络编程,觉得网络还是蛮重要的,可能是一个核心基础吧。所以准备更新几篇关于网络编程的文章,先从Java开始。

说起网络,一定要提起网络模型这个概念。在计算机网络中,一般是指OSI(Open System Interconnection,开放式系统互联)七层参考模型TCP/IP四层参考模型。这两个模型在网络中应用最为广泛。直接上图吧!

 

网络模型

 

虽然整个网络分为很多层,但是对于程序开发人员尤其是软件开发者一般使用的都是应用层。所以着重讲一下应用层的开发内容。也就是在应用层面利用网络进行数据与信息的传输和通讯。

既然要通过网络进行通讯,那么网络通讯的基本要素是什么呢?

 

只有具备上述要素的网络,才能完成基本的网络通信。

 

2、IP地址

在网络基本要素中,IP地址是最基本的要素。我们知道,IP地址是每台联网设备的标识,可以通过cmd的ipconfig指令来查看本机的IP地址。

IP地址

但是也有一些特殊的IP地址,比如本地主机回环地址为127.0.0.1,也就是我们所称的localhost。Android模拟器中访问本地主机的IP地址为10.0.2.2。这个在后面的Android网络编程中也会具体提到。

那么在Java中如何获取主机的IP地址呢?其实在JDK的API中有这样一个方法:

 

static InetAddress

getByName(String host)

          在给定主机名的情况下确定主机的 IP 地址。

 

它返回的是一个静态的InetAddress对象,参数是String类型的主机名称。而InetAddress对象中有这样两个方法:

 

String

getHostAddress()

          返回 IP 地址字符串(以文本表现形式)。

 String

getHostName()

          获取此 IP 地址的主机名。

 

这样就能轻易的获取到主机名的IP地址和相应的主机名。示例代码如下:

 

InetAddress ia = InetAddress.getByName("www.baidu.com");

System.out.println("IP Address : " + ia.getHostAddress());

System.out.println("Host Name : " + ia.getHostName());

 

3、Socket

这里再提一个很重要的概念——Socket。翻译过来叫做“套接字”。那什么叫做Socket呢?

 

网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个Socket。

 

这么说可能有点抽象,举个百度百科的例子:

“Socket像一个多孔插座。一台主机犹如布满各种插座的房间,每个插座有一个编号,有的插座提供220伏交流电, 有的提供110伏交流电,有的则提供有线电视节目。 客户软件将插头插到不同编号的插座,就可以得到不同的服务。”

 

所以归纳来说,Socket是用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或计算机之间的通信。它是为网络服务提供的一种机制。

 

在网络编程中要注意以下几点:

 

至于如何创建Socket,方法有很多,常用的是如下方法:

Socket(InetAddress address, int port)

          创建一个流套接字并将其连接到指定 IP 地址的指定端口号。

DatagramSocket()

          构造数据报套接字并将其绑定到本地主机上任何可用的端口。

/*对于TCP/IP*/

Socket s = new Socket("127.0.0.1",10000);

/*对于UDP*/

DatagramSocket ds = new DatagramSocket();

二、网络协议

 

前面基本了解了网络中的一些必备的基本概念。现在就来看最重要的部分——网络协议。

 

网络协议常见的有UDP、TCP/IP两种,而在Java开发中一般使用TCP/IP协议。下面就分别来讲解一下两种协议的使用方法和区别。

 

1、UDP

 

UDP 是User Datagram Protocol的简称, 中文名是用户数据报协议,是OSI 参考模型中一种无连接的传输层协议,提供面向事务的简单不可靠信息传送服务。

 

而UDP协议具有如下特点:

 

既然UDP协议是用来网络通讯的,那么一定有接收和发送的方法。在JDK的API中,有下面方法:

 

 void

receive(DatagramPacket p)

          从此套接字接收数据报包。

 void

send(DatagramPacket p)

          从此套接字发送数据报包。

 

可以看到,两个方法的参数都是数据包,那么肯定需要封装一个数据包对象。然后通过套接字传递,所以也要封装套接字对象。具体步骤如下:

 

1)发送方

 
①建立UDP Socket服务:
 

/*对于UDP*/

DatagramSocket ds = new DatagramSocket();

 

②为了提供数据,所以将数据封装到数据包中:

 

DatagramPacket(byte[] buf, int length, InetAddress address, int port)

          构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号。

 

//将数据封装到byte数组中

byte [] buf = "This is UDP ".getBytes();

//构造数据包

DatagramPacket dp =

    new DatagramPacket(buf,buf.length,InetAddress.getByName("127.0.0.1"),10000);

 

③通过Socket服务的发送方法将数据包发送出去,并关闭Socket服务,抛出异常。

 

ds.send(dp);

ds.close();

 

2)接收方

 
①与发送方一样,同样需要一个UDP的Socket,但不同的是,这里一定要添加一个发送方的端口号。
 
//注意,添加发送方的端口号
DatagramSocket ds = new DatagramSocket(10000);
 
②定义一个数据包,存储接收到的字节数据。
 

//将数据封装到byte数组中

byte [] buf = new byte[1024];

//构造数据包

DatagramPacket dp = new DatagramPacket(buf,buf.length);

 

③通过Socket服务的接收方法将接收到的数据存入已经定义好的数据包中。

 

//接收数据

ds.receive(dp);

 

④数据包对象中有更多的功能可以提取自己数据中的不同数据信息。所以通过数据包对象的特有功能,能将不同数据取出并处理,比如直接打印在控制台上。

 

//获得IP地址

String ip = dp.getAddress().getHostAddress();

//获得数据

String data = new String (dp.getData(),0,dp.getLength());

//获得端口号

int port = dp.getPort();

 

⑤不要忘记关闭资源,还要抛出异常。

 

//关闭资源

ds.close();

 
 

2、TCP/IP

 
Transmission Control Protocol/Internet Protocol的简写,中译名为传输控制协议/因特网互联协议,又名网络通讯协议,是Internet最基本的协议、Internet国际互联网络的基础,由网络层的IP协议和传输层的TCP协议组成。

 

相对于UDP来说,它有以下特点:

 

值得注意的是,在TCP/IP传输中,分为客户端和服务端,而且客户端对应的对象是Socket,服务端对应的对象是ServerSocket。

与UDP一样,分为客户端和服务端两部分:

 

1)客户端

 
①创建Socket服务,并指定要连接的主机和端口:
 
//一定要指定IP和端口

Socket s = new Socket("127.0.0.1",10000);
 
②为了发送数据,要获取Socket流中的输出流:

OutputStream

getOutputStream()

          返回此套接字的输出流。

 
//获取输出流

OutputStream out = s.getOutputStream();
 
③将数据写入数据流中,记得关闭资源和抛出异常:
 
//将数据写入输入流

out.write("This is TCP/IP").getBytes();

s.close();
 

2)服务端

 
①建立服务端的ServerSocket服务,并监听一个端口号。
 

//注意端口号一定要跟客户端匹配

ServerSocket ss = new ServerSocket(10000);

 

②获取连接过来的客户端对象,这个方法是阻塞式的,如果没有连接就会一直等待 :

 

Socket

accept()

          侦听并接受到此套接字的连接。

 

//阻塞式方法 

Socket s = ss.accept();

 

③如果客户端发送数据,服务端需要使用对应的客户端对象并获取到该客户端对象的读取流来读取发送过来的数据。

 

InputStream

getInputStream()

          返回此套接字的输入流。

InetAddress

getInetAddress()

          返回套接字连接的地址。

 
//获取客户端发来的数据

InputStream in = s.getInputStream();

byte[] buf = new byte[1024];

int len = in.read(buf);

System.out.println(new String (buf,0,len));
 
④关闭服务端与客户端,这个是可选操作,另外记得抛出异常。
 
s.close();

ss.close();

 

 

 

三、注意事项

 

1、TCP/IP中,客户端和服务端进行键盘录入加入缓冲区的时候,必须加入结束符,因为客户端和服务端都是阻塞式方法,可能会出现死等的现象。

 

 

//添加回车 并刷新 防止死等

bufOut.newLine();

bufOut.flush();

 

当然也可以用结束标记符来进行防止死等的现象:

 

 void

shutdownInput()

          此套接字的输入流置于“流的末尾”。

 void

shutdownOutput()

          禁用此套接字的输出流。

 

 

//关闭客户端的输出流,相当于给流中添加了一个结束标记-1

s.shutdownOutput();

 

 

2、如何并发访问服务端?

 

可以将每个客户端封装到一个单独的线程中,使得服务端可以同时处理多个客户请求。只要明确了每一个客户端要在服务端执行的代码,并将该代码存入run方法中。

 

 

 

项目源码:

https://github.com/IamXiaRui/JavaSE_Demo_Net/tree/master/xr_java_net/src

发表评论

电子邮件地址不会被公开。 必填项已用*标注