android网络编程的再学习

Android中的网络编程一个是基于http协议的短连接,一个是基于Socket的长连接
与服务器通信一般有三种:HttpURLConnection、HttpClient(比前者的稳定性更好,但已已弃用)、Socket(有TCP/UDP两种方式)
这两种方式在手机端应用很广,比如说豌豆荚类的手机助手软件,它的通信方式有USB,wifi,还有一部手机助手用的是蓝牙的
助手类手机软件的通信方式:

比如说豌豆荚这个软件往电脑上一装,把手机端通过USB接进来,PC端就会很智能的安装驱动,我们知道在windows系统下,如果不安装驱动,手机是连接不上的。而在linux下是不用安装驱动的。Windows下之所以能很智能的安装驱动,是因为USB一连上就能够读取PID,VID信息,一个是厂家的唯一标识,一个是版本的唯一标识,通过这两个就知道你手机的信息,(PID还有一个含义是进程ID也就是进程标识符,操作系统里每打开一个程序,都会创建一个进行ID,也就是这个PID),这些信息一旦你读到后,你就可以到数据库中去查,去查查这个厂家是不是生产手机的,去看看是这个厂家生产的手机的哪个版本。这些信息有,再去找应用安装驱动就容易了,而这个驱动装上之后,就可以利用USB使用手机端和PC端进行通信。
Wifi就更简单了,当你的手机接入到局域网的时候,它就会等着,等着什么呢?比如一台了豌豆荚这个应用,当这个应用启动的时候,就会使用UDP的方式往这个局域网中的所有IP上扔一个数据包,如果你手机上安装了这个应用,就会获取到这个数据包,而且这个数据包是手机端能处理的,因此手机端就会和PC端建立连接,连接建立好之后,就可以传输数据了。

蓝牙也是一样的,它有一个专门的工具叫BlueToothSocket,bluetoothsocket类不仅可以在手机助手软件可以使用,而且可以在大数据量实时传输的时候使用,比如像一些贵金属的交易平台还有一些股票交易的平台,他们的数据量就非常大,而且是不允许延迟的,像股票这样的,一旦延迟,就出事儿了,这时可以使用bluetoothsocket这个api。还有在小数据量实时性不强的地方也会使用到,比如说消息推送还有精准营销,比如我在一个商城中买了一件东西,那么在服务器端就会记录谁谁谁在某个时间买了什么东西,所买的东西的信息,当在服务器端记录了这些信息之后,精准营销的做法是:当所买的东西这一类的商品有促销信息的时候,它会从服务器端把这些信息比对一下,然后把这些信息发个你,因为你以前买过。这样处理的原理是什么呢?画图说明,手机端和服务器端,如果说每次访问服务器都要建立连接,访问后断开连接,而下一次再访问的时候再建立连接、断开连接,再加上传输数据,这样消耗的时间就会很多,这时候,我的处理方法是,建立一个连接通道,建立好之后,就不关掉(close掉它)了,但是这时候,手机在底层会做这样的一件事儿,当连接空闲的时候,比如说一分钟,通常我们也会遇到这个问题,我们用手机应用访问网络的时候,等着一段时间就会出现timeout时间超时这个问题,也就是说,如果我们建立的这个连接通道如果不关的话,就会被回收掉。考虑到这个问题,我的处理方式,使用长连接,长连接的思路是这样子的,建立这个连接通道,每隔一段时间就会给服务器发送一个数据包,骚扰一下服务器,这个时间段不能大于1分钟,发送的数据包的大小也要很小,比如1个字节,目的就是保持这个通道的畅通,而服务器端在收到这个数据包的时候也会返回一个数据包,当手机端收到这个数据包的时候,就说明这个通道是畅通无阻的。
使用socket长连接还有一个问题需要注意,我们知道手机和服务器端通信的话,是要通过基站传输的,如果一个人拿着手机从一个基站下跑到了另一个基站下,那么这个连接通道就会断开了,会在另一个基站下重新连接,所以手机处理socket长连接的机制不是这么简单,我了解过,能在以后的开发中根据一些资料来运用。长连接处理的是大数据量的实时传输。
而对于小数据量的,实时性不强的,这时候用到的,画图说明,手机端和服务器端,如果是在服务器端找手机,是找不到的,只能找到局域网分配给这个手机的ip,拿到这个ip是没用的,但是在基站下面会找到手机的位置,但是这样是很消耗资源的,服务器要想直接对手机端进行消息推送是不行的,我的解决办法是,在手机端,利用socket的UDP方式每隔一段时间就向服务器端发送数据包,由于数据量很小,实时性也不强,因此这里的时间间隔就不用那么短,像精准营销,我们每半天发送一个就不少了,每隔半天向服务器发送一个数据包,服务器就会处理,检查这时候是否有促销信息,如果有的话就会给、手机端发送这个信息,消息推送的原理也是这样的。
分析完与服务器端的通信方式和通信渠道之后,我就使用HttpClient实现与服务器端的通信,之所以使用它,是因为它的稳定性好。
首先我要判断手机通信的渠道,看是wifi、wap还是net,我在代码里建立了一个工具类NetUtil,用于判断网络的类型,在这个类里面先判断是wifi还是apn(接入口/基站),如果都不是就要提示用户去配置网络,如果是apn这种方式,就要判断是wap还是net,如果是wap,就要去设置ip和端口。怎么判断是wap还是net呢?读取当前连接的apn信息,看ip和端口是否有值,使用到ConnectivityManager类中判断网络类型的方法。

手机端使用HTTP协议和服务器端进行网络交互,并对返回数据进行解析
手机中加载和显示网页:(不能使用系统浏览器)借用WebView控件在自己程序中嵌入一个浏览器
调用其getSetting方法设置一些浏览器属性
这里只设置setJavaScriptEnabled方法去支持JavaScript脚本
发HTTP请求,拿响应数据 的动作被深度封装了
下面我们手动发送HTTP请求:HttpURLConnection 和 HttpClient

手机端的通信渠道有:wifi(覆盖范围一般有百米),手机APN接入点(即基站,有wap和net两种方式,覆盖范围是1~2公里)

使用HttpURLConnection在wap方式下跑的时候,由于移动代理的服务器是有地域性的,比如说北京有 一台服务器,而上海也有一台服务器,虽然这两台服务器的端口一样,但是它们是由不同的人员来维护的,这就有可能产生一些差距,我们的应用在北京的时候使用很正常,再把这个应用拿到上海去,通过wap方式访问的时候,成功率就会很低,而且每次产生的错误可能不一样,这种错误是很难调的,没有规律性,而我们的应用是要在全国推广的,所以这种错误是不允许的。不使用HttpURLConnection,而改用HttpClient这个框架的时候,问题就可以避免了。HttpClient的稳定性明显比前者好的多,所以以后的应用也都改使用HttpClient框架。OkHttp是一个相对成熟的解决方案,Android4.4的源码中可以看到HttpURLConnection已经替换成OkHttp实现

IP是互联网络协议(通过IP协议使Internet成为一个可以连接不同操作系统终端的网络,安装IP软件可以将消息分成一个个小包从一个主机传送到了互联网络的另一个主机,但没解决传输中可能出现的问题),
TCP是传输控制协议(还需要安装端对端的可控的TCP协议保障通信的可靠性,其通信二端各建立一个代表二端通信端口的Socket形成网络虚拟链路,其中使用ServerSocekt创建的TCP服务器端先主动接收另一端的连接请求,最后通过该Socket连接通道产生的IO流进行通信),
所以TCP/IP连着是一套功能互补的网络协议集

USB连接:windows系统不安装驱动手机是连接不上的,linux下不用安装驱动,而一连接就智能的安装驱动是因为USB一连上就能够读取PID(厂家唯一标识、进程ID),VID(版本唯一标识),就可以到数据库中去查这个厂家是不是生产手机的,这个厂家生产的手机的哪个版本,再去找应用安装驱动就容易了,而这个驱动装上之后,就可以利用USB使用手机端和PC端进行通信。

Wifi连接:手机接入局域网就会等电脑上启动的应用使用UDP的方式往这个局域网中的所有IP上扔一个数据包,手机上安装该应用就会获取并处理这个数据包,这样就实现了连接并通信;

蓝牙连接:使用bluetoothsocket类建立长连接

