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

JavaSE-网络编程

2016-02-05 10:58 471 查看
Java网络编程,是一个经过很好的封装之后,可供程序员简单调用进行网络连接处理的一个模块。

1、知识背景

进行网络连接,必须要具备两个要素:

一是进行通信的主机的IP地址和应用端口号;

二是进行通信所需要的通信协议;

java网络编程也是基于这两个要素,才能进行网络编程的。

网络编程中国有两个主要的问题:

1.如何准确的定位网络上的一台或多台主机

2.定位好主机后,如何进行高效的可靠的进行信息传输

这两个问题,就对应着前边网络连接的两个要素。

网络可以分为四层:应用层、传输层、网络层 和 链路层

应用层的协议有:HTTP、ftp、DNS、telnet等

传输层协议有:TCP、UDP

网络层协议有:IP、ICMP、ARP的等

TCP/IP网络协议,其实是一组协议族,包括TCP、IP、UDP等多个协议。

IP地址,在Java中对应的类就是InetAddress,唯一识别网络中的主机

端口号,区分应用程序,不同的进程由有不同的端口号,0~65535之间,其中0~1023被预先定义的服务使用,

我们应该使用1024~65535进行某个程序的通信,以避免端口号的冲突。

IP地址和端口号的组合得出一个网络套接字。

TCP,传输控制协议

先经过三次握手,建立连接,进行可靠传输;

在连接中可进行大数据量的传输;

传输完毕后,需释放连接,效率低

UDP,用户数据包协议

将数据、源、目的封装成包,不需要建立连接,是不可靠的

每个数据包的大小限制在64kb

无需释放资源,速度快

2、InetAddress类

InetAddress就代表IP地址,一个InetAddress对象对应一个IP地址,在java.net包下;

获得InetAddress对象,是通过其类自身的静态方法,getByName(String host),即通过域名进行获取,该方法要联网,进行DNS解析,以获取到IP地址;

InetAddress对象的getHostAddress()方法和getHostName()方法,可以得到IP地址 和 主机域名;

InetAddress的静态方法getLocalHost()方法,可以获取到本地主机对应的InetAddress对象。

@Test
public void testInetAddress() throws Exception{
//通过域名创建InetAddress类对象,创建过程中会进行网络DNS解析,获取到域名对应的IP地址
InetAddress inet = InetAddress.getByName("www.cqupt.edu.cn");
System.out.println(inet);//www.cqupt.edu.cn/219.153.62.66
System.out.println(inet.getHostAddress());//219.153.62.66
System.out.println(inet.getHostName());//www.cqupt.edu.cn

//获取本机的InetAddress对象
InetAddress inetLocal = InetAddress.getLocalHost();
System.out.println(inetLocal);//yuchen-PC/192.168.253.1
}

3、Socket编程

Socket是什么?套接字,是为了方便网络编程,构造的一个封装类,将网络通信封装成输入输出流的形式,进行数据的传输。

通信的两端,都必须有Socket,是两台机器间通信的端点;

网络编程就是Socket编程;

Socket允许程序把网络当成一个流,数据在两个Socket间通过IO传输;

Java 中有对应Socket的类,那就是java.net包下的Socket类;

TCP和UDP协议都可以通过Socket进行。

TCP通过Socket传输

客户端:四个步骤

1.创建Socket对象,通过构造方法,指明服务器端的IP地址(InetAddress类对象)、端口号

2.获取输出流,通过Socket对象的getOutputStream()方法

3.数据的输出过程(如果还要接收服务端返回的数据,先调用Socket对象的shutdownOutput()方法,显示告诉服务端已发送完毕,避免服务端read()方法的阻塞等待)

(如果需要接收服务端,获得输入流,读数据即可)

4.释放资源,对应的流以及Socket对象,必须要判断是否为null,否则可能引起空指针异常

//客户端
@Test
public void customer(){
Socket socket = null;
OutputStream outputStream = null;
try {
//1.创建一个Socket对象,通过构造器指明服务器的IP地址,以及接受的端口号
socket = new Socket(InetAddress.getByName("localhost"),9090);
//2.getOutputStream 发送数据,返回为OutputStream的实现类对象
outputStream = socket.getOutputStream();
//3.具体的输出过程
outputStream.write("我是客户端,请多关照!".getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally{
//4.关闭对应的流和Socket对象,必须要判断是否为null,否则可能引起空指针异常
if(outputStream!=null){
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}

if(socket!=null){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}


服务器端:五个步骤

1.创建ServerSocket对象,通过构造方法指明,该服务器端监听的端口号

2.通过ServerSocket对象的accept()方法,获得Socket对象(从服务器端获取到的Socket对象可以得到客户端相应的信息)

3.通过Socket对象的getInputStream()方法获取输入流;

4.对输入流进行操作

(如果需要回复客户端,获得输出流,进行写入操作即可)

5.释放资源,对应的流,以及ServerSocket对象、Socket对象,必须要判断是否为null,否则可能引起空指针异常

@Test
public void server(){
ServerSocket serverSocket = null;
Socket socket = null;
InputStream inputStream = null;
try{
//1.创建一个ServerSocket对象,通过构造器,指明监听的端口号
serverSocket = new ServerSocket(9090);
//2.调用accept()方法,返回一个Socket对象
socket = serverSocket.accept();
//3.调用getInputStream()方法,获取从客户端发送过来的输入流
inputStream = socket.getInputStream();
//4.对获取的流进行操作
byte[] bytes = new byte[20];
int len;
while((len = inputStream.read(bytes))!=-1){
String str = new String(bytes,0, len);
System.out.print(str);
}
//Socket可以获取到数据源的IP地址
System.out.println("收到来自于:"+socket.getInetAddress().getHostAddress());
} catch(Exception e){
e.printStackTrace();
} finally{
//5.关闭相应的流,以及Socket对象、ServerSocket对象
if(inputStream!=null){
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}

if(socket!=null){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}

if(serverSocket!=null){
try {
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}

}
}
}


先运行服务器端程序,处于等待状态;再运行客户端程序,通信完成;控制台显示出服务器端接收到的数据

例2:客户端发送数据到服务器端,服务器端将数据打印到控制台,同时返回“已收到信息”给客户端

关键是调用shutdownOutput()方法,显示告诉Socket的另一端,已发送完毕,避免对方read方法的阻塞。

public class TestTCP2 {

//客户端
@Test
public void customer() {

Socket s = null;
OutputStream os = null;
InputStream is = null;

try {
s = new Socket(InetAddress.getByName("localhost"),9090);

os = s.getOutputStream();
os.write("我是客户端,请多关照!".getBytes());
//执行此方法,显示的告诉服务器端,已发送完毕
s.shutdownOutput();

is = s.getInputStream();
byte[] b = new byte[20];
int len;
while((len = is.read(b))!=-1){
String str = new String(b,0,len);
System.out.print(str);
}
} catch (IOException e) {
e.printStackTrace();
} finally{
if(is!=null){
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}

if(os!=null){
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}

if(s!=null){
try {
s.close();
} catch (IOException e) {
e.printStackTrace();
}
}

}

}

//服务器端
@Test
public void server(){
ServerSocket ss = null;
Socket s= null;
InputStream is = null;
OutputStream os= null;

try {
ss = new ServerSocket(9090);
s = ss.accept();

is = s.getInputStream();
byte[] b = new byte[20];
int len;
while((len =is.read(b))!=-1){
String str = new String(b,0,len);
System.out.print(str);
}

os = s.getOutputStream();
os.write("已收到信息".getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally{
if(os!= null){
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(is != null){
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(s!= null){
try {
s.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(ss!=null){
try {
ss.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}


例3:客户端向服务端发送本地文件,服务端接收文件将文件保存到本地,并回复客户端“已收到文件信息”

注意,客户端文件的发送,其实就是将文件的读取与数据的发送结合,都是利用一段缓冲区,读一段,发一段;循环,直到发送完成;

服务端文件的接收,也是讲文件的写入与数据的接收结合,利用一段缓冲区,在输入流中读取一段,写入一段到文件;循环,直到接收完成。

文件流资源也要及时释放。

public class TestTCP3 {

@Test
public void client(){
Socket s = null;
FileInputStream fis = null;
OutputStream os = null;
InputStream is = null;

try {
//1.创建Socket对象
s = new Socket(InetAddress.getByName("localhost"), 9090);
//2.从本地获取文件,发送给服务端
os = s.getOutputStream();
fis = new FileInputStream(new File("1.jpg"));
byte[] b = new byte[1024];
int len;
while((len = fis.read(b))!=-1){
os.write(b,0,len);//注意write方法的参数用法
}
s.shutdownOutput();
//3.接收来自服务端的信息
is = s.getInputStream();
byte[] res = new byte[20];
int lenRes;
while((lenRes = is.read(res))!=-1){
String str = new String(res, 0 ,lenRes);
System.out.print(str);
}
} catch (Exception e) {
e.printStackTrace();
} finally{
//4.释放所有资源,对应流,及Socket对象
if(is!=null){
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(os!=null){
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(fis!=null){
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(s!=null){
try {
s.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

@Test
public void server(){
ServerSocket ss = null;
Socket s = null;
FileOutputStream fos = null;
InputStream is = null;
OutputStream os =null;

try {
//1.创建ServerSocket的对象
ss = new ServerSocket(9090);
//2.调用accept()方法,返回Socket对象
s = ss.accept();

//3.将接收到的文件,保存到本地
fos = new FileOutputStream(new File("2.jpg"));
is = s.getInputStream();
byte[] b = new byte[1024];
int len;
while((len = is.read(b))!=-1){
fos.write(b,0,len);
}
System.out.println("收到文件来自于"+s.getInetAddress().getHostAddress());
//4.接收成功的信息反馈给客户端
os = s.getOutputStream();
os.write("已收到图片信息".getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally{
//5.释放资源
if(os!=null){
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}

if(is!=null){
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(fos!=null){
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(s!=null){
try {
s.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(ss!=null){
try {
ss.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}

UDP通过Socket传输

UDP的传输,也是通过socket编程,java中的UDP使用的socket类是DatagramSocket,报文对应的是DatagramPacket类

发送端:步骤四步

1.创建DatagramSocket对象,无需指定信息,信息在报文对象中指定

2.创建DatagramPacket对象,通过构造方法指定,报文绑定的byte[] buf,InetAddresss地址,端口号

3.调用DatagramSocket对象的send(DatagramPacket pack)方法,发送报文

4.释放DatagramSocket对象的资源,报文对象无需释放(也不能释放)

接收端:步骤四步

1.创建DatagramSocket对象,指定监听的端口号

2.创建DatagramPacket对象,通过构造方法指定,报文绑定的byte[] buf,用以接收数据

3.调用DatagramSocket对象的receive(DatagramPacket pack)方法,接收报文

4.释放DatagramSocket对象的资源,报文对象无需释放(也不能释放)

//基于UDP协议,发送端发送数据,接收端接收到数据,并显示到控制台
public class TestUDP {
//发送端
@Test
public void send(){
DatagramSocket ds=null;
try {
//1.创建DatagramSocket对象,无需指明信息,信息在DatagramPacket中指明
ds = new DatagramSocket();
//2.创建一个数据报,大小不能大于64kb,记录着数据、发送端的IP、端口号等信息,以及接收端的IP、端口号
byte[] b = "你好,我是发送端".getBytes();
DatagramPacket pack = new DatagramPacket(b, 0, b.length,
InetAddress.getByName("localhost"), 9090);
//3.调用DatagramSocket的send()方法
ds.send(pack);
} catch (IOException e) {
e.printStackTrace();
} finally{
//4.关闭DatagramSocket对象资源
if(ds!=null){
ds.close();
}
}
}

@Test
public void receive(){
DatagramSocket ds = null;
try {
//1.创建DatagramSocket对象,指明监听的端口号
ds = new DatagramSocket(9090);
//2.创建DatagramPacket对象,用于接收收到的数据
byte[] buf = new byte[1024];
DatagramPacket pack = new DatagramPacket(buf, 0, buf.length);//缓冲区与报文对象是绑定的
//3.调用DatagramSocket的receive()方法接收
ds.receive(pack);
String str = new String(pack.getData(),0,pack.getLength());
System.out.println(str);
} catch (Exception e) {
e.printStackTrace();
} finally{
//4.释放DatagramSocket的资源
if(ds!=null){
ds.close();
}
}
}
}

UDP协议的特性,是不需要建立连接,故,在未开启接收端程序的情况下,开启发送端,也不会出现异常,不同于TCP

3、URL编程

URL是什么?Uniform Resource Location 统一资源定位符。是进行对网络上的资源进行标示查找的一种机制。

URL由五部分构成:<传输协议>://<主机名>:<端口号>/<文件名>

Java的java.net包中有类URL与网络资源统一资源定位符相对应。通过URL类,可以获取(“下载”)相应的网络资源文件到本地,必要时,也可以向网络发送信息。

通过URL对象可以获取到网络资源的信息,协议、主机名、端口号、文件名等

通过URL的openStream()的方法得到输入流,进行读取,就把网络资源文件下载到本地。

@Test
public void testURL(){
FileOutputStream fos =null;
try {
//URL Uniform Resource Location 统一资源定位符,标示网络上的一个资源文件
URL url = new URL("http://localhost:8080/web/login.html?a=b");

//获取资源文件的各种信息
System.out.println(url.getProtocol());//http
System.out.println(url.getHost());//localhost
System.out.println(url.getPort());//8080
System.out.println(url.getPath());// /web/login.html
System.out.println(url.getFile());// /web/login.html?a=b
System.out.println(url.getRef());// null
System.out.println(url.getQuery());// a=b

//将文件读取到本地,即下载,使用URL的openStream()方法获取输入流
fos = new FileOutputStream(new File("login.html"));
InputStream is = url.openStream();
byte[] b = new byte[1024];
int len;
while((len = is.read(b))!=-1){
fos.write(b, 0, len);
}

} catch (Exception e) {
e.printStackTrace();
} finally{
if(fos!=null){
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

}

改为 http://www.baidu.com/index.html可以把百度首页代码下载到本地
URL类的openConnection()方法可以得到URLConnection对象,URLConnection相当于应用与URL之间的一个连接。

URL对象只能单向读取网络资源到本地,不能向网络资源进行发送消息,

而URLConnection对象可以双向通信,既可以下载,也可以向网络发送信息。

URLConnection的getInputStream()方法可以得到输入流,从连接中读出数据,下载网络文件到本地;getOutputStream()方法,可以得到输出流,向连接中写入信息。

@Test
public void testURL2(){
FileOutputStream fos = null;
OutputStream os = null;
try {
//URL Uniform Resource Location 统一资源定位符,标示网络上的一个资源文件
URL url = new URL("http://localhost:8080/web/login.html?a=b");

//使用URLConnection不但可以读取网络数据,还可以向网络发送数据
//URLConnection代表了一个应用与URL之间的一个连接,可以进行双向通信
URLConnection urlConnection = url.openConnection();

//将文件读取到本地,使用URLConnection的getInputStream()方法获得输入流
fos = new FileOutputStream(new File("login2.html"));
InputStream is = urlConnection.getInputStream();
byte[] b = new byte[1024];
int len;
while((len = is.read(b))!=-1){
fos.write(b, 0, len);
}

//使用URLConnection的getOutputStream()方法获取输出流,向网络发送信息
os = urlConnection.getOutputStream();
os.write("网络资源,你好".getBytes());

} catch (Exception e) {
e.printStackTrace();
} finally{
if(fos!=null){
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}

if(os!=null){
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: