前言

Socket的使用在 Android网络编程中非常重要

今天我将带大家全面了解 Socket 及 其使用方法

目录

示意图

1.网络基础

阅读本文前,请先了解 关于计算机网络基础,如计算机体系结构、TCP、UDP等知识

2. Socket定义

即套接字,是应用层 与 TCP/IP 协议族通信的中间软件抽象层,表现为一个封装了 TCP / IP协议族 的编程接口(API)

示意图

Socket不是一种协议,而是一个编程调用接口(API),属于传输层(主要解决数据如何在网络中传输)

即:通过Socket,我们才能在Andorid平台上通过 TCP/IP协议进行开发

对用户来说,只需调用Socket去组织数据,以符合指定的协议,即可通信

成对出现,一对套接字:

Socket ={(IP地址1:PORT端口号),(IP地址2:PORT端口号)}

一个 Socket 实例 唯一代表一个主机上的一个应用程序的通信链路

3. 建立Socket连接过程

示意图

4. 原理

Socket的使用类型主要有两种:

流套接字(streamsocket) :基于 TCP协议,采用 流的方式 提供可靠的字节流服务

数据报套接字(datagramsocket):基于 UDP协议,采用 数据报文 提供数据打包发送的服务

具体原理图如下:

原理图

5. Socket 与 Http 对比

Socket属于传输层,因为 TCP / IP协议属于传输层,解决的是数据如何在网络中传输的问题

HTTP协议 属于 应用层,解决的是如何包装数据

由于二者不属于同一层面,所以本来是没有可比性的。但随着发展,默认的Http里封装了下面几层的使用,所以才会出现Socket & HTTP协议的对比:(主要是工作方式的不同):

Http:采用 请求—响应 方式。

即建立网络连接后,当 客户端 向 服务器 发送请求后,服务器端才能向客户端返回数据。

可理解为:是客户端有需要才进行通信

Socket:采用 服务器主动发送数据 的方式

即建立网络连接后,服务器可主动发送消息给客户端,而不需要由客户端向服务器发送请求

可理解为:是服务器端有需要才进行通信

6. 使用步骤

Socket可基于TCP或者UDP协议,但TCP更加常用

所以下面的使用步骤 & 实例的Socket将基于TCP协议

// 步骤1:创建客户端 & 服务器的连接

// 创建Socket对象 & 指定服务端的IP及端口号

Socket socket = new Socket("192.168.1.32", 1989);

// 判断客户端和服务器是否连接成功

socket.isConnected());

// 步骤2:客户端 & 服务器 通信

// 通信包括:客户端 接收服务器的数据 & 发送数据 到 服务器

// 步骤1:创建输入流对象InputStream

InputStream is = socket.getInputStream()

// 步骤2:创建输入流读取器对象 并传入输入流对象

// 该对象作用:获取服务器返回的数据

InputStreamReader isr = new InputStreamReader(is);

BufferedReader br = new BufferedReader(isr);

// 步骤3:通过输入流读取器对象 接收服务器发送过来的数据

br.readLine();

// 步骤1:从Socket 获得输出流对象OutputStream

// 该对象作用:发送数据

OutputStream outputStream = socket.getOutputStream();

// 步骤2:写入需要发送的数据到输出流对象中

outputStream.write(("Carson_Ho"+"\n").getBytes("utf-8"));

// 特别注意:数据的结尾加上换行符才可让服务器端的readline()停止阻塞

// 步骤3:发送数据到服务端

outputStream.flush();

// 步骤3:断开客户端 & 服务器 连接

os.close();

// 断开 客户端发送到服务器 的连接,即关闭输出流对象OutputStream

br.close();

// 断开 服务器发送到客户端 的连接,即关闭输入流读取器对象BufferedReader

socket.close();

// 最终关闭整个Socket连接

7. 具体实例

实例 Demo 代码包括:客户端 & 服务器

本文着重讲解客户端,服务器仅采用最简单的写法进行展示

7.1 客户端 实现

步骤1:加入网络权限

步骤2:主布局界面设置

包括创建Socket连接、客户端 & 服务器通信的按钮

android:id="@+id/connect"

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:text="connect" />

android:id="@+id/disconnect"

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:text="disconnect" />

android:id="@+id/receive_message"

android:layout_width="match_parent"

android:layout_height="wrap_content" />

android:id="@+id/Receive"

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:text="Receive from message" />

android:id="@+id/edit"

android:layout_width="match_parent"

