当前位置:   article > 正文

Android 网络编程(1)——Socket编程So easy_android socket原理图

android socket原理图

Android网络编程概述

首先,了解的几个问题:

1、Android系统网络相关API接口

1)、java.net.*(标准Java接口)

java.net.*提供与联网有关的类,包括流、数据包套接字(socket)、internet协议、常见Http处理等。比如创建URL,以及URLConnection/HTTPURLConnection对象、设置链接参数、链接到服务器、向服务器写数据、从服务器读取数据等通信。这些在Java网络编程中均有涉及。

2)、Org.apache接口

对于大部分应用程序而言JDK本身提供的网络功能已远远不够,这时就需要Android提供的Apache HttpClient了。它是一个开源项目,功能更加完善,为客户端的Http编程提供高效、最新、功能丰富的工具包支持。

3)、Android.net.*(Android网络接口)

常常使用此包下的类进行Android特有的网络编程,如:访问WIFI,访问Android联网信息,邮件等功能。

2、网络架构主要有两种模式B/S、C/S

  • B/S:就是浏览器/服务器端模式,通过应用层的HTTP协议通信,不需要特定客服端软件,而是需要统一规范的客户端,简而言之就是Android网络浏览器(如chrome,QQ浏览器等)访问web服务器端的方式
  • C/S:就是客户端/服务器端模式,通过任意的网络协议通信,需要特定的客户端软件
3、服务器返回客户端的内容有三种方式
  • 以HTML代码的形式返回
  • 以XML字符串的形式返回,做Android开发是这种方式比较多。返回的数据需要通过XML解析(SAX、DOM、PULL等)器进行解析(必备知识)
  • 以json对象的方式返回
Android完全支持JDK本身的TCP、UDP网络通信API,也可以使用ServerSocket、Socket来建立基于TCP/IP协议的网络通信;也可以使用DatagramSocket、Datagrampacket、MulticastSocket来建立基于UDP协议的网络通信。

TCP/IP与UDP的区别
  • TCP/IP:有连接
  • UDP:无连接
Android网络编程的2种实现
  • 基于http协议
  • 基于socket套接字
Android 网络编程——基于socket通信
一、什么是Socket
1、所谓Socket通常也称作"套接字",用于描述IP地址和端口,是一个同学连的句柄
2、应用程序通常通过"套接字"向网络发送请求或者应答网络请求
二、Socket基本通信模型

Socket通信主要分为服务端和客服端
三、使用基于TCP协议的Socket
一个客户端要发起一次通信,首先必须知道运行服务器端的主机IP地址。然后由网络基础设施利用目标地址,将客服端发送的信息传递到正确的主机上,在Java中,地址可以由一个字符串来定义,这个字符串可以使用数字型的地址(比如192.168.1.1),也可以是主机名(example.com)。在Java当中InetAddress类代表了一个网络目标地址,包括主机名和数字类型的地址信息。
基于TCP协议操作Socket的API
服务端——ServerSocket:这个类是实现一个服务器端的Socket,利用这个类可以监听来自网络的请求
1)、创建ServerSocket的方法
ServerSocket(int localPort);
ServerSocket(int localPort, int queueLimit);
ServerSocket(int localPort, int queueLimit, InetAddress localAddr);
创建一个ServerSocket必须指定一个端口,以便客户端能够向端口号发送连接请求。端口的有效范围是0~65535
2)、ServerSocket操作
Socket accept();——此方法为下一个传入的连接请求创建Socket实例,并将已经成功连接的Socket实例返回给服务器套接字,如果没有连接请求,accept()方法将阻塞等待
void close();——此方法用于关闭套接字
客户端——Socket
1)、创建Socket的方法
Socket(InetAddress remoteAddress, int remotePort);
利用Socket的构造函数,可以创建一个TCP套接字后,先连接到指定的远程地址和端口号
2)、操作Socket的方法
InputStream getInputStream();
OutputStream getOutputStream();
void close();
操作TCP Socket的示意图如下


四、使用UDP的Socket
1)、创建DatagramPacket
DatagramSocket(byte[] data, int offset, int length, InetAddress remoteAddr, int remotePort);
该构造函数创建一个数据报文对象,数据包含在第一个参数中
2)、创建DatagramSocket
DatagramSocket(int localPort);
该构造函数将创建一个UDP套接字
3)、DatagramSocket:发送和接收
void send(DatagramPacket packet);——此方法用来发送DatagramPacket实例,一旦创建连接,数据报将发送到该套接字所连接的地址
void receive(DatagramPacket packet);——此方法将阻塞等待,直到接收到数据报文,并将报文中的数据复制到指定的DatagramPacket实例中

服务器端编程步骤
1、创建服务器端套接字并绑定到一个端口上
2、套接字设置监听模式等待连接请求
3、接收连接请求后进行通信
4、返回,等待另一个连接请求

客户端编程步骤
1、创建客户端套接字(指定服务器端的IP地址和端口号)
2、连接(Android创建Socket时会自动连接)
3、与服务器端进行通信
4、关闭套接字

