(8) - 网络编程 (图)

---------------------- ASP.Net+Android+IO开发S.Net培训、期待与您交流! ----------------------


1、 概述

网络通讯的要素:

IP地址:互联网上的每一台计算机都有一个唯一标示自己的标记,这个标记就是IP地址。网络通讯就是通过互联网上的IP地址进行通信。

端口:其实就是一个计算机为应用程序标记的标识号码,便于网络通信寻找对应的网络程序进行通信。它是一个逻辑端口,有效端口:0 ~ 65535,系统使用或保留的端口是:0 ~ 1024.。

传输协议:通信的规则,只有通信两端遵守这个通信规则方能通信,这里我们学习传输协议UDP和TCP。

 

通信的步骤:

(1)找到IP地址

(2)数据要发送到对象指定应用程序,为标识这些应用程序,所以给这些网络应用程序都用数字标识,为方便称呼这个数字,叫做端口,即逻辑端口。

(3)定义通信规则,称之为协议。国际组织定义了通用协议,即TCP/IP。

 

2、 网络模型

描述了网络的整个传输的层次,从应用层一层一层的封包,通过最底层的链路层进行物理线路的电信号传输,达到目的地后,再从目标的底层链路层,一层层解包,将数据提交给应用层。如下为OSI的参考模型及TCP/IP的参考模型。


3、 InetAddress

IP地址使用32位长度的二进制数据表示,为便于记忆,IP都是以十进制数据表示形式,如192.168.1.3。

在浏览网站时,为进一步记忆众多网站地址,用含意义的字符串替代十进制表示的IP地址,如www.baidu.com,他们都与IP地址存在映射关系。

在java中由IneAddress类来操作IP的相关功能。

常用方法:

byte[]getAddress() //返回此InetAddress 对象的原始 IP 地址
static InetAddress getByName(String host)//在给定主机名的情况下确定主机的 IP 地址
String getHostAddress() //返回 IP 地址字符串(以文本表现形式)。
static InetAddress getLocalHost() //返回本地主机。

4、 UDP传输协议

一种面向无连接的传输协议,即不需要知道发送端与接收端是否连接,发送端该发数据时就发,数据以数据报的形式传输。数据可能丢失,只求速度,多用于通讯信息不重要但要求实时性的聊天程序和视频会议等。

特点:

(1)   面向无连接,不需建立连接就发送。

(2)   因不确定连接,是不可靠的协议。

(3)   也因不需确定连接,通讯速度快。

(4)   每个数据报的打下限制在64k内。

 

UDP传输在Java中靠DatagramSocket与DatagramPacket类支持:

DatagramSocket(数据报套接字):

用来发送和接收数据报的套接字。在发送端创建DatagramSocket对象时可不指定端口,它用send方法发送数据报DatagramPacket对象,在接收端创建DatagramSocket时需指定端口,它用receive接收数据报对象。

 

DatagramPacket(数据报包):

数据报包用来实现无连接包投递服务。每条报文根据该包中包含的信息从一台机器由路由到另一台机器。从一台机器发送到另一台机器的多个包可能选择不同的路由,也可能按不同的顺序到达。不对包投递做出保证。在构建时需传入byte数组,作为数据包,或者指定长度和ip地址。可由getAddress().getHostAddress()获取对方IP地址。

下面看一个简单的聊天程序,考虑到一端可以进行收发数据,采用多线程技术:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
/*
多线程实现聊天程序,交替发送端和接收端的端口号能进行同台机子测试
需开两个程序,开一个,交替端口号再开一个。
即10002---》10001,10001-----》10002在端口间测试
*/
public class UDP_Chat
{
public static void main(String[] args)
{
        try
        {
               //如果想在同一台机子测试,请将发送和接收端口设置成不同端口号,即10001--->发送---->10002
               //先执行已个程序,而后交换下面代码的端口号,再执行一个,就可实现同一机子测试
               int sendPort = 10001;
               int receivePort = 10002;
               //建立发送端和接收端
               DatagramSocket sendSocket = new DatagramSocket();
               DatagramSocket receiveSocket =new DatagramSocket(receivePort);
              
               //分别开线程
               new Thread(new Send(sendSocket,sendPort)).start();
               new Thread(new Receive(receiveSocket)).start();
              
        } catch (SocketException e)
        {
               throw new RuntimeException("Socket创建失败!");
        }
}
}
//发送端
class Send implements Runnable
{
private DatagramSocket ds;
private int sendPort=10001;
public Send(DatagramSocket ds, int  sendPort)
{
        this.sendPort = sendPort;
        this.ds = ds;
}
public void run()
{
        try
        {
               //获取键盘录入
        BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
        String strLine = null;
        while((strLine = bf.readLine())!=null)
        {
               //读到over停止聊天
               if("over".equals(strLine))
                      break;
              
               byte[] bys = strLine.getBytes();
              
               //数据报对象,构造需传入byte数组+ip+端口号
               DatagramPacket dp = new DatagramPacket(bys, bys.length, InetAddress.getByName("127.0.0.1"),sendPort);
               //发送数据
               ds.send(dp);
        }
        ds.close();
        }catch(IOException e)
        {
               throw new RuntimeException("数据发送失败!");
        }
}
}
//接收端
class Receive implements Runnable
{
private DatagramSocket ds = null;
public Receive(DatagramSocket ds)
{
        this.ds = ds;
}
public void run()
{
        //无限接收外来数据
        while(true)
        {
               //准备一个byte数组接收数据
               byte[] bys = new byte[1024];
               DatagramPacket dp = new DatagramPacket(bys, bys.length);
               try
               {
                      //接收
                      ds.receive(dp);
                      //打印到控制台
                      System.out.println(new String(dp.getData(), 0, dp.getLength()));
               } catch (IOException e)
               {
                      throw new RuntimeException("数据接收失败!");
               }
        }
}
}


