您的位置:首页 > 理论基础 > 计算机网络

黑马程序员-13-java网络编程-概念及UDP、TCP连接

2015-05-28 15:46 846 查看
------- android培训java培训、期待与您交流!
----------

网络编程

在掌握了基本的程序编写技巧后,我们来看一下Java网络编程。在这部分总结之前,我们首先需要掌握一些计算机网络的学科知识,这里给出了部分主要内容的概述:

端口:为进程间通信提供必要标识的数据标识,我们称之为端口。它是计算机程序为了区分不同进程而创立的区别方式。因此,端口号只具有本地意义,对应主机间通讯,在某种情况下必须指明对应接受传输数据端口号。

端口号分为两种:服务端使用端口号、客户端使用端口号。

服务端使用端口号又分为:熟知端口号、登记端口号。

熟知端口号:0~1023,IANA,即互联网地址指派机构,将此类端口派给了TCP/IP协议的一些重要程序。

登记端口号:1024~49151,此类端口号为一些没有熟知端口号的应用程序使用。想要使用登记端口号,则必须在IANA登记才可使用。

客户端使用端口号:又被称为短暂端口号,因为此类端口号只在客户进程运行时才会动态的选择调用。当用户进程结束时,此类端口则会自动释放。便于其他进程使用。此类端口号并没有具体使用要求,一般在程序运行时,都会由系统分配,并回收。

端口的存在,另一点就是为直达进程的网际进程间通信,打下了条件。

除了端口之外,在网际数据传输上,我们根据实际情况将计算机网络体系结构分为一下两种模型。



图8.计算机网络体系结构图

本质上,这两种模型都是对同一种事物,即网络,的不同描述。我们这里的Java程序,主要运用到的是TCP/IP中传输层、网际层,这两层的协议。如果是Java-Web开发,则会涉及应用层的协议。具体协议划分、数据封装,以及报文、报文段、分组、帧、数据报,这些内容,如果想要详细了解,则看计算机网络对应教材,此处不再赘述。

想要在Java编程中使用到网络编程,则需要我们引入java.net包。java.net包实现为了实现对网络通讯的三种操作:地址控制、端口控制、规则控制。其中,端口因为不需要太多操作,且完全由数字及IANA规定了使用规则,Java中就只需要实现地址控制和规则操作了。

InetAddress系列IP地址类

java.net包中,用来描述IP地址的是InetAddress类和他的两个字累Inte4Address与Inet6Address。IP地址类主要被我们用于对IP地址信息的Java操作。常用来获取指定主机或IP地址的网络信息。如例:

import java.net.*;

class  MyIPTest
{
public static void main(String[] args)
{
//本地主机地址获取
InetAddress i =InetAddress.getLocalHost();
System.out.println(i.toString());
System.out.println("name:"+i.getHostName());
System.out.println("address:"+i.getHostAddress());

//获取目标IP对应主机地址信息
InetAddress i2 =InetAddress.getByName("8.8.8.8");
System.out.println("name:"+i.getHostName());
System.out.println("address:"+i.getHostAddress());

//获取目标主机名所对应的一系列IP地址
InetAddress[] i3 =InetAddress.getAllByName("www.google.com");
for(int n = 0; n<i3.length();n++)
{
System.out.println("name:"+i3
.getHostName());
System.out.println("address:"+i
.getHostAddress());
}

}
}


该例就是对获取主机地址信息常用方法的一个类比应用。可以从其结果和参数上,看出各个方法的着重点。

java.net包中,用于描述传输规则的类分为两种类型方向,UDP和TCP。

UDP:不可靠,即时性,面向无连接,每隔数据报大小限制在64K之内。

TCP:“三次握手、四次挥手”式连接,可靠,效率低、非即时,连接形成传输通道。

所以,即时通讯常用UDP,以保证即时性。但是如一些下载文件之类的通信,常用TCP以保证完整性。

这两个网络规则的实现,避免不了要涉及到文件的传输和对应进程的定位。实现这种操作的基础就是Socket套接字服务。Socket套接字,计算机间通过套接字来实现端对端通信。简易定义Socket=(IP地址,端口号)。

我们首先从UDP入手。

UDP服务

UDP的套接字类包括:DatagramSocket和DatagramPacket。UDP的收发服务及数据传输,在Java中就是由这两个类来实现的。我们通过实例来说明具体步骤。