android:layout_height="wrap_content" />

android:id="@+id/send"

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:text="send"/>

步骤3:创建Socket连接、客户端 & 服务器通信

具体请看注释

MainActivity.java

package scut.carson_ho.socket_carson;

import android.os.Bundle;

import android.os.Handler;

import android.os.Message;

import android.support.v7.app.AppCompatActivity;

import android.view.View;

import android.widget.Button;

import android.widget.EditText;

import android.widget.TextView;

import java.io.BufferedReader;

import java.io.IOException;

import java.io.InputStream;

import java.io.InputStreamReader;

import java.io.OutputStream;

import java.net.Socket;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

public class MainActivity extends AppCompatActivity {

/**

* 主 变量

*/

// 主线程Handler

// 用于将从服务器获取的消息显示出来

private Handler mMainHandler;

// Socket变量

private Socket socket;

// 线程池

// 为了方便展示,此处直接采用线程池进行线程管理,而没有一个个开线程

private ExecutorService mThreadPool;

/**

* 接收服务器消息 变量

*/

// 输入流对象

InputStream is;

// 输入流读取器对象

InputStreamReader isr ;

BufferedReader br ;

// 接收服务器发送过来的消息

String response;

/**

* 发送消息到服务器 变量

*/

// 输出流对象

OutputStream outputStream;

/**

* 按钮 变量

*/

// 连接 断开连接 发送数据到服务器 的按钮变量

private Button btnConnect, btnDisconnect, btnSend;

// 显示接收服务器消息 按钮

private TextView Receive,receive_message;

// 输入需要发送的消息 输入框

private EditText mEdit;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

/**

* 初始化操作

*/

// 初始化所有按钮

btnConnect = (Button) findViewById(R.id.connect);

btnDisconnect = (Button) findViewById(R.id.disconnect);

btnSend = (Button) findViewById(R.id.send);

mEdit = (EditText) findViewById(R.id.edit);

receive_message = (TextView) findViewById(R.id.receive_message);

Receive = (Button) findViewById(R.id.Receive);

// 初始化线程池

mThreadPool = Executors.newCachedThreadPool();

// 实例化主线程,用于更新接收过来的消息

mMainHandler = new Handler() {

@Override

public void handleMessage(Message msg) {

switch (msg.what) {

case 0:

receive_message.setText(response);

break;

}

}

};

/**

* 创建客户端 & 服务器的连接

*/

btnConnect.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

// 利用线程池直接开启一个线程 & 执行该线程

mThreadPool.execute(new Runnable() {

@Override

public void run() {

try {

// 创建Socket对象 & 指定服务端的IP 及 端口号

socket = new Socket("192.168.1.172", 8989);

// 判断客户端和服务器是否连接成功

System.out.println(socket.isConnected());

} catch (IOException e) {

e.printStackTrace();

}

}

});

}

});

/**

* 接收 服务器消息

*/

Receive.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

// 利用线程池直接开启一个线程 & 执行该线程

mThreadPool.execute(new Runnable() {

@Override

public void run() {

try {

// 步骤1:创建输入流对象InputStream

is = socket.getInputStream();

// 步骤2:创建输入流读取器对象 并传入输入流对象

// 该对象作用:获取服务器返回的数据

isr = new InputStreamReader(is);

br = new BufferedReader(isr);

// 步骤3:通过输入流读取器对象 接收服务器发送过来的数据

response = br.readLine();

// 步骤4:通知主线程,将接收的消息显示到界面

Message msg = Message.obtain();

msg.what = 0;

mMainHandler.sendMessage(msg);

} catch (IOException e) {

e.printStackTrace();

}

}

});

}

});

/**

* 发送消息 给 服务器

*/

btnSend.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

// 利用线程池直接开启一个线程 & 执行该线程

mThreadPool.execute(new Runnable() {

@Override

public void run() {

try {

// 步骤1:从Socket 获得输出流对象OutputStream

// 该对象作用:发送数据

outputStream = socket.getOutputStream();

// 步骤2:写入需要发送的数据到输出流对象中

outputStream.write((mEdit.getText().toString()+"\n").getBytes("utf-8"));

// 特别注意:数据的结尾加上换行符才可让服务器端的readline()停止阻塞

// 步骤3:发送数据到服务端

outputStream.flush();

} catch (IOException e) {

e.printStackTrace();

}

}

});

}

});

/**

* 断开客户端 & 服务器的连接

*/

btnDisconnect.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