5、 TCP传输协议

面向连接的网络传输协议,分服务端和客户端,想要发送数据,两者需相互确认连接成功,方可发送数据。这就是需要经过所谓的”三次握手”,这样保证了数据的可靠性,多用于文件的传输,下载等数据要求严格的应用。

特点:

(1)   面向连接,需经“三次握手”进行连接确认。

(2)   是可靠的协议,只在连接成功的情况下发送数据,不易丢失数据。

(3)   由于需建立连接,所以速度稍慢。

(4)   这里通过客户端的IO流操作数据。

 

Java的TCP协议由ServerSocket与Socket类提供支持,两者是相当于服务端与客户端,分别封装了服务端和客户端的基本操作。

 

在传输数据量较大的文件,文件的结束标志并不能被传输,它只是告诉了本类的读取流,一旦读取完毕即读到文件结束标志,将结束发送,服务端却无法确认文件是否读完,所以客户端读完后,需调用shutdownOutput();告知服务端结束传输。

 

ServerSocket:

服务端的套接字,在TCP传输中该端的程序需先启动,它将获取端口号,监听向该端口发送连接请求的客户端套接字对象。通过accept()返回客户端的对象。getInetAddress().getHostAddress()的方式获取客户端的IP。在整个传输中只扮演着控制者或说服务者。

Socket:

客户端的封装类,它是两台机器的通信端,扮演着通信者,构建时传入服务器端的IP地址和通信端口号。由getOutputStream()和getInputStream()返回字节流的对象,转成字节流来进行数据的操作。而后利用字节流的read和write方法对byte数据进行传输。

下面看一个TCP传输协议的例子:

服务端:在这种面向连接的传输协议中,要求服务端必须先开启

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
 
public class TCPServer
{
public static void main(String[] args) throws IOException
{
        ServerSocket ss = new ServerSocket(10001);
        //连接客户端对象
        Socket s = ss.accept();
       
        //接收客户端数据
        InputStream in = s.getInputStream();
        byte[] bys = new byte[1024];
        //读入数组中
        int len = in.read(bys);
       
        System.out.println(new String(bys, 0,len));
       
        //回馈数据给客户端
        OutputStream out = s.getOutputStream();
        out.write("已收到".getBytes());
       
        //关闭资源
        s.close();
        ss.close();
}
}

客户端:服务端存在的情况下,开启后向服务端发送连接请求,连接成功后可发送数据。

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
 
public class TCPClient
{
public static void main(String[] args) throws UnknownHostException, IOException
{
        //创建套接字,输入主机ip和端口号
        Socket s = new Socket("127.0.0.1", 10001);
       
        //发送数据流
        OutputStream out = s.getOutputStream();
        out.write("客户端发送的数据".getBytes());
       
        //接收服务端的回馈信息
        InputStream in = s.getInputStream();
        byte[] bys = new byte[1024];
        int len = in.read(bys);
        System.out.println(new String(bys, 0,len));
        s.close();
}
}


6、 并发的网络编程

并发,即同时为多个客户端服务,由于客户端的随机性,不可能由服务器单线操作,这里就要考虑能并发执行的多线程了。服务端将执行代码写进线程的run函数中,而后循环监听发送连接请求的客户端,用accept方法连接客户端,并为每一个客户端建立线程,而后交给线程自己去处理,主线程只负责监听连接。

下面看一个并发上传图片的例子,注意如果读取是利用InputStreamReader转换成字符流读入BufferedReader缓冲区中,一定要记得换行和刷新,这里才用的是直接的字节流:

//服务端:需先运行
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
 