文件发送端步骤演示:

import java.net.*;

class  UDP_sendDemo
{
public static void main(String[] args)
{
//1.通过DatagramSocket创立UDP服务
DatagramSocket UDPs = newDatagramSocket();

//2.确定传输数据,将其封入DatagramPacket对象中,进行传输前准备
byte[] buf = "This is an UDPpacket.".getBytes();
DatagramPacket UDPp =
newDatagramPacket(buf,buf.length,InetAddress.getByName("127.0.0.1"),7070);

//3.通过开启的UDP服务,将数据发给指定接收方
UDPs.send(UDPp);

//4.发送结束,关闭服务
UDPs.close();
}
}
/*当然,有发送就得有接受,没有给定接收方,这样的发送必然发生丢包。
*/


文件接收端步骤演示:

import java.net.*;

class  UDP_receiveDemo
{
public static void main(String[] args)
{
//1.通过DatagramSocket创立UDP接收端点,并指定监听端口
DatagramSocket UDPs = newDatagramSocket(7070);

//2.定义本地DatagramPacket对象用于存储接收数据,便于用其类封装的方法
byte[] buf = new byte[1024];
DatagramPacket UDPp =
newDatagramPacket(buf,buf.length);

//3.通过UDP服务,接收数据
UDPs.receive(UDPp);

//4.通过DatagramPacket类封装方法获取数据
String ip =UDPp.getAddress().getHostAddress();
String res =UDPp.getAddress().getHostName();
String data = newString(UDPp.getData(),0,UDPp.getLength());

int port = UDPp.getPort();
//此处获取的端口号为发送端定义的端口号,该号由发送端决定

System.out.println(res+"::"+ip+"::"+port+"::"+data);

//5.接收结束,关闭服务(可选操作)
UDPs.close();
}
}
/*在定义接收端的时候,一般会为接收端指定一个和发送端端口对应的端口。用于分辨
对应接收信息。receive方法是一个阻塞式方法,如果没有接收到所求数据,则程序
在该位置被阻塞。直到获取所需信息。
另一个需要注意的是,如果没有指定端口号,不论是发送端还是接收端,系统都会为
未指定端口的进程分配一个系统端口。该端口号,数字上是随程序顺序递增的。通常,
为我们为了保证程序收发的正常进行,会为接收端指定端口号。我们也可用一样的方
法为发送端指定端口号。
*/


当然,想要发送成功,就如上面两例演示所展示,必须得先开接收端。有了接收端,发送端的数据才不会丢包。

实际生活中,接收端一般都是24小时运行的。一般情况下,作为接收端,我们会将它内部接受信号部分(即除去UDP服务开启部分),放入一个while条件循环当中。这样是为了保证接收端能够不断的接收到客户端信息。

TCP服务

TCP的套接字类包括:Socket和SeverSocket。从字面上不难看出TCP的类是以客户端、服务端进行分类的。客户端和服务端作为两个端点,只要建立好并完成连接,就可以通讯实现数据传输。



图9.TCP客户端与服务端数据传输示意图

我们通过实例来说明上图中的客户端与服务端的信息传递具体步骤。下面这则实例,我们描述了从客户端向服务端发送数据的过程。

客户端文件发送步骤演示:

import java.net.*;
import java.io.*;

class  TCP_clientDemo
{
public static void main(String[] args)
{
//1.建立客户端socket服务,指定服务端IP和对应端口
Socket client = newSocket("8.8.8.8",9920);

//2.建立输出流,发送需求数据
OutputStream out =client.getOutputStream();
out.write("This is a TCPdata".getBytes());

//3.任务完成,客户端关闭
client.close();

}
}


服务端文件接收步骤演示:

import java.net.*;
import java.in.*;

class  TCP_serverDemo
{
public static void main(String[] args)
{
//1.建立服务端socket服务,并设立监听端口
ServerSocket server = newServerSocket(9220);

//2.通过服务端SeverSocket类中的accept方法,获取客户端对象
Socket sc = server.accept();

//3.建立输入流,读取客户端发送信息
{
InputStream in =sc.getInputStream();

byte[] buf = newbyte[1024];
int length = in.read(buf);
String s = newString(buf,0,length);

System.out.println(s);
}
//4.接收结束,关闭客户端
sc.close();  //服务端关闭可规定在一定时间之后。

//5.关闭服务端(可选操作)
server.close();

}
}