Android Socket通信原理,注意地方
1、中间的管道连接是通过InputStream/OutputStream流实现的
2、一旦管理建立起来可以进行通信
3、关闭管道的同时意味着关闭Socket
4、当对同一个Socket创建重复管道时会异常
5、通信过程中顺序很重要:服务器端首先得到输入流,然后将输入流信息输出到其各个客户端;客户端先建立连接后先写入输出流,然后再获得输入流,不然会有EOFException的异常。

测试实例
服务器端——采用java代码在PC机编写的服务器,PC机的IP地址为:192.168.0.78
  1. import java.io.BufferedReader;
  2. import java.io.IOException;
  3. import java.io.InputStreamReader;
  4. import java.io.OutputStream;
  5. import java.net.Socket;
  6. import java.net.ServerSocket;
  7. import java.util.ArrayList;
  8. import java.util.List;
  9. public class MyServer{
  10. //定义保存所有的Socket,与客户端建立连接得到一个Socket
  11. public static List<Socket> socketList = new ArrayList<Socket>();
  12. public static void main(String[] args) throws IOException{
  13. ServerSocket server = new ServerSocket(8888);
  14. while (true){
  15. System.out.println("start listening port 8888.");
  16. Socket socket = server.accept();
  17. System.out.println("connect succeed.");
  18. socketList.add(socket);
  19. //每当客户端连接之后启动一条ServerThread线程为该客户端服务
  20. new Thread(new MyServerRunnable(socket)).start();
  21. }
  22. }
  23. public static class MyServerRunnable implements Runnable {
  24. //定义当前线程处理的Socket
  25. Socket socket = null;
  26. //该线程所处理的Socket所对应的输入流
  27. BufferedReader bufferedReader = null;
  28. public MyServerRunnable(Socket socket){
  29. this.socket = socket;
  30. try {
  31. //将服务器端的输入流的数据放入读Buffer中
  32. bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
  33. }catch (IOException e){
  34. e.printStackTrace();
  35. }
  36. }
  37. public void run(){
  38. String content = null;
  39. //采用循环不断的从Socket中读取客户端发送过来的数据
  40. while ((content = readFromClient()) != null){
  41. //遍历socketList中的每一个Socket,将读取的内容向每个Socket发送一次
  42. for (Socket socket:MyServer.socketList) {
  43. OutputStream outputStream;
  44. try {
  45. outputStream = socket.getOutputStream();
  46. outputStream.write((content+"\n").getBytes("utf-8"));
  47. }catch (IOException e){
  48. e.printStackTrace();
  49. }
  50. }
  51. }
  52. }
  53. // 定义读取客户端的信息
  54. public String readFromClient() {
  55. try {
  56. return bufferedReader.readLine();
  57. } catch (Exception e) {
  58. e.printStackTrace();
  59. }
  60. return null;
  61. }
  62. }
  63. }
客户端——Android代码
AndroidManifest.xml——主要添加网络权限<uses-permission android:name="android.permission.INTERNET"/>
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
  3. package="com.example.socket"
  4. android:versionCode="1"
  5. android:versionName="1.0" >
  6. <uses-sdk
  7. android:minSdkVersion="8"
  8. android:targetSdkVersion="19" />
  9. <uses-permission android:name="android.permission.INTERNET"/>
  10. <application
  11. android:allowBackup="true"
  12. android:icon="@drawable/ic_launcher"
  13. android:label="@string/app_name"
  14. android:theme="@style/AppTheme" >
  15. <activity
  16. android:name="com.example.socket.MainActivity"
  17. android:label="@string/app_name" >
  18. <intent-filter>
  19. <action android:name="android.intent.action.MAIN" />
  20. <category android:name="android.intent.category.LAUNCHER" />
  21. </intent-filter>
  22. </activity>
  23. </application>
  24. </manifest>
activity_main.xml
  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. xmlns:tools="http://schemas.android.com/tools"
  3. android:layout_width="match_parent"
  4. android:layout_height="match_parent"
  5. android:gravity="center"
  6. android:orientation="vertical"
  7. tools:context=".MainActivity" >
  8. <TextView
  9. android:id="@+id/show"
  10. android:layout_width="wrap_content"
  11. android:layout_height="wrap_content"
  12. android:text="显示接收到服务器端数据"/>
  13. <Button
  14. android:id="@+id/send"
  15. android:layout_width="match_parent"
  16. android:layout_height="wrap_content"
  17. android:text="发送数据到服务器端"/>
  18. </LinearLayout>