class ServerThread implements Runnable
{
private Socket s = null;
private static int count = 1;
 
public ServerThread(Socket s)
{
        this.s = s;
}
public void run()
{
        FileOutputStream fis =null;
        try
        {
               //获取客户端输入流
               InputStream in =s.getInputStream();
               String ip =s.getInetAddress().getHostAddress();//获取地址
               fis  = new FileOutputStream(ip+(count++)+".jpg");
              
               byte[] bys= new byte[1024];
               int len = 0;
               //读取数据
               while((len= in.read(bys))!=-1)
               {
                      //写入文件中
                      fis.write(bys, 0, len);
               }
              
               //回馈客户端信息
               OutputStream out=s.getOutputStream();
               out.write("上传成功!".getBytes());
               s.close();
        } catch (IOException e)
        {
               throw new RuntimeException("上传失败");
        }
        finally
        {
                      try
                      {
                             if(fis != null)
                                    fis.close();
                      } catch (IOException e)
                      {
                             throw newRuntimeException("写入流关闭失败!");
                      }
        }
}
}
public class UploadPicDemo
{
public static void main(String[] args)
{    
        ServerSocket ss=null;
        Socket s = null;
        try
        {
                ss = new ServerSocket(10001);
                //循环连接多个客户端
                while(true)
                {
                      //没连接一个交给线程处理
                       s = ss.accept();
                       new Thread(new ServerThread(s)).start(); 
                }
        } catch (IOException e)
        {
               throw new RuntimeException("服务端创建失败!");
        }
}
}

//客户端:读入本地图片,上传,可有多个
import java.io.*;
import java.net.Socket;
 
public class UploadTcpClient
{
public static void main(String[] args)
{
        Socket s = null;
        FileInputStream fis = null;
        try
        {
               //建立客户端套接字,输入主机ip,端口号
               s = new Socket("127.0.0.1", 10001);
               //获取要读取文件
               fis = new FileInputStream("1.jpg");
               OutputStream out =s.getOutputStream();//客户端数据发送对象
              
               byte[] bys = new byte[1024];
               int len = 0;
               //读取并发送
               while((len = fis.read(bys))!=-1)
               {
                      out.write(bys, 0, len);
               }
               s.shutdownOutput();//告诉服务端发送完毕
              
               //接收反馈信息
               InputStream in =s.getInputStream();
               byte[] by = new byte[1024];
               int n = in.read(by);
               System.out.println(new String(by,0, n));
               s.close();
        } catch (IOException e)
        {
               throw new RuntimeException("文件读取失败!");
        }
        finally
        {
               try
               {
                      if(fis != null)
                             fis.close();
               }catch(IOException e)
               {
                      throw new RuntimeException("文件流关闭失败!");
               }
        }
}
}


7、 URL-URLConnection

URL代表一个统一资源定位符,是指向互联网”资源”的指针。在这里他更适合对网站地址进行拆分,从网站地址中获取指定的部分,如获取协议,主机号,端口号等。

String getProtocol()  // 获取协议

String getHost()     // 获取主机名

int getPort()         // 获取端口号

String getFile()     // 获取URL文件名

String getPath()     //获取此URL的路径部分

String getQuery()   // 获取此URL的查询部,客户端传输的特定信息

 

URLConnection:它代表应用程序和 URL 之间的通信链接。此类的实例可用于读取和写入此 URL 引用的资源。通常,创建一个到URL 的连接需要几个步骤:

通过在 URL 上调用 openConnection 方法创建连接对象。

处理设置参数和一般请求属性。

使用 connect 方法建立到远程对象的实际连接。

远程对象变为可用。远程对象的头字段和内容变为可访问。

如:

URL url =new URL(urlPath);//传入一连接服务器的URL地址
       //获取连接
       URLConnectionconn = url.openConnection();
       //获取输入流,读取获得的数据,并显示在文本框中
       InputStreamin = conn.getInputStream();
       byte[]buf = new byte[1024];
       intlen=in.read();
       Stringstr = new String(buf,0,len));//获取URL地址的资源信息


8、 域名解析

当获取到一个网站地址时,由于是字串表意思的网址,则需要经过域名解析,解析成IP地址,这要通过DNS服务器(域名解析服务器),再根据IP地址访问该IP地址的服务器。解析过程如下:

比如http://www.sina.com.cn/myweb/demo.html,先启动相对应的http传输协议,而后去公网的DNS服务器需找www.sina.com.cn的IP地址映射,再将IP返回,根据IP地址去访问sina的主机服务器。

DNS服务器一般会默认配置,优先配置和访问最近的DNS服务器。

---------------------- ASP.Net+Android+IO开发S.Net培训、期待与您交流! ----------------------

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值