为了分工分为四层:OSI参考模式
1.类似信纸的应用程序层(向用户提供常用的应用程序:超文本传输HTTP、域名解DNS、析电子邮件SMTP、文件传输FTP、远程登录Telnet)
2.类似信封的传输控制层(
TCP是面向连接的带确认的可靠的有序的带流量控制的端到端的数据流服务
64k 分数据包 服务器收到后合并交给服务器端的应用层、
UDP放的还是应用程序层的数据,只不过速度快,适用于一次只传很少数据、对可靠性要求不高的实时性通信,因为它是面向数据报的非连接(正式通信前不建立连接,不管对方状态就直接发送,数据对方是否可以接收其无法控制)的不可靠的传输控制协议,也是通信二端各建立一个Socket,但没有虚拟链路,这二个Socket只是发送、接收数据报的对象,在数据的发送端UDP将网络数据流封装成数据报发送,将数据传给IP层转发,并不保证数据可以到达目的地;在数据的接收端UDP将数据报转换成实际数据内容;可以认为UDP协议的Socket类似于码头,数据报类似于集装箱;码头的作用就是负责发送接收集装箱,而DataGramSocket的作用则是发送接收数据报,因此基于UDP协议的通信双方没有所谓的客户端和服务器端的概念)
3.类似信箱的网络层(IP)所有TCP、UDP等传输控制层协议都是基于IP协议以IP数据包格式传输数据, IP是面向无连接的提供不可靠的、尽力而为的不保证送达的协议
4.物理+链路层

网络通信-长连接Socket(C/S)
客户端在发送请求数据之前先与服务器建立连接,并且不关闭连接,保持与服务器端通讯的实时性
但是会比较占用服务器端资源
长连接做实时/及时通信 QQ、微信
java.net.InetAddress表示一个网络IP地址
* 创建实例

InetAddress addr = InetAddress.getLocalHost();找本机

InetAddress addr = InetAddress.getByName(“xx.xxx.xx.xx”);
通过IP找远程主机
方法

getHostName()     得到主机名      getHostAddress()  得到主机IP

java.net.ServerSocket在服务器端选择一个端口等待/监听客户端发起连接
* 应选择 1024 - 50000 端口,选择的端口不能被其他应用占用

创建实例

ServerSocket ss = new ServerSocket(8000);

方法

accept()         
    等待客户端发起连接,当客户端发起连接时,这个方法会创建 socket 连接通道
    *它是一个阻塞的方法 *返回 Socket 类型对象,表示通道的一端

out.flush();发送完消息并关流
ss.close() * 停止服务,释放端口
java.net.Socket套接字(封装IP和端口 端口对端口的虚拟链接)

android网络编程的再学习_第1张图片
* 网络套接字是网络中一个端点的表示;封装了java进行网络通信时所必须的信息;
连接时使用的协议,本地主机地址/端口,目标主机IP地址及端口;
* IP+端口
网络中本质传的都是二进制数据流
客户端 输入输出流(流的底层都是二进制数据)
服务器端 输入输出流
IP地址 寻找目标主机用的
port端口号: 定位目标主机上的服务应用
每个服务端应用都会开辟唯一一个端口号
最大65535,不允许多个应用使用同一个端口号

域名:如:www.baidu.com
IP(局域网IP和广域网IP):172.30.2.4
自己主机的IP可以是:LocalHost或127.0.0.1
DNS:服务器:将域名解析为IP地址
主机
sockt连接通道:端口对端口的虚拟链接
被动方:服务器端(软件)  网易 手机也可以作为服务器
主动方:客户端 用户
端口:如:ftp 21、http 80

  • 连接通道

    创建实例

    1,服务器端:
    Socket s = ss.accept();

    2,客户端:
    Socket s = new Socket(ip, port);//参数:IP ,端口
    方法

    s.getInputStream()
    s.getOutputStream()
    因为in.read()会一直等待下去,是阻塞的方法
    s.setSoTimeout(超时时长):对接收数据有影响 ,阻断等待

    isClosed()
    isConnected()
    isOutputShutdown()
    isInputShutdown()

out.flush();发送完消息并关流
s.close() 断开Socket通道
Android的网络编程部分,基本上和Java的网络编程是一样的,
基本上也分成两种,一种是基于Socket的,另外一种是基于Http协议的。

基于Socket的基本用法(长连接)

一:服务器端server 属于一个服务端程序
1:在本机1234端口创建一个服务器端的ServerSocket监听:
ServerSocket ss = new ServerSocket(1234);

2:开始监听请求(等待客户端发起连接后调用该监听类的accept方法
建立Socket连接通道并返回该通道)
Socket s = ss.accept();//阻塞等待客户端的连接

3:通过Socket通道取得客户端的输入和输出流:
InputStream in = s.getInputStream();
OutputStream out=s.getOutputStream();

4:然后就可以通过流来进行网络传输了
//out.write()在服务端是给客户端发送消息
// in.read()读取客户端发来的消息并显示
byte[] bs = new byte[1024];
in.read(bs);
String str= new String(bs);
System.out.println(str);
out.write(“Server send”.getBytes());

包装成上层流Reader、Writer来做。
BufferedReader in =
new BufferedReader(字符缓冲流
new InputStreamReader(字符转换流,可带编码
s.getInputStream()),”UTF-8”);文件字节流
PrintWriter out =
new PrintWriter(//带自动行刷新的字符缓冲流
new OutputStreamWriter(//字符转换流,可带编码
s.getOutputStream(),”UTF-8”));//文件字节流

out.println(“server send”);在服务器端给客户端发送消息
System.out.println(in.readLine());在服务器端读取客户端发来的消息并显示
5:在服务器端最好要记得关闭输出流和Server服务
out.flush();发送完消息并关流
ss.close();在服务器端关Server服务

二:客户端:client
1:用指定的IP和端口发起(创建)一个socket连接通道:
Socket s = new Socket(“192.168.1.2”,1234);

2:通过Socket通道取得服务端的输入和输出流:
InputStream in = s.getInputStream();
OutputStream out = s.getOutputStream();

3:然后就可以通过流来进行网络传输了
//out.write()在客户端是给服务端发送消息
//in.read()在客户端是读取服务端发来的消息

byte[] bs = new byte[1024];
String str = “client send”;
out.write(str.getBytes());
in.read(bs);
System.out.println(new String(bs));

包装成上层流Reader、Writer来做。
BufferedReader in =
new BufferedReader(字符缓冲流
new InputStreamReader(字符转换流,可带编码
s.getInputStream()),”UTF-8”);文件字节流
PrintWriter out =
new PrintWriter(//带自动行刷新的字符缓冲流
new OutputStreamWriter(//字符转换流,可带编码
s.getOutputStream(),”UTF-8”));//文件字节流

out.println(“client send”);在客户端给服务器端发送消息
System.out.println(in.readLine());在客户端读取服务器端发来的消息并显示
4:在客户端中最好要记得关闭输出流和Socket通道
out.flush();发送完消息并关流
s.close();在客户端关Socket通道

一,网络编程中两个主要的问题
一个是如何准确的定位网络上一台或多台主机,
另一个就是找到主机后如何可靠高效的进行数据传输。
在TCP/IP协议中IP层主要负责网络主机的定位,数据传输的路由,
由IP地址可以唯一地确定Internet上的一台主机。
而TCP层则提供面向应用的可靠(tcp)的或非可靠(UDP)的数据传输机制,
这是网络编程的主要对象,一般不需要关心IP层是如何处理数据的。
目前较为流行的网络编程模型是客户机/服务器(C/S)结构。即通信双方一方作为服务器等待客户提出请求并予以响应。客户则在需要服务时向服务器提出申请。服务器一般作为守护进程始终运行,监听网络端口,一旦有客户请求,就会启动一个服务进程来响应该客户,同时自己继续监听服务端口,使后来的客户也能及时得到服务。
二,两类传输协议:TCP和UDP
TCP是Tranfer Control Protocol的简称,是一种面向连接的保证可靠传输的协议。通过TCP协议传输,得到的是一个顺序的无差错的数据流。发送方和接收方的成对的两个socket之间必须建立连接,以便在TCP协议的基础上进行通信,当一个socket(通常都是server socket)等待建立连接时,另一个socket可以要求进行连接,一旦这两个socket连接起来,它们就可以进行双向数据传输,双方都可以进行发送或接收操作。
UDP是User Datagram Protocol的简称,是一种无连接的协议,每个数据报都是一个独立的信息,包括完整的源地址或目的地址,它在网络上以任何可能的路径传往目的地,因此能否到达目的地,到达目的地的时间以及内容的正确性都是不能被保证的。
比较:
UDP:1,每个数据报中都给出了完整的地址信息,因此无需要建立发送方和接收方的连接。
2,UDP传输数据时是有大小限制的,每个被传输的数据报必须限定在64KB之内。
3,UDP是一个不可靠的协议,发送方所发送的数据报并不一定以相同的次序到达接收方
TCP:1,面向连接的协议,在socket之间进行数据传输之前必然要建立连接,所以在TCP中需要连接
时间。
2,TCP传输数据大小限制,一旦连接建立起来,双方的socket就可以按统一的格式传输大的
数据。
3,TCP是一个可靠的协议,它确保接收方完全正确地获取发送方所发送的全部数据。
应用:
1,TCP在网络通信上有极强的生命力,例如远程连接(Telnet)和文件传输(FTP)都需要不定长度的数据被可靠地传输。但是可靠的传输是要付出代价的,对数据内容正确性的检验必然占用计算机的处理时间和网络的带宽,因此TCP传输的效率不如UDP高。
2,UDP操作简单,而且仅需要较少的监护,因此通常用于局域网高可靠性的分散系统中client/server应用程序。例如视频会议系统,并不要求音频视频数据绝对的正确,只要保证连贯性就可以了,这种情况下显然使用UDP会更合理一些。
三,基于Socket的java网络编程
1,什么是Socket
网络上的两个程序通过一个双向的通讯连接实现数据的交换,这个双向链路的一端称为一个Socket。Socket通常用来实现客户方和服务方的连接。Socket是TCP/IP协议的一个十分流行的编程界面,一个Socket由一个IP地址和一个端口号唯一确定。
但是,Socket所支持的协议种类也不光TCP/IP一种,因此两者之间是没有必然联系的。在Java环境下,Socket编程主要是指基于TCP/IP协议的网络编程。
2,Socket通讯的过程
Server端Listen(监听)某个端口是否有连接请求,Client端向Server 端发出Connect(连接)请求,Server端向Client端发回Accept(接受)消息。一个连接就建立起来了。Server端和Client 端都可以通过Send,Write等方法与对方通信。
对于一个功能齐全的Socket,都要包含以下基本结构,其工作过程包含以下四个基本的步骤:
  (1) 创建Socket;
  (2) 打开连接到Socket的输入/出流;
  (3) 按照一定的协议对Socket进行读/写操作;
  (4) 关闭Socket.(在实际应用中,并未使用到显示的close,虽然很多文章都推荐如此,不过在我的程序中,可能因为程序本身比较简单,要求不高,所以并未造成什么影响。)
3,创建Socket
创建Socket
java在包java.net中提供了两个类Socket和ServerSocket,分别用来表示双向连接的客户端和服务端。这是两个封装得非常好的类,使用很方便。其构造方法如下:
Socket(InetAddress address, int port);
Socket(InetAddress address, int port, boolean stream);
Socket(String host, int prot);
Socket(String host, int prot, boolean stream);
Socket(SocketImpl impl)
Socket(String host, int port, InetAddress localAddr, int localPort)
Socket(InetAddress address, int port, InetAddress localAddr, int localPort)
ServerSocket(int port);
ServerSocket(int port, int backlog);
ServerSocket(int port, int backlog, InetAddress bindAddr)
  其中address、host和port分别是双向连接中另一方的IP地址、主机名和端 口号,stream指明socket是流socket还是数据报socket,localPort表示本地主机的端口号,localAddr和 bindAddr是本地机器的地址(ServerSocket的主机地址),impl是socket的父类,既可以用来创建serverSocket又可 以用来创建Socket。count则表示服务端所能支持的最大连接数。例如:学习视频网 http://www.xxspw.com
Socket client = new Socket(“127.0.01.”, 80);
ServerSocket server = new ServerSocket(80);
  注意,在选择端口时,必须小心。每一个端口提供一种特定的服务,只有给出正确的端口,才 能获得相应的服务。0~1023的端口号为系统所保留,例如http服务的端口号为80,telnet服务的端口号为21,ftp服务的端口号为23, 所以我们在选择端口号时,最好选择一个大于1023的数以防止发生冲突。
  在创建socket时如果发生错误,将产生IOException,在程序中必须对之作出处理。所以在创建Socket或ServerSocket是必须捕获或抛出例外。