ClientThread.java
  1. package com.example.socket;
  2. import java.io.BufferedReader;
  3. import java.io.IOException;
  4. import java.io.InputStreamReader;
  5. import java.io.OutputStream;
  6. import java.net.InetSocketAddress;
  7. import java.net.Socket;
  8. import java.net.SocketTimeoutException;
  9. import android.annotation.SuppressLint;
  10. import android.os.Handler;
  11. import android.os.Looper;
  12. import android.os.Message;
  13. import android.util.Log;
  14. public class ClientThread implements Runnable {
  15. private Socket s;
  16. // 定义向UI线程发送消息的Handler对象
  17. Handler handler;
  18. // 定义接收UI线程的Handler对象
  19. Handler revHandler;
  20. // 该线程处理Socket所对用的输入输出流
  21. BufferedReader br = null;
  22. OutputStream os = null;
  23. public ClientThread(Handler handler) {
  24. this.handler = handler;
  25. }
  26. @SuppressLint("HandlerLeak") @Override
  27. public void run() {
  28. // TODO Auto-generated method stub
  29. try {
  30. s = new Socket();
  31. Log.d("111111111111", "@@@@@@@@@@@@@@@@@@@@");
  32. // s = new Socket("192.168.0.78", 8888);//此方法不能设定连接时限
  33. s.connect(new InetSocketAddress("192.168.0.78", 8888), 5000);
  34. Log.d("111111111111", "$$$$$$$$$$$$$$$$$$");
  35. br = new BufferedReader(new InputStreamReader(s.getInputStream()));
  36. os = s.getOutputStream();
  37. // 启动一条子线程来读取服务器相应的数据
  38. new Thread() {
  39. @Override
  40. public void run() {
  41. String content = null;
  42. // 不断的读取Socket输入流的内容
  43. try {
  44. while ((content = br.readLine()) != null) {
  45. // 每当读取到来自服务器的数据之后,发送的消息通知程序
  46. // 界面显示该数据
  47. Message msg = new Message();
  48. msg.what = 0x123;
  49. msg.obj = content;
  50. handler.sendMessage(msg);
  51. Log.d("111111111111", content);
  52. }
  53. } catch (IOException io) {
  54. io.printStackTrace();
  55. }
  56. }
  57. }.start();
  58. // 为当前线程初始化Looper
  59. Looper.prepare();
  60. // 创建revHandler对象
  61. revHandler = new Handler() {
  62. @Override
  63. public void handleMessage(Message msg) {
  64. // 接收到UI线程的中用户输入的数据
  65. if (msg.what == 0x345) {
  66. // 将用户在文本框输入的内容写入网络
  67. try {
  68. os.write((msg.obj.toString() + "\r\n")
  69. .getBytes("utf-8"));
  70. } catch (Exception e) {
  71. e.printStackTrace();
  72. }
  73. }
  74. }
  75. };
  76. // 启动Looper
  77. Looper.loop();
  78. } catch (SocketTimeoutException e) {
  79. Message msg = new Message();
  80. msg.what = 0x123;
  81. msg.obj = "网络连接超时!";
  82. handler.sendMessage(msg);
  83. } catch (IOException io) {
  84. io.printStackTrace();
  85. }
  86. }
  87. }
MainActivity.java
  1. package com.example.socket;
  2. import android.annotation.SuppressLint;
  3. import android.app.Activity;
  4. import android.os.Bundle;
  5. import android.os.Handler;
  6. import android.os.Message;
  7. import android.view.View;
  8. import android.widget.Button;
  9. import android.widget.TextView;
  10. public class MainActivity extends Activity {
  11. Handler handler;
  12. // 定义与服务器通信的子线程
  13. ClientThread clientThread;
  14. TextView show;
  15. Button send;
  16. @SuppressLint("HandlerLeak") @Override
  17. protected void onCreate(Bundle savedInstanceState) {
  18. super.onCreate(savedInstanceState);
  19. setContentView(R.layout.activity_main);
  20. show = (TextView)this.findViewById(R.id.show);
  21. send = (Button)this.findViewById(R.id.send);
  22. send.setOnClickListener(new View.OnClickListener() {
  23. @Override
  24. public void onClick(View arg0) {
  25. // TODO Auto-generated method stub
  26. try {
  27. // 当用户按下按钮之后,将用户输入的数据封装成Message
  28. // 然后发送给子线程Handler
  29. Message msg = new Message();
  30. msg.what = 0x345;
  31. msg.obj = "Android 网络编程--Socket通信编程";
  32. clientThread.revHandler.sendMessage(msg);
  33. } catch (Exception e) {
  34. e.printStackTrace();
  35. }
  36. }
  37. });
  38. handler = new Handler(){
  39. @Override
  40. public void handleMessage(Message msg) {
  41. // 如果消息来自子线程
  42. if (msg.what == 0x123) {
  43. // 将读取的内容追加显示在文本框中
  44. show.append("\n" + msg.obj.toString());
  45. show.setTextSize(50);
  46. }
  47. }
  48. };
  49. clientThread = new ClientThread(handler);
  50. // 客户端启动ClientThread线程创建网络连接、读取来自服务器的数据
  51. new Thread(clientThread).start();
  52. }
  53. }
测试
先运行服务器端,再运行客户端。点击客户端的【发送数据到服务器】,服务器端接收到数据后将接收到的数据发送给客户端,客户端显示结果如下图所示:


总结
1、服务器端监听阻塞,accept一直阻塞到建立连接成功
2、客服端创建Socket对象阻塞,一直阻塞到建立连接成功
3、客服端与服务器端通过Socket的输入流进行接收数据,输出流进行发送数据。通信流程图如下图所示




声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Cpp五条/article/detail/272005
推荐阅读
相关标签
  

闽ICP备14008679号