#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());
}
}
}
}
}
到此,剧终!感谢各位看官,喜欢就分享,喜欢就点赞吧!