#JAVA中的网路编程
这篇文章,是关于JAVA网络编程扯蛋,如果蛋疼了呢?就继续往下看吧!
网络模型
关于java的网络编程,其实也没什么的,第一个先扯淡的是这个网络模型:OSI参考模型&TCP/IP参考模型,结构如下图所示:

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

用户在这个应用层混,我们在这个传输层和网际层混。那么,用户在操作数据时,是怎么样的过程呢?
其实呀,数据先是从用户混的应用层向下封装打包,再经过网络传输到接收端,接收端自下而上拆包,最后得到用户发送的数据。
大概是这样一个模式:A——->B——->B——–>A
上图解释,大家掌声欢迎!

网络通信的三要素
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.sunofbeach.net,哈哈!
还有就是特殊的IP地址啦:本地的IP地址,或者说是本地的回环地址:127.0.0.1,主机名是:localHost
你也可以打开这个DOC命令行,输入ipconfig查看网络配置。
知道了IP地址,那在JAVA中如何获取IP地址呢?其实很简单,在JAVA中IP地址对应的是:InetAddress类,在java.net包中放着,需要用的话,就不用客气地把它导入。
InetAddress这个类不有构造方法,所以呢,不能实例化,但可以通过它本身的方法获取到对象,还记得单例设计模式也是这样吗?
好,看看主要的方法:

由上面的表子可以看出,这个要获取到IP地址,首先要得到这个InetAddress,然后,通过这个InetAddress对象的getHostAddress方法,就可以获取到这个IP地址。
有些东西要另外说一下,这样方便记忆和理解:
get:获得, Host:主机,Address:地址,也就是获取主机地址的意思,这样你永远记得这个是获取主机IP地址啦,是吧!
那么,如何获取这个InetAddress对象呢?从上面的表子也可以看出有好几个静态的方法呢!这里的前提是我们不知道这个IP地址,要获取的是IP地址,所以我们用这个方法来获取InetAddress对象吧,然后通过getHostAddress()方法来获取IP地址!

代码体现如下:
//导包
import java.net.*;
public class Demo{
	public static void main(String[] args)throws Exception{
		
		//获取InetAddress对象
		InetAddress ia = InetAddress.getByName("www.sunofbeach.net");
		//然后调用InetAddress对象中的getHostAddress()方法获得IP
		String ip = ia.getHostAddress();
		//输出到控制台上看看结果吧:
		System.out.println("www.sunofbeach.net的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传输协议的区别,下面分别说说哈!

好啦,有了上面两个表子,大家可以对比啦,面试的时候也要会吹牛啦!其实呀,面试官录取你,或者说给你打高分并不是因为你的能力有多好,而是因为你能让他心情愉快,感觉良好就可以了!这是心理学家进行大量实验得出的结果。
对了,上面的三次握手解释一下吧:
Client说:服务端哥,我想联你,在吗?—————>请求链接
Server说:客户端弟,我在的,你联过来吧!———->服务端确认链接
Client说:那好,我真的联上来啦!——————–>客户端确认链接
计算机与计算机之间的通信步骤:
1、通过IP地址找到主机;2、通过端口找到接收的程序;3、通过协议发送数据。
PS:凌晨三点四十分了,电脑没早了,嘻嘻!下次再写吧!晚安!
传输协议:UDP
好啦,我又回来了!继续吧,哈哈!
先说这个UDP,TCP后面说。复习一下吧,UDP,不面向链接,不可靠,速度快,一般用于聊天,视频,通话之类的一次性数据传输,不需要精确数据或者永久数据的情况下使用。
在说这两个传输协议之前,要提到的是Socket,Socket大概是插座的意思,那么这里呢可以理解为网线插口,也就是一个端点。很多资料上也跟语文老师那样形象生动地比喻成港口码头。
现在有两台主机,之间要传输数据,所以呢,首先要有码头,那我们看看如何创建码头。
UDP的码头是DatagramSocket,它可以接受和发送数据“包”。一般发送的话要指定IP地址,还有端口,而接受端可以不指定IP,但要监视端口。下面是DatagramSocket的常用构造方法:
DatagramSocket() 构造数据报套接字并将其绑定到本地主机上任何可用的端口。 DatagramSocket(int port) 创建数据报套接字并将其绑定到本地主机上的指定端口。 DatagramSocket(int port, InetAddress laddr) 创建数据报套接字,将其绑定到指定的本地地址。
第一个一般用于接收端,第二个用于发送端。
方法有那些常用的呢:

主要用到的还是接收和发送,另外就是获取IP地址。
有了这些,我们还需要一个类DatagramPacket,这个类呢,是用于把数据打包的。常用的构造方法列一下:

看起来又长又臭,其实写多几次就很熟悉了,闭着眼睛你都能写出来。
有了码头,有了数据包,接下来就是形象生动地说说步骤:
发送端的操作:
1、建立Socket服务,随便你指不指定端口,不指定系统分配。另外就是端口顺延,如果这次端口是1555,下次就是1556,因为当你运行完程序后,不能保证端口也释放了,为了防止出错,所以就这样顺延下去咯!
2、将要发送的数据封装到包中,准备发送,嘻嘻!
3、当然是发送数据啦,嘻嘻。通过send方法发送出去!管它有没有收到呢!
4、最后,不要忘记关闭资源!

实干兴邦,空谈误国,代码体现一下吧,这个是发送端的哦:
//发送端
//导包
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!

// 导包
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传输协议。
传输协议:TCP
TCP第一步也是创建服务,但是这服务稍微有点不同。客户端的服务是Sokcet,服务端的服务是ServerSocket。无论是UDP,还是TCP,只要多写几次,就熟悉得不得了了,真的。有一套路的,就这死板的格式。
老规矩,先看看Socket的常用的构造方法和普通方法吧

客户端的步骤:
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其他场景的应用了,比如说,上传图片,并发上传图片,还要追求高效,这可以采用缓冲技术。所以,接下来就搞几个应用看看哈!
网络传输应用
下次再写吧,嘻嘻,累了!睡觉先!早安!
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());
					}
			}
		}
	}
}
到此,剧终!感谢各位看官,喜欢就分享,喜欢就点赞吧!



 拉大锯  回复 @Android
 拉大锯  回复 @Android 



