4,简单的Client/Server程序
1. 客户端程序
import java.io.*;
import java.net.*;
public class TalkClient {
public static void main(String args[]) {
try{
Socket socket=new Socket(“127.0.0.1”,4700);
//向本机的4700端口发出客户请求
BufferedReader sin=new BufferedReader(new InputStreamReader(System.in));
//由系统标准输入设备构造BufferedReader对象
PrintWriter os=new PrintWriter(socket.getOutputStream());
//由Socket对象得到输出流,并构造PrintWriter对象
BufferedReader is=new BufferedReader(new InputStreamReader(socket.getInputStream()));
//由Socket对象得到输入流,并构造相应的BufferedReader对象
String readline;
readline=sin.readLine(); //从系统标准输入读入一字符串
while(!readline.equals(“bye”)){
//若从标准输入读入的字符串为 “bye”则停止循环
os.println(readline);
//将从系统标准输入读入的字符串输出到Server
os.flush();
//刷新输出流,使Server马上收到该字符串
System.out.println(“Client:”+readline);
//在系统标准输出上打印读入的字符串
System.out.println(“Server:”+is.readLine());
//从Server读入一字符串,并打印到标准输出上
readline=sin.readLine(); //从系统标准输入读入一字符串
} //继续循环
os.close(); //关闭Socket输出流
is.close(); //关闭Socket输入流
socket.close(); //关闭Socket
}catch(Exception e) {
System.out.println(“Error”+e); //出错,则打印出错信息
}
}
}
2. 服务器端程序
import java.io.*;
import java.net.*;
import java.applet.Applet;
public class TalkServer{
public static void main(String args[]) {
try{
ServerSocket server=null;
try{
server=new ServerSocket(4700);
//创建一个ServerSocket在端口4700监听客户请求
}catch(Exception e) {
System.out.println(“can not listen to:”+e);
//出错,打印出错信息
}
Socket socket=null;
try{
socket=server.accept();
//使用accept()阻塞等待客户请求,有客户
//请求到来则产生一个Socket对象,并继续执行
}catch(Exception e) {
System.out.println(“Error.”+e);
//出错,打印出错信息
}
String line;
BufferedReader is=new BufferedReader(new InputStreamReader(socket.getInputStream()));
//由Socket对象得到输入流,并构造相应的BufferedReader对象
PrintWriter os=newPrintWriter(socket.getOutputStream());
//由Socket对象得到输出流,并构造PrintWriter对象
BufferedReader sin=new BufferedReader(new InputStreamReader(System.in));
//由系统标准输入设备构造BufferedReader对象
System.out.println(“Client:”+is.readLine());
//在标准输出上打印从客户端读入的字符串
line=sin.readLine();
//从标准输入读入一字符串
while(!line.equals(“bye”)){
//如果该字符串为 “bye”,则停止循环
os.println(line);
//向客户端输出该字符串
os.flush();
//刷新输出流,使Client马上收到该字符串
System.out.println(“Server:”+line);
//在系统标准输出上打印读入的字符串
System.out.println(“Client:”+is.readLine());
//从Client读入一字符串,并打印到标准输出上
line=sin.readLine();
//从系统标准输入读入一字符串
} //继续循环
os.close(); //关闭Socket输出流
is.close(); //关闭Socket输入流
socket.close(); //关闭Socket
server.close(); //关闭ServerSocket
}catch(Exception e){
System.out.println(“Error:”+e);
//出错,打印出错信息
}
}
}
5,支持多客户的client/server程序
前面的Client/Server程序只能实现Server和一个客户的对话。在实际应用 中,往往是在服务器上运行一个永久的程序,它可以接收来自其他多个客户端的请求,提供相应的服务。为了实现在服务器方给多个客户提供服务的功能,需要对上 面的程序进行改造,利用多线程实现多客户机制。服务器总是在指定的端口上监听是否有客户请求,一旦监听到客户请求,服务器就会启动一个专门的服务线程来响 应该客户的请求,而服务器本身在启动完线程之后马上又进入监听状态,等待下一个客户的到来。

你可能感兴趣的:(android网络编程的再学习)