try {

// 断开 客户端发送到服务器 的连接,即关闭输出流对象OutputStream

outputStream.close();

// 断开 服务器发送到客户端 的连接,即关闭输入流读取器对象BufferedReader

br.close();

// 最终关闭整个Socket连接

socket.close();

// 判断客户端和服务器是否已经断开连接

System.out.println(socket.isConnected());

} catch (IOException e) {

e.printStackTrace();

}

}

});

}

}

7.2 服务器 实现

因本文主要讲解客户端,所以服务器仅仅是为了配合客户端展示;

为了简化服务器使用,此处采用Mina框架

服务器代码请在eclipse平台运行

按照我的步骤一步步实现就可以无脑运行了

步骤1:导入Mina包

示意图

步骤2:创建服务器线程

TestHandler.java

package mina;

// 导入包

public class TestHandler extends IoHandlerAdapter {

@Override

public void exceptionCaught(IoSession session, Throwable cause) throws Exception {

System.out.println("exceptionCaught: " + cause);

}

@Override

public void messageReceived(IoSession session, Object message) throws Exception {

System.out.println("recieve : " + (String) message);

session.write("hello I am server");

}

@Override

public void messageSent(IoSession session, Object message) throws Exception {

}

@Override

public void sessionClosed(IoSession session) throws Exception {

System.out.println("sessionClosed");

}

@Override

public void sessionOpened(IoSession session) throws Exception {

System.out.println("sessionOpen");

}

@Override

public void sessionIdle(IoSession session, IdleStatus status) throws Exception {

}

}

步骤3:创建服务器主代码

TestHandler.java

package mina;

import java.io.IOException;

import java.net.InetSocketAddress;

import org.apache.mina.filter.codec.ProtocolCodecFilter;

import org.apache.mina.filter.codec.textline.TextLineCodecFactory;

import org.apache.mina.transport.socket.nio.NioSocketAcceptor;

public class TestServer {

public static void main(String[] args) {

NioSocketAcceptor acceptor = null;

try {

acceptor = new NioSocketAcceptor();

acceptor.setHandler(new TestHandler());

acceptor.getFilterChain().addLast("mFilter", new ProtocolCodecFilter(new TextLineCodecFactory()));

acceptor.setReuseAddress(true);

acceptor.bind(new InetSocketAddress(8989));

} catch (Exception e) {

e.printStackTrace();

}

}

}

至此,客户端 & 服务器的代码均实现完毕。

7.3 测试结果

点击 Connect按钮: 连接成功

示意图

输入发送的消息,点击 Send 按钮发送

示意图

服务器接收到客户端发送的消息

示意图

点击 Receive From Message按钮,客户端 读取 服务器返回的消息

示意图

点击 DisConnect按钮,断开 客户端 & 服务器的连接

客户端示意图

服务器示意图

7.4 源码地址

8. 总结

相信大家已经非常了解关于Socket的使用

下面我将继续对 Android 的网络编程进行讲解,感兴趣的同学可以继续关注本人运营的Wechat Public Account:

请点赞!因为你的鼓励是我写作的最大动力!

不定期分享关于安卓开发的干货,追求短、平、快,但却不缺深度。

java socket android_Android:这是一份很详细的Socket使用攻略相关推荐

  1. Carson带你学Android:这是一份全面详细的WebView学习攻略

    前言 现在很多App里都内置了Web网页(Hybrid App),比如说很多电商平台,淘宝.京东.聚划算等等,如下图 那么这种该如何实现呢?其实这是Android里一个叫WebView组件实现 今天, ...

  2. Android 系统(202)---Android:这是一份全面 amp; 详细的Webview使用攻略

    Android:这是一份全面 & 详细的Webview使用攻略 前言 现在很多App里都内置了Web网页(Hybrid App),比如说很多电商平台,淘宝.京东.聚划算等等,如下图 京东首页 ...

  3. 调参到头秃?你需要这份自动超参搜索技术攻略

    点击上方,选择星标或置顶,不定期资源大放送! 阅读大概需要13分钟Follow小博主,每天更新前沿干货 来源:PaperWeekly本文约4845字,建议阅读9分钟本文介绍了自动超参搜索的系统架构及技 ...

  4. java和智神_动漫界和智神、攻略之神、梨神被称神的动漫人物,你知道还有谁?...

    看前先收藏,不秃也变强!看后点个赞,先挣一千万! 动漫界和智神.攻略之神.梨神被称神的动漫人物,你知道还有谁? 在我们平时看的动漫里,除了那些剧情不咋样,主角开挂到最后的动漫外,还有一些被很多动漫迷们 ...

  5. 收下这份十万商家称赞的开店攻略,带你发家致富!

    理想与现实之间的距离,大概就是开店吧! 总觉得自己投点钱,一两年回本,后面每月轻松赚几万.几十万:结果却发现房租太贵.人工太贵.自己什么都不懂,然后随波逐流的没有特色. 其实,细心的朋友会发现路边的门 ...

  6. js 中转换成list集合_程序员:java集合介绍-List,具说很详细,你不来看看?

    Java集合介绍 作为一个程序猿,Java集合类可以说是我们在工作中运用最多.最频繁的类.相比于数组(Array)来说,集合类的长度可变,更加方便开发. Java集合就像一个容器,可以存储任何类型的数 ...

  7. java中的几种锁(很详细)-小白收藏

    最近学习java中的几种锁,看到比较详细的一篇,先转发,后续补充自己的见解 其实如果按照名称来说,锁大概有以下名词:  自旋锁 ,自旋锁的其他种类,阻塞锁,可重入锁 ,读写锁 ,互斥锁 ,悲观锁 ,乐 ...

  8. java波斯王子武者之心,波斯王子2:武者之心通关攻略

    键盘常用键:1.方向键W.S.A.D.2.左右键.空格.E.C.R 3.很有用的视图键:Q.F. 一.王子出招表(完全版) ①.右手: 单击左键:普通单击 双击左键:普通两连击 三击左键:普通三连击 ...

  9. java基础热门侠客养成_《世界》侠客基础养成攻略

    在<世界>里,侠客这个职业作为高爆发的物理输出英雄,其拥有超群的群体输出能力,在整个游戏里无论是pk还是刷怪刷boss都起到一个十分给力的作用.那如何玩好侠客?小编今天就跟大家共同讨论一下 ...

最新文章

  1. 【人工智能工程师】掌握这10个项目,秒杀90%面试者!
  2. Web 开发学习笔记(6) --- 前端开发之 HTML5
  3. 江边上洗萝卜——一个个来
  4. 框架原理第一讲,熟悉常用的设计方式.(以MFC框架讲解)
  5. java的String构造对象的几种方法以及内存运行过程
  6. asp.net 读取excel文件的一些方法,NPOI方法
  7. 电子邮件地址验证:详细解释,生产质量WPF文本框代码
  8. 【Win 10应用开发】Adaptive磁贴模板的XML文档结构
  9. linux 查看是否有led设备,linux驱动开发--字符设备:通过cdd_cdev结构中的led变量区分是哪个节点,private_data使用...
  10. python绘制3d动态模型_给大家介绍一个python三维动画制作库,数学作图,数据可视化建模...
  11. sqlyog简单入门使用
  12. eviews建立时间序列模型_模型建立——时间序列 eviews协整检验(EG两步法(Engle-Granger))...
  13. 百度下拉词目前用软件工具可以刷出来吗?
  14. oracle设置默认角色,oracle的用户和角色管理
  15. 抖音特效专场PR模板 Premiere视频转场过渡快速切换画面视频模板下载
  16. hashcat详细使用教程
  17. flea-cache使用之整合Memcached和Redis接入
  18. java 数据周期预测_预测算法 | Holt Winter季节性指数平滑法 附JAVA代码
  19. 靠窗座位订票技能和退票省钱技能
  20. uni-app 启动广告页

热门文章

  1. mac删除android sd卡,如何从mac完全删除android及其所有文件?
  2. lvm 扩展根目录_转://如何增加linux根目录的磁盘空间(基于LVM)?
  3. fullgc频繁的原因_系统运行缓慢,CPU 100%,Full GC次数过多,这一招帮你全搞定
  4. Ubuntu 12.04 静态ip的设置方法
  5. location.href属于重定向还是转发_servlet2 单元测试、转发、重定向
  6. pycharm怎么编写python代码_如何设置PyCharm中的Python代码模版(推荐)
  7. 下面哪个字段是http请求中必须具备的_理解HTTP协议-HTTP协议详解总结
  8. python垃圾邮件识别_【Python】垃圾邮件识别
  9. linux串口编程实例_Linux 网络编程——原始套接字实例:发送 UDP 数据包
  10. 使用优化的基于模糊规则的特征选择技术和基于树的集成方法进行山洪敏感性建模--文献阅读