套接字,为TCP/IP协议网络通讯的网络操做单元;html
而抽象上来讲:Socket只是一个供上层调用的抽象接口,至关因而传输层下的数据,还没通过应用层的封装,或者说不须要应用层的封装,由于直接使用socket链接的有两种状况,第一种状况就是直接获取传输层传输过来的输入流,按顺序合成以后就是一个完整的文件,或者是一个字符串等等不须要所谓的解析;另外一种状况就是从socket链接返回的数据进行二次封装,进行应用层解析,达到大牛的网络访问框架,实现应用层封装,不过不少细节须要注意。java
联系:UrlConnection基于Http协议,只不过多了封装,本质上也是创建Tcp链接,利用socket进行链接和数据传输,只不过每次链接以后都要手动关闭链接。所以直接使用Socket进行网络通信得考虑线程治理、客户状态监控等,可是不用发送头信息等,更省流量。android
区别web
HttpURLConnection
HttpURLConnection只是继承UrlConnection,二者都是接口,只是在该接口的基础上进行简单封装apache
从Android4.4开始HttpURLConnection的底层实现采用的是okHttp编程
HttpClientjson
HttpClient就是对java提供的方法的一些封装,在HttpURLConnection的输入输出流操做,在HttpClient接口里直接封装成HttpPost、HttpGet、HttpResponse。很方便,另外须要注意的是post方式的状况下,咱们须要进行字符编码,不然会出错。
Apache HttpClient早就不推荐httpclient,5.0以后干脆废弃,后续会删除。6.0删除了HttpClient。设计模式
OkHttp
okhttp是高性能的http库,支持同步、异步,并且实现了spdy、http二、websocket协议,api很简洁易用,和volley同样实现了http协议的缓存。picasso就是利用okhttp的缓存机制实现其文件缓存,实现的很优雅,很正确,反例就是UIL(universal image loader),本身作的文件缓存,并且不遵照http缓存机制。api
OkHttp的最底层是Socket,而不是HTTP,它经过Platform的Class.forName()反射得到当前Runtime使用的socket库跨域
volley
volley是一个简单的异步http库,仅此而已。缺点是不支持同步,这点会限制开发模式。自带缓存,支持自定义请求。不适合大文件上传和下载。
Volley在Android 2.3及以上版本,使用的是HttpURLConnection,而在Android 2.2及如下版本,使用的是HttpClient。
Volley本身的定位是轻量级网络交互,适合大量的,小数据传输。
不过再怎么封装Volley在功能拓展性上始终没法与OkHttp相比。Volley中止了更新,而OkHttp获得了官方的承认,并在不断优化。
android-async-http。
与volley同样是异步网络库,但volley是封装的httpUrlConnection,它是封装的httpClient,而android平台不推荐用HttpClient了,因此这个库已经不适合android平台了。
Retrofit
Retrofit 是一个 RESTful 的 HTTP 网络请求框架的封装。注意这里并无说它是网络请求框架,主要缘由在于网络请求的工做并非 Retrofit 来完成的。Retrofit 2.0 开始内置 OkHttp,前者专一于接口的封装,后者专一于网络请求的高效,两者分工协做,宛如古人的『你耕地来我织布』,小日子别提多幸福了。参考深刻浅出 Retrofit
retrofit与picasso同样都是在okhttp基础之上作的封装,项目中能够直接用了。Retrofit由于也是square出的,因此你们可能对它更崇拜些。Retrofit的跟Volley是一个套路,但解耦的更完全:比方说经过注解来配置请求参数,经过工厂来生成CallAdapter,Converter,你可使用不一样的请求适配器(CallAdapter), 比方说RxJava,Java8, Guava。你可使用不一样的反序列化工具(Converter),比方说json, protobuff, xml, moshi等等。炒鸡解耦,里面涉及到超多设计模式,我的以为是很经典的学习案例。虽然支持Java8, Guava你可能也不须要用到。xml,protobuff等数据格式你也可能不须要解析。but,万一遇到鬼了呢。至于性能上,我的以为这彻底取决于请求client,也就是okhttp的性能,跟这些封装工具没太大关系。
对于咱们熟知的网络访问工具类HttpURLConnection和HttpClient,这两个接口均可以用来开发Http访问。
须要引入httpClient包,在本人AS环境,SDK处于23的状况下,须要引入:
android { useLibrary 'org.apache.http.legacy'//httpClient须要包 }
须要的权限:
<uses-permission android:name="android.permission.INTERNET" />
简单的使用:
httpURLConnection、HttpClient:
/** * 使用HTTPUrlConnection例子 * @param username * @param password * @return */ public static String login(String username,String password){ String msg = ""; try { username = URLEncoder.encode(username,"UTF-8");//这里要注意编码,若是参数含有汉字或是空格(尤为是日期中的空格),不编码会发生错误 password = URLEncoder.encode(password,"UTF-8"); } catch (UnsupportedEncodingException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } //要访问的HttpServlet String urlStr="http://127.0.0.1:8080/MyProject/getUser?"; //要传递的数 String params ="username="+username+"&password="+password; urlStr = urlStr+params; try{ URL url =new URL(urlStr); //得到链接 HttpURLConnection conn = (HttpURLConnection)url.openConnection(); conn.setConnectTimeout(6000); conn.setRequestMethod("GET");//请求方式 InputStream in = conn.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(in, HTTP.UTF_8)); String line = null; while ((line = reader.readLine()) != null) { if(msg==null){ msg=line; }else{ msg += line; } } reader.close(); in.close();//关闭数据流 conn.disconnect(); }catch(Exception e){ e.printStackTrace(); return null; } return msg; } /** * 使用HttpClient访问,get方式,若是sdk版本为23,须要引入org.apache.http.legacy * @return */ private static String loginHttpClientGet(){ // http地址 String httpUrl = "http://192.168.1.110:8080/httpget.jsp?par=HttpClient_android_Get"; //HttpGet链接对象 HttpGet httpRequest = new HttpGet(httpUrl); String strResult = ""; try { //取得HttpClient对象 HttpClient httpclient = new DefaultHttpClient(); //请求HttpClient,取得HttpResponse HttpResponse httpResponse = httpclient.execute(httpRequest); //请求成功 if (httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { //取得返回的字符串 strResult = EntityUtils.toString(httpResponse.getEntity()); // mTextView.setText(strResult); } else { // mTextView.setText("请求错误!"); } return strResult; } catch (ClientProtocolException e) { // mTextView.setText(e.getMessage().toString()); } catch (IOException e) { // mTextView.setText(e.getMessage().toString()); } catch (Exception e) { // mTextView.setText(e.getMessage().toString()); } return strResult; } /** * 使用HttpClient访问,post方式 * @return */ private static String loginHttpClientPost(){ // http地址 String httpUrl = "http://192.168.1.110:8080/httpget.jsp"; //HttpPost链接对象 HttpPost httpRequest = new HttpPost(httpUrl); //使用NameValuePair来保存要传递的Post参数 List<NameValuePair> params = new ArrayList<NameValuePair>(); //添加要传递的参数 params.add(new BasicNameValuePair("par", "HttpClient_android_Post")); String strResult = ""; try { //设置字符集 HttpEntity httpentity = new UrlEncodedFormEntity(params, "gb2312"); //请求httpRequest httpRequest.setEntity(httpentity); //取得默认的HttpClient HttpClient httpclient = new DefaultHttpClient(); //取得HttpResponse HttpResponse httpResponse = httpclient.execute(httpRequest); //HttpStatus.SC_OK表示链接成功 if (httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { //取得返回的字符串 strResult = EntityUtils.toString(httpResponse.getEntity()); // mTextView.setText(strResult); } else { // mTextView.setText("请求错误!"); } return strResult; } catch (ClientProtocolException e) { // mTextView.setText(e.getMessage().toString()); } catch (IOException e) { // mTextView.setText(e.getMessage().toString()); } catch (Exception e) { // mTextView.setText(e.getMessage().toString()); } return strResult; }
socket:
//服务器端 public class MyServer { private static int count=0; public static void main(String[]args){ try { //实例化服务器套接字 设置端口号8888 ServerSocket server=new ServerSocket(8888); while(true){ //链接编号设置 count=count+1; //时间格式 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); //实例化客户端 Socket client=server.accept(); //实例化时间 以及 id System.out.println(count+":"+sdf.format(System.currentTimeMillis())); //获取输出流 OutputStream out=client.getOutputStream(); //输出字符串 String msg="Hello,Android!"; //写字符串 out.write(msg.getBytes()); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } //客户端 public class MyClientActivity extends Activity { /** Called when the activity is first created. */ private Button rev=null; private TextView revtext=null; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); rev=(Button)findViewById(R.id.rev); revtext=(TextView)findViewById(R.id.receiver); rev.setOnClickListener(new receiverlistenr()); } class receiverlistenr implements OnClickListener{ public void onClick(View v) { // TODO Auto-generated method stub try { //实例化Socket Socket socket=new Socket("169.254.202.149",8888); //得到输入流 InputStream in=socket.getInputStream(); //缓冲区 byte[] buffer=new byte[in.available()]; //读取缓冲区 in.read(buffer); //转换字符串 String msg=new String(buffer); //设置文本框的字符串 revtext.setText(msg); } catch (UnknownHostException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
Socket链接---至少须要一对套接字,其中一个运行于客户端,称为ClientSocket ,另外一个运行于服务器端,称为ServerSocket套接字之间的链接过程分为三个步骤:服务器监听,客户端请求,链接确认
请求报文的通常格式:
一般来讲一个HTTP请求报文由请求行、请求报头、空行、和请求数据4个部分组成。
GET http://blog.csdn.net/itachi85 HTTP/1.1 //请求行 Host: blog.csdn.net //请求报头 Connection: keep-alive Cache-Control: max-age=0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.80 Safari/537.36 QQBrowser/9.3.6872.400 Accept-Encoding: gzip, deflate, sdch Accept-Language: zh-CN,zh;q=0.8 Cookie: bdshare_firstime=1443768140949; uuid_tt_dd=5028529250430960147_20151002;
//不能省略的空格
28b5 }ysI 1ߡFsgl n- ]{^_ { 'z! C , m# 0 !l ` 4x ly .ݪ*
...省略
响应报文的通常格式:
HTTP的响应报文由状态行、消息报头、空行、响应正文组成。
HTTP/1.1 200 OK //状态行 Server: openresty //响应报头 Date: Sun, 27 Mar 2016 08:26:54 GMT Content-Type: text/html; charset=utf-8 Transfer-Encoding: chunked Connection: keep-alive Keep-Alive: timeout=20 Vary: Accept-Encoding Cache-Control: private X-Powered-By: PHP 5.4.28 Content-Encoding: gzip //不能省略的空格 28b5 }ysI 1ߡFsgl n- ]{^_ { 'z! C , m# 0 !l ` 4x ly .ݪ* ڴzAt_Xl * 9'O ɬ ' ק 3 ^1a ...省略
若是是请求文件(下载)
要从文件已经下载的地方开始继续下载。在之前版本的 HTTP 协议是不支持断点的,HTTP/1.1 开始就支持了。通常断点下载时才用到 Range 和 Content-Range 实体头。
Range
用于请求头中,指定第一个字节的位置和最后一个字节的位置,通常格式:
Range:(unit=first byte pos)-[last byte pos]
Content-Range
用于响应头,指定整个实体中的一部分的插入位置,他也指示了整个实体的长度。在服务器向客户返回一个部分响应,它必须描述响应覆盖的范围和整个实体长度。通常格式:
Content-Range: bytes (unit first byte pos) - [last byte pos]/[entity legth]
请求下载整个文件:
通常正常回应
注意:对于socket网络访问
服务器端:根据serversocket.accept()接收到请求socket,socket的inputstream为请求报文(与Http请求报文一致),而且把数据写入到socket的outputStream中(数据格式与HTTP响应报文一致)。
客户端:根据socket,inputStream为服务器返回的数据
据RFC2616标准(现行的HTTP/1.1)得知有如下8种方法:OPTIONS、GET、HEAD、POST、PUT、DELETE、TRACE和CONNECT。
con.setRequestMethod("");//设置请求状态
HTTP请求方法有8种,分别是GET、POST、DELETE、PUT、HEAD、TRACE、CONNECT 、OPTIONS。其中PUT、DELETE、POST、GET分别对应着增删改查,对于移动开发最经常使用的就是POST和GET了。
用来检查服务器的性能。例如:AJAX进行跨域请求时的预检,须要向另一个域名的资源发送一个HTTP OPTIONS请求头,用以判断实际发送的请求是否安全。
参考连接:
URLConnection/HttpURLConnection/HttpClient/socket 差异