上例中,我们只演示了从客户端向服务端发送信息的情况。接下来为我们来演示一下,当服务端接受到请求后,向客户端发送数据的过程。

TCP客户端代码:

import java.net.*;
import java.io.*;

class  TCP_client_receiveDemo
{
public static void main(String[] args)
{
Socket client = newSocket("8.8.8.8",9980);

OutputStream out =client.getOutputStream();
out.write("This is aboutgeting answers.".getBytes());

//关闭客户端输出流:
client.shutdownOutput();

//客户端接受服务端文件:
InputStream in =client.getInputStream();
byte[] buf = new byte[1024];
int len = in.read(buf);

System.out.println("client_request::"+newString(buf,0,len));

client.close();
}
}


TCP服务端代码:

import java.net.*;
import java.io.*;

class  TCP_server_sendDemo
{
public static void main(String[] args)
{
ServerSocket server = newServerSocket(9980);

Socket s = server.accept();

String ip =s.getInetAddress().getHostAddress();
InputStream in =s.getInputStream();
byte[] buf = new byte[1024];
int len = in.read(buf);

System.out.println(ip+"::"+newString(buf,0,len));

//服务端发送文件给客户端:
OutputStream out =s.getOutputStream();
out.write("Request_Datapackage".getBytes());

s.close();
}
}


从上面的TCP实例中,我们不难看出。实际应用中TCP连接所对应服务端,也可以主动关闭客户端和自身之间的连接。而实际应用中,我们的确加入了时间判定用于关闭超时的无用连接。这样的特征是和TCP“三次握手、四次挥手”的特点相对应的。

需要注意的是,TCP是一种面向连接的服务。这就决定了该服务的服务端和客户端是有先后顺序的。服务端早于客户端启动。服务端在前,客户端在后。且,当一方数据传输结束后,我们需要关闭输出流。使用对应Socket对象来调用shutdownOutput()方法来实现。

当然,我们也可以通过这种方式来建立服务器,用浏览器作为客户端访问。Tomcat服务器,就是这样的一种封装了SeverSocket的Java服务器软件,它具体的实现过程都是一样的。Windows当中也提供了telnet这个CMD命令,来实现命令控制台直连服务器,这种访问形式。题外话,不难想到,Windows这样的命令也间接的反映了它安全性的问题。

我们访问网页的时候常常要用到URL统一标示语言,来定位访问的网站的IP、端口,和位置路径。在没有封装URL类对象之前,我们常用下面这种手动方法,来进行分割提取信息。这也是URL类封装后的信息提取原理。

String url ="http://192.168.1.1:8080/Requto/default.html"
int i1 = url.indexOf("//")+2;
int i2 =url.indexOf("/",i1);

String str =url.substring(i1,i2);
String[] buf =str.split(":");
String host = buf[0];
int port =Integer.parseInt(buf[1]);
String path =url.substring(i2);


当人们发现这样做不利于程序封装,进而重新设立了URL类,来统一管理此类数据后。我们的网络地址数据提取,就变得容易了许多。通常情况下,我们常用的方法有:

String getFile():获取本URL的文件名;

String getHost():获取本URL的主机名;

String getPort():获取本URL的端口号;

String getPath():获取本URL的路径部分;

String getQuery():获取本URL的查询部;

String getProtocol():获取本URL的协议名;

URLConnection openConnection():返回本URL的一个远程对象的连接引用;

这些方法,能够帮助我们从URL对象中,获取我们所需的信息。要注意的是URLConnection类,本类将原本连接及一系列相关操作、信息,通过封装而将其升高到了应用层的层面上。这样做,简化了对应网络端对端连接的操作。由于Socket在URLConnection内部封装,URLConnection对象,不需要在结束时关闭本身。关闭操作由内部自动完成。同时,由于URLConnection是应用层类,它要求并实现了本身在输出数据的时候,会自动拆包。

除了openConnection方法外,URLConnection还提供了openStream方法。该方法能够直接返回输出流,其本身实际上是openConnection和getInputStream两者的组合。先调用方法openConnection,再调用方法getInputStream。

------- android培训java培训、期待与您交流!
----------
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: