JAVA 中的网路编程

2,060 阅读16分钟

1.JAVA中的网路编程

这篇文章,是关于JAVA网络编程扯蛋,如果蛋疼了呢?就继续往下看吧!

2.网络模型


关于java的网络编程,其实也没什么的,第一个先扯淡的是这个网络模型:OSI参考模型&TCP/IP参考模型,结构如下图所示:

ositcpip%e5%8f%82%e8%80%83%e6%a8%a1%e5%9e%8b

通常来讲,这个做开发的在这个传输层和网际层混,而应用层则是FTP/HTTP协议等,传输层就是这个TCP或者这个UDP啦,这个经常问到,面试的时候,说什么TCP和UDP的区别?不过你看完这篇文章,基本上可以扯淡出来了,嘻嘻!还有网际层就是这个IP啦,最后一个是硬件,网卡之类的。

%e5%9b%be%e7%89%871

用户在这个应用层混,我们在这个传输层和网际层混。那么,用户在操作数据时,是怎么样的过程呢?

其实呀,数据先是从用户混的应用层向下封装打包,再经过网络传输到接收端,接收端自下而上拆包,最后得到用户发送的数据。

大概是这样一个模式:A——->B——->B——–>A

上图解释,大家掌声欢迎!

%e5%9b%be%e7%89%872

3.网络通信的三要素:IP地址,端口,还有传输协议

首先是IP地址,这个嘛,其实大家都听得多啦!其实它是一个网络设备标识,就像你家的地址一样,只是,它用数字表示,或者说像门号一样。假如把酒店看成网络,那么每间房门号就是一个IP地址啦。看看它的英文吧,Internet Protocol,也就是网络协议的意思,嘻嘻!理解就好,不解释太多,要不更糊涂了,相信大家都知道IP是什么的。

说到IP,还要说到域名。因为IP纯数字,不好记忆,那么就用域名与之对应,这个呢DNS服务器解决,域名与IP地址是映射关系。比如说,我们可以通过百度的IP地址来找到百度的网页:202.108.22.5 ,也可以通过www.baidu.com来找到同样的页面,这里百度公司就是通过DNS服务器把域名www.baidu.com解析到202.108.22.5,所以他们存在映射关系,这样的话,是不是网民记www.baidu.com比记202.108.22.5这个好多啦!下面请看一下广告:本网站的域名是www.sunofbeaches.com,哈哈!

还有就是特殊的IP地址啦:本地的IP地址,或者说是本地的回环地址:127.0.0.1,主机名是:localHost

你也可以打开这个DOC命令行,输入ipconfig查看网络配置。

知道了IP地址,那在JAVA中如何获取IP地址呢?其实很简单,在JAVA中IP地址对应的是:InetAddress类,在java.net包中放着,需要用的话,就不用客气地把它导入。

InetAddress这个类不有构造方法,所以呢,不能实例化,但可以通过它本身的方法获取到对象,还记得单例设计模式也是这样吗?

好,看看主要的方法:

%e5%9b%be%e7%89%873

由上面的表子可以看出,这个要获取到IP地址,首先要得到这个InetAddress,然后,通过这个InetAddress对象的getHostAddress方法,就可以获取到这个IP地址。

有些东西要另外说一下,这样方便记忆和理解:

get:获得, Host:主机,Address:地址,也就是获取主机地址的意思,这样你永远记得这个是获取主机IP地址啦,是吧!

那么,如何获取这个InetAddress对象呢?从上面的表子也可以看出有好几个静态的方法呢!这里的前提是我们不知道这个IP地址,要获取的是IP地址,所以我们用这个方法来获取InetAddress对象吧,然后通过getHostAddress()方法来获取IP地址!

%e5%9b%be%e7%89%874

代码体现如下:

//导包
import java.net.*;

public class Demo{
	public static void main(String[] args)throws Exception{
		
		//获取InetAddress对象
		InetAddress ia = InetAddress.getByName("www.sunofbeaches.com");
		//然后调用InetAddress对象中的getHostAddress()方法获得IP
		String ip = ia.getHostAddress();
		//输出到控制台上看看结果吧:
		System.out.println("www.sunofbeaches.com的IP地址是:"+ip);
	}
}

再来一个获取本地的IP地址试试吧,嘻嘻!

//导包
import java.net.*;

public class Demo{
	public static void main(String[] args)throws Exception{
		
		//获取InetAddress对象
		InetAddress ia = InetAddress.getLocalHost();
		
		//获取IP地址
		String ip = ia.getHostAddress();
		//在实际开发中,一般简写为:
                //InetAddress ia = InetAddress.getLocalHost().getHostAddress();
		
		System.out.println("本机IP地址为:"+ip);
	}
}

 

OK,搞定完IP之后,就到这个端口吧!

我们通过IP找到了主机,对吧!但是,但是这个主机上有很多的软件吧,他们都在运行,当我们发送一个数据到主机上,那这个数据到底是给那个软件的呢,这样就出现问题了!解决这个问题,当然是用端口啦,这个端口呢,并不是物理端口,所谓的物理端口就是计算机硬件上的插口。这里的端口是逻辑端口,关于端口的几点说明如下:

1、端口的范围从0~65535(2的16次方减1)

2、系统保留端口为:0~1024

3、一些常用的端口,FTP:21,SMTP:25,HTTP:80,嘻嘻还有好些呢,这些不需要记的,了解一下,吹牛扯淡用得上!

好吧,端口到这里就完事啦,最后一个是:协议!

顾名思义,协议就是一起商量好,共同来往的相关规定,或者说是传输规则。传输协议,UDP和TCP协议。

重点来啦,面试常问哦!UDP和TCP传输协议的区别,下面分别说说哈!


名称:UDP协议
说明:是面向无链接,但明确对方主机端口的协议,很自私,它只管传输,不管死活,不管有没有接收到。一顿狂发,速度快,这种协议用在视频通话,聊天,不需要长久保存数据或者精确获取数据的情况下使用。
是否面向链接:否,管它在不在呢,一顿狂发送
数据包大小: 每一个数据包要在64K之内
是否可靠:不可靠,千万别靠
特点:不需要建立链接,速度快极了

 

名称:TCP协议
说明:是面向链接的协议,要建立链接后再进行传输数据,常用于上传下载
是否面向链接:是,要建立链接,三次握手机制,很友好,是吧,哈哈!
数据包大小:建立链接后进行大数据传输
是否可靠:可靠协议,三次握手的保证!
特别:一定要建立链接,所以呢,效率稍微有点不快!

好啦,有了上面两个表子,大家可以对比啦,面试的时候也要会吹牛啦!其实呀,面试官录取你,或者说给你打高分并不是因为你的能力有多好,而是因为你能让他心情愉快,感觉良好就可以了!这是心理学家进行大量实验得出的结果。

对了,上面的三次握手解释一下吧:

Client说:服务端哥,我想联你,在吗?—————>请求链接

Server说:客户端弟,我在的,你联过来吧!———->服务端确认链接

Client说:那好,我真的联上来啦!——————–>客户端确认链接

 

计算机与计算机之间的通信步骤:

1、通过IP地址找到主机;2、通过端口找到接收的程序;3、通过协议发送数据。

PS:凌晨三点四十分了,电脑没早了,嘻嘻!下次再写吧!晚安!

4.传输协议:UDP

好啦,我又回来了!继续吧,哈哈!

先说这个UDP,TCP后面说。复习一下吧,UDP,不面向链接,不可靠,速度快,一般用于聊天,视频,通话之类的一次性数据传输,不需要精确数据或者永久数据的情况下使用。

在说这两个传输协议之前,要提到的是Socket,Socket大概是插座的意思,那么这里呢可以理解为网线插口,也就是一个端点。很多资料上也跟语文老师那样形象生动地比喻成港口码头。

现在有两台主机,之间要传输数据,所以呢,首先要有码头,那我们看看如何创建码头。

UDP的码头是DatagramSocket,它可以接受和发送数据“包”。一般发送的话要指定IP地址,还有端口,而接受端可以不指定IP,但要监视端口。下面是DatagramSocket的常用构造方法:

DatagramSocket()            构造数据报套接字并将其绑定到本地主机上任何可用的端口。
DatagramSocket(int port)            创建数据报套接字并将其绑定到本地主机上的指定端口。
DatagramSocket(int port, InetAddress laddr) 创建数据报套接字,将其绑定到指定的本地地址。

第一个一般用于接收端,第二个用于发送端。

方法有那些常用的呢:

int getPort() 返回此套接字的端口。
 void receive(DatagramPacket p) 从此套接字接收数据报包。
 void send(DatagramPacket p) 从此套接字发送数据报包。
 InetAddress getInetAddress() 返回此套接字连接的地址。
 InetAddress getLocalAddress() 获取套接字绑定的本地地址。
 int getLocalPort() 返回此套接字绑定的本地主机上的端口号。

主要用到的还是接收和发送,另外就是获取IP地址。

有了这些,我们还需要一个类DatagramPacket,这个类呢,是用于把数据打包的。常用的构造方法列一下:

DatagramPacket(byte[] buf, int length) 构造 DatagramPacket,用来接收长度为 length 的数据包。
DatagramPacket(byte[] buf, int length, InetAddress address, int port) 构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号。

看起来又长又臭,其实写多几次就很熟悉了,闭着眼睛你都能写出来。

有了码头,有了数据包,接下来就是形象生动地说说步骤:

发送端的操作:

1、建立Socket服务,随便你指不指定端口,不指定系统分配。另外就是端口顺延,如果这次端口是1555,下次就是1556,因为当你运行完程序后,不能保证端口也释放了,为了防止出错,所以就这样顺延下去咯!

2、将要发送的数据封装到包中,准备发送,嘻嘻!

3、当然是发送数据啦,嘻嘻。通过send方法发送出去!管它有没有收到呢!

4、最后,不要忘记关闭资源!

%e5%9b%be%e7%89%8721

实干兴邦,空谈误国,代码体现一下吧,这个是发送端的哦:

//发送端

//导包
import java.net.*;

public class Demo{
	public static void main(String[] args)throws Exception{
		
		//创建Socket服务
		DatagramSocket ds = new DatagramSocket();
                //不指定端口也行,稍后在数据里指定。
		
		//把数据打包
		String data = "阳光沙滩:sunofbeaches.com";
		byte[] buf = data.getBytes();
                //把数据转换成字节数组
		DatagramPacket dp = new DatagramPacket(buf,
                                  buf.length,
                                  InetAddress.getByName("PresidentsPC"),
                                  10000);//通过名字获取IP,并指定端口
		
		//发送数据
		ds.send(dp);
		//关闭资源
		ds.close();
	}
}

 

接受端这边的步骤又是怎么样的呢?差不了多少!

1、建立服务

2、定义一个缓冲区来接受数据

3、调用DatagramPacket中的各种方法来达到我们的需求

4、关闭资源

好,太棒了,接下来还需要接收端的代码:

//接收端

//导包
import java.net.*;

public class Server{
	public static void main(String[] args)throws Exception{
		
		//建立服务监视这个端口
		DatagramSocket ds = new DatagramSocket(10000);
		
		//定义一个缓冲区用于接收数据
		byte[] buf = new byte[1024];//可以乘以64,因为一个包在64K以内。
		DatagramPacket dp = new DatagramPacket(buf,buf.length);
		ds.receive(dp);
		
		//获取IP,这是一个习惯,因为要知道这数据是从那里来的
		String ip = dp.getAddress().getHostAddress();
		System.out.println("来自"+ip+"的数据是");
		
		//把数据弄出来吧
		System.out.println(new String(buf,0,dp.getLength()));
		
		//关闭资源
		ds.close();		
	}
}

 

爽吧,这下叼了。来几个例子吧,这些例子都是很经典的。第一个是发送从键盘录入的字符,有了这个基础之后,我们还可以做一个模仿聊天器的程序。

先是发送键盘录入的程序,下面分析一下吧:

1、首先,要建立服务

2、获取资源,从控制台中获取,这个可以用高效的方法BufferedReader,读一行,爽到爆了。

3、把资源打成包发送出去

4、关闭资源。

PS,这次我们进行异常处理,完整地写出代码吧,OK!当然OK,嘻嘻!

第一个是发送端(我们叫客户端吧!(^_^))

//客户端,用来发送从键盘录入的资源

//导包
import java.net.*;
import java.io.*;

public class Demo{
	public static void main(String[] args){
		
		//建立服务
		DatagramSocket ds = null;
		
		//获取输入字符
		BufferedReader br = null;
		try{
		if(ds==null)
			ds =  new DatagramSocket();
		if(br==null)
			br = new BufferedReader(new InputStreamReader(System.in));//获取键盘录入信息
			
			//把读取的资源打包
			byte[] buf = br.readLine().getBytes();//合在一起写啦。
			
			DatagramPacket dp = 
                        new DatagramPacket(buf,buf.length,
                        InetAddress.getByName("PresidentsPC"),
                        10004);// 打包数据并指定IP和端口
			
			//发送出去
			ds.send(dp);
		}catch(Exception e){
			System.out.println("数据发送失败");
		}finally{
			//关闭资源
			
			ds.close();
			if(br!=null)
			try{
				br.close();
			}catch(Exception e){
				System.out.println("流关不了,坏了!");
			}
		}
	}
}

接下来是接收端(我们叫服务端吧,高端大气上档次)

//接收端

//导包
import java.net.*;

public class Server{
	public static void main(String[] args){
		
		//创建服务
		DatagramSocket ds = null;
		DatagramPacket dp = null;
		try{
			if(ds==null)
				ds = new DatagramSocket(10004);
			//创建缓冲区
			byte[] buf  = new byte[1024];
			
			//接收数据
			if(dp==null)
				dp = new DatagramPacket(buf,buf.length);
			ds.receive(dp);
			
			String ip  = dp.getAddress().getHostName();
			
			//输出数据到控制台上
			System.out.println(ip+" :"+new String(buf,
                                     0,
                                     dp.getLength()));
			//关闭资源
		
		}catch(Exception e){
			System.out.println("哎呀,妈呀,出问题了!");
		}finally{
			ds.close();
		}		
	}
}

结果是可以的,不过,只不过,呵呵,太不爽了,才玩了一次,接下来,我们要写一个多线程的,不断地接收数据,OK!当然OK!

%e6%88%aa%e5%9b%be01

// 导包
import java.io.*;
import java.net.*;

public class ChatDemo {

	public static void main(String[] args) {
		
		//创建读取线程
		new Thread(new ChatThread()).start();

		// 创建服务
		DatagramSocket ds = null;
		BufferedReader br = null;
		try {
			if (ds == null)
				ds = new DatagramSocket();
			// 读取键盘录入
			if (br == null)
				br = 
                       new BufferedReader(new InputStreamReader(System.in));
			// 读取数据
			String line = null;
			while ((line = br.readLine()) != null) {
				// 发送数据
				byte[] buf = line.getBytes();
				DatagramPacket dp = new DatagramPacket(buf, buf.length, InetAddress.getByName("PresidentsPC"), 10000);
				ds.send(dp);
			}
		} catch (Exception e) {
			System.out.println(e.toString());
		} finally {

			ds.close();
			if (br != null)
				try {
					br.close();
				} catch (Exception e) {
					System.out.println(e.toString());
				}
		}
	}
}

//搞一个线程来接受数据
class ChatThread implements Runnable {
	
	public void run(){
		
		while(true){
			DatagramSocket ds = null;
			try {
	                      ds = new DatagramSocket(10000);//监视10000端口
	                        //获取数据
	                        byte[] buf = new byte[1024];
	                        
	                        DatagramPacket dp = new DatagramPacket(buf,buf.length);
	                        ds.receive(dp);                       
	                        
	                        //获取IP
	                        String ip = dp.getAddress().getHostAddress();
	                        
	                        //输出数据
	                        System.out.println(ip+" :"+new String(buf,0,dp.getLength()));
	                   
	                        
                        } catch (Exception e) {
                        	System.out.println(e.toString());
                        }finally{
                        	ds.close();
                        }
		}
	}
}

如果可以看得懂上面这个程序,并能独立写出来的话,那么UDP传输协议,基本上搞定啦,那么接下来我们继续学习TCP传输协议。

5.传输协议:TCP

TCP第一步也是创建服务,但是这服务稍微有点不同。客户端的服务是Sokcet,服务端的服务是ServerSocket。无论是UDP,还是TCP,只要多写几次,就熟悉得不得了了,真的。有一套路的,就这死板的格式。

老规矩,先看看Socket的常用的构造方法和普通方法吧:

Socket(InetAddress address, int port) 创建一个流套接字并将其连接到指定 IP 地址的指定端口号。

Socket(String host, int port) 创建一个流套接字并将其连接到指定主机上的指定端口号。

 void close() 关闭此套接字。
 InetAddress getInetAddress() 返回套接字连接的地址。
 InputStream getInputStream() 返回此套接字的输入流。
 OutputStream getOutputStream() 返回此套接字的输出流。
 int getPort() 返回此套接字连接到的远程端口。
 void shutdownInput() 此套接字的输入流置于“流的末尾”。
 void shutdownOutput() 禁用此套接字的输出流。

客户端的步骤:

1、建立服务Socket

2、获取输出流,把数据变成字节数组, 通过输出流发送给服务端。

3、关闭输出流,获取输入流,获取反馈信息

4、关闭资源

代码体现:

//导包
import java.io.*;
import java.net.*;

public class Client {

	public static void main(String[] args) {

		// 创建服务
		Socket s = null;
		try {
			if (s == null)
				s = new Socket("PresidentsPC", 13000);
			//把数据转换成字节数组
			byte[] buf = "阳光沙滩".getBytes();
			
			//获取输出流
			OutputStream out = s.getOutputStream();
			//发送数据
			out.write(buf);
			
			//关闭发送流
			s.shutdownOutput();
			
			//获取输入流,获取反馈信息
			InputStream in = s.getInputStream();
			byte[] buffer  = new byte[1024];
			int len = in.read(buffer);
			
			//打印反馈信息
			System.out.println(new String(buffer,0,len));
		} catch (Exception e) {
			System.out.println(e.toString());
		}finally{
			if(s!=null){
				try{
					s.close();
				}catch(Exception e)
				{
					System.out.println(e.toString());}
				}
		
		}

	}
}

 

服务端又如何呢,其实还是大同小异的,要是IO知识掌握了,一点问题都没有,闭上眼睛就来了!

步骤如下

1、建立服务ServerSocket服务,然后。用ServerSocket的accept()方法得到Socket服务

2、获取输入流,然后可以得到数据

3、对读到的数据进行处理,该干嘛干嘛去

4、反馈信息给客户端

5、各种关闭资源

如下:

//导包
import java.io.*;
import java.net.*;

public class Server {

	public static void main(String[] args)throws Exception {

		//创建服务
		ServerSocket ss = new ServerSocket(13000);
		
		Socket s = ss.accept();
		//先搞到IP地址
		String ip = s.getInetAddress().getHostAddress();
		
		//输出链接上来的机器
		System.out.println(ip+"-----connected");
		
		//获取输入流
		InputStream in  = s.getInputStream();
		//读取数据
		byte[] buf = new byte[1024];
		
		int len = 0;
		while((len = in.read(buf))!=-1){
			//打印到控制台上吧
			System.out.println(new String(buf,0,len));
		}
		s.shutdownInput();
		
		//发送反馈信息
		
		OutputStream out = s.getOutputStream();
		
		out.write("服务端收到啦".getBytes());
		
		//关闭资源
		s.close();
		ss.close();
	}

}

 

把这个例子也搞定了,就可以更深入发学习TCP其他场景的应用了,比如说,上传图片,并发上传图片,还要追求高效,这可以采用缓冲技术。所以,接下来就搞几个应用看看哈!

6.网络传输应用

 

下次再写吧,嘻嘻,累了!睡觉先!早安!

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

class ServerForAll {

	public static void main(String[] args) throws Exception {

		// 创建服务
		ServerSocket ss = new ServerSocket(19000);
		// 创建线程
		while (true) {
			Socket s = ss.accept();
			new Thread(new ServerThread(s)).start();
		}

	}

}

class ServerThread implements Runnable {
	// 持有s的引用
	private Socket s;

	public ServerThread(Socket s) {
		this.s = s;
	}

	public void run() {

		// 获取流
		InputStream in = null;
		OutputStream out = null;
		// 把上传的文件直接入到目录下
		OutputStream outPutFile = null;
		String ip = s.getInetAddress().getHostAddress();
		System.out.println(ip+"---connected");
		
		int count = 0;
		try {
			in = s.getInputStream();
			out = s.getOutputStream();
			File file = new File("Data.jpg");
			if (file.exists())
				file = new File("Data" + "(" + (count++) + ")");
			outPutFile = new FileOutputStream(file);

			// 读取文件
			byte[] buf = new byte[1024];
			int len = 0;
			while ((len = in.read(buf)) != -1) {
				// 写数据
				outPutFile.write(buf, 0, len);
			}
			// 反馈信息
			out.write("上传成功!".getBytes());

			s.close();
			outPutFile.close();
		} catch (Exception e) {
			System.out.println(e.toString());
		}

	}
}




class Client {

	public static void main(String[] args) {
		// 把路径直接用参数传入
		if (args.length == 0) {
			System.out.println("请传入jpg图片的路径参数");
			return;
		}
		if (!args[0].endsWith(".jpg")) {
			System.out.println("图片格式不正确");
			return;
		}

		File file = new File(args[0]);
		if (!file.exists()) {
			System.out.println("文件不存在");
			return;
		}
		// 创建服务
		Socket s = null;
		InputStream in = null;
		try {
			s = new Socket("PresidentsPC", 19000);
			in = new FileInputStream(file);
			// 获取输出流
			OutputStream out = s.getOutputStream();
			byte[] buf = new byte[1024];
			int len = 0;
			while ((len = in.read(buf)) != -1) {
				// 发送数据
				out.write(buf, 0, len);
			}
			// 关闭发送流
			s.shutdownOutput();

			// 获取输入流,获取反馈信息
			InputStream input = s.getInputStream();
			byte[] buffer = new byte[1024];
			int length = input.read(buffer);

			// 打印反馈信息
			System.out.println(new String(buffer, 0, length));
		} catch (Exception e) {
			System.out.println(e.toString());
		} finally {
			if (s != null) {
				try {
					s.close();
				} catch (Exception e) {
					System.out.println(e.toString());
				}
				if (in != null)
					try {
						in.close();
					} catch (Exception e) {
						System.out.println(e.toString());
					}
			}

		}

	}
}

到此,剧终!感谢各位看官,喜欢就分享,喜欢就点赞吧!