Android Socket

参考资料

菜鸟教程

怎么理解TCP的面向连接和UDP的无连接

https://www.cnblogs.com/xiaomayizoe/p/5258754.html

https://www.cnblogs.com/qifengshi/p/6602881.html

Overview

Socket 一词起源于UNIX 操作系统,UNIX 系统中将IP + 端口号 的通信的方式称之为Socket 中文译为套接字 ,在学习Socket的时候不要太过于纠结它的名字,毕竟只是个代号。

TCP和UDP

首先要想比较容易的理解TCP和UDP协议,首先得对整体的网络协议框架有一个整体的了解,并不需要多清楚,只要把握整体就好了,这里我推荐 阮一峰 阮老师的博客 网络协议入门1 , 网络协议入门2

这里的TCP 协议不等于TCP/IP协议族 ,这一点不要弄混。

首先呢UDP 是先与TCP 出现的,但是UDP协议是存在着一些“问题”的,所有后来就有了更为复杂的TCP协议。

UDP(User Datagram Protocol)协议

如果已经知道对方的IP端口号 ,通过UDP协议我们是已经可以实现,不同的计算机的程序之间的通信了,但是UDP协议还存在着一个“问题”。

  • UDP协议是一种无连接的协议(发送数据之前无需建立连接),不能确保服务器是否收到了发送的数据,这就不能保证数据的完成性,当服务器高负载的情况下很容易出现。
  • UDP协议无法保证发送数据的顺序

比如说,通过UDP协议向服务器发送了如下的数据

11

22

33

但是服务器可能接收到的是

33

11

22

但是这种,情况一般是在网络拥堵的情况下才会出现。

TCP(Transmission Control Protocol) 协议

因为UDP的一些“弊端”,所以TCP协议出现了,TCP协议,面向连接,发送数据有顺序,不会造成丢包,但是一些代价也随之而来。TCP与UDP相比消耗的资源较多。

TCP 三次握手

https://www.cnblogs.com/qifengshi/p/6602881.html

总结

当对数据的完整性非常高的的时候,需要采用TCP协议传输,比如传输文件 ,发送邮件

当对数据完成行要求不高的时候,可以采用UDP协议,比如视频通话

UDP 和 TCP 没有绝对的好与坏,之后谁更适用。

InetAddress

This class represents an Internet Protocol (IP) address.

该类代表着一个IP地址.

/**
* 获取本机信息
* 主机名
* IP地址
*/
private void getLocalHostInfo() {
try {
InetAddress inetAddress = InetAddress.getLocalHost();
String hostName = inetAddress.getHostName();
String ipAddress = inetAddress.getHostAddress();
System.out.printf("HostName:%s IP Address:%s", hostName, ipAddress);
} catch (UnknownHostException e) {
e.printStackTrace();
}
}

基于TCP的Socket

一个DEMO

通过Socket实现文件上传到服务器的功能,上传功能具有断点续传机制。

效果图如下

实现思路如下

首先,需要在Android 的 Assets 目录下,放入一个名为tmep.flv 的视频文件,

客户端实现思路
  1. 建立与服务端的连接
  2. 选择要上传的文件
  3. 在写入文件的时候,要先想服务器发送头信息(如: 要上传的文件的名称)
  4. 当暂停的时候释放Socket占用的资源,并且记录已经写入了的长度
  5. 当再次开始的时候,获取已经写入了的长度,在写入的时候通过流的skip 方法,跳过这些流。
  6. 建立新的Socket连接,继续向服务器发送数据

PS: 在写入的时候会更新进度条,来显示写入进度

服务端实现思路
  1. 开启服务等待客户端了连接
  2. 当客户端连接后,首先获取客户端传递过来的头信息
  3. 获取到头信息的文件名后,在本地建立文件并写入
  4. 当客户端暂停后,清空资源,等待客户端再次连接
  5. 当客户端再次连接后,向本地已经存在的文件中追加数据
注意事项
  • 在没有完全地交互完的时候,不要关闭流,如果关闭了流,那么Socket连接也会关闭,会抛出SocketClosed 异常。

代码实现

服务端代码

/**
* 通过Socket来实现上传操作
*/
public class SocketUploadClass implements ICallAble { private ServerSocket serverSocket; //线程池
ExecutorService executorService; @Override
public void call() {
try {
startServer();
} catch (IOException e) {
e.printStackTrace();
}
} /**
* 初始化服务器
*/
private void startServer() throws IOException {
serverSocket = new ServerSocket(9988);
//创建线程池 线程数 = 可用cpu 数量 * 50
this.executorService =
Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 50);
//等待客户端接入
while (true) {
Socket socket = serverSocket.accept();
executorService.execute(new SocketTask(socket));
}
} /**
* 用来处理用户写入的县城类
*/
class SocketTask implements Runnable {
//连接到服务器的线程
Socket socket; public SocketTask(Socket socket) {
this.socket = socket;
} @Override
public void run() {
String ipAddress = socket.getInetAddress().toString();
int port = socket.getPort();
System.out.printf("Client 【%s】 Port:%d 已连接", ipAddress, port); //接收数据
try {
InputStream inputStream = socket.getInputStream();
BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
//读取第一行的头信息
String header = "";
int i;
while ((i = inputStream.read()) != -1) {
if (i == '\r')
break;
header += (char) i;
}
System.out.println(header); //要存储的文件名
String fileName = header.split("=")[1];
//将传递过来的内容写入到本地文件
byte[] buffer = new byte[1024];
FileOutputStream fileOutputStream = new FileOutputStream(fileName, true);
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
int len;
while ((len = bufferedInputStream.read(buffer)) != -1) {
bufferedOutputStream.write(buffer, 0, len);
}
//释放资源
bufferedOutputStream.flush();
bufferedInputStream.close();
bufferedOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

客户端代码

布局文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"> <Button
android:id="@+id/uploadBtn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="upload"
android:text="Update"/> <Button
android:id="@+id/pauseBtn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="pause"
android:text="Pause"/> <ProgressBar
android:id="@+id/processPB"
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>

Activity代码

public class SocketUploadActivity extends CommonActivity {

    private ProgressBar mProgressBar;
private Button mPauseBtn;
private Button mUploadBtn;
private Socket mSocket; //要上传的文件名称
private String fileName = "temp.flv"; //标识是否开始写入
private boolean start = true; private int mCurrentLen; @Override
public int layoutId() {
return R.layout.activity_socket_upload;
} @Override
public void init() {
mUploadBtn = (Button) this.findViewById(R.id.uploadBtn);
mPauseBtn = (Button) this.findViewById(R.id.pauseBtn);
mProgressBar = (ProgressBar) this.findViewById(R.id.processPB);
} /**
* 上传文件
*/
public void upload(View view) {
AssetManager manager = this.getAssets(); new Thread(() -> {
try {
start = true; if (mSocket == null || mSocket.isClosed())
mSocket = new Socket("10.0.2.2", 9988); InputStream inputStream = manager.open(fileName);
//当前已经读取到的总长度
mCurrentLen = getSharedPreferences("socketConfig", MODE_PRIVATE).getInt(fileName, 0);
int totalLen = inputStream.available(); //总长度
int currentRate = 0;
byte[] buffer = new byte[1024];
int len;
//读取文件
OutputStream outputStream = mSocket.getOutputStream();
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream);
//跳过已经传输了的宽度
inputStream.skip(mCurrentLen);
//写入一行头信息,告知服务器一些关于文件的信息,这里仅仅写入了文件的名称,也可以写入其他的
bufferedOutputStream.write(("file=" + fileName + "\r").getBytes("UTF-8")); //当用户没有暂停上传,并且还有数据的时候,写入数据
while (start && (len = inputStream.read(buffer)) != -1) {
bufferedOutputStream.write(buffer, 0, len);
//计算已经写入了的长度
mCurrentLen += len; //计算已经上传了的比例
int rate = (int) Math.floor(mCurrentLen * 1.0 / totalLen * 100); //更新进度,更新进度条不需要返回UI线程,直接更新就行
if (rate > currentRate) {
currentRate = rate;
mProgressBar.setProgress(rate);
L.e("Total Len=: " + totalLen + " Rate:" + rate);
}
}
//释放资源
bufferedOutputStream.flush();
inputStream.close();
bufferedOutputStream.close();
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}).start();
} /**
* 暂停写入,关闭Socket连接,将已经写入的长度进行存储,实现断点续传
*/
public void pause(View view) throws IOException {
start = false;
//为了进行断点续传,将已经写入的数据的长度存储起来
SharedPreferences.Editor editor =
this.getSharedPreferences("socketConfig", MODE_PRIVATE).edit();
editor.putInt(fileName, mCurrentLen);
editor.apply();
mSocket.close();
}
}

检验是否上传成功

看一看我们长传过来的视频是否能够正常播放,如果能够就证明我们上传成功了。

Android Socket的更多相关文章

  1. Android Socket编程学习笔记

    http://blog.csdn.net/eyu8874521/article/details/8847173 度娘给出的描述:通常也称作"套接字",用于描述IP地址和端口,是一个 ...

  2. Protobuf实现Android Socket通讯开发教程

    本节为您介绍Protobuf实现Android Socket通讯开发教程,因此,我们需要先了理一下protobuf 是什么? Protocol buffers是一种编码方法构造的一种有效而可扩展的格式 ...

  3. Android Socket通信详解

    一.Socket通信简介  Android与服务器的通信方式主要有两种,一是Http通信,一是Socket通信.两者的最大差异在于,http连接使用的是“请求—响应方式”,即在请求时建立连接通道,当客 ...

  4. Android Socket编程

    花了大概两天的时间,终于把Android的Socket编程给整明白了.抽空和大家分享一下: Socket Programming on Android Socket 编程基础知识: 主要分服务器端编程 ...

  5. Android Socket 通信

    Android socket 通信 安卓编写Socket客户端,实现连接Socket服务端通信. 创建Socket连接并获取服务端数据 先创建几个全局变量吧 private BufferedWrite ...

  6. Android Socket连接PC出错问题及解决

    最近测试问题:Android 通过Socket链接电脑,ip和端口都是正确的,也在同一网段,可android端就是报异常如下: 解决办法:测试电脑的防火墙可能开着,在控制面板把防火墙打开即可.

  7. android socket 线程连接openwrt与arduino单片机串口双向通信

    package zcd.netanything; import java.io.BufferedReader; import java.io.InputStreamReader; import jav ...

  8. android socket编程用Bufferreader读取的一个失败教训

    由于我的手机需要用笔记本开的wifi,躺在床上玩手机时需要关电脑或者是要让电脑放歌的时候总是不想下床,于是我想能不能用一个APP,然后通过局域网实现在手机上对电脑进行操控呢?说干就干. 我在电脑上用的 ...

  9. 一个Android Socket的例子

    1.开篇简介 Socket本质上就是Java封装了传输层上的TCP协议(注:UDP用的是DatagramSocket类).要实现Socket的传输,需要构建客户端和服务器端.另外,传输的数据可以是字符 ...

随机推荐

  1. Nginx反向代理2--配置文件配置

    2.1Nginx的反向代理 什么是正向代理? 1.2   使用nginx实现反向代理 Nginx只做请求的转发,后台有多个http服务器提供服务,nginx的功能就是把请求转发给后面的服务器,决定把请 ...

  2. bzoj千题计划246:bzoj2242: [SDOI2011]计算器

    http://www.lydsy.com/JudgeOnline/problem.php?id=2242 #include<map> #include<cmath> #incl ...

  3. bzoj千题计划205:bzoj3529: [Sdoi2014]数表

    http://www.lydsy.com/JudgeOnline/problem.php?id=3529 有一张n*m的数表,其第i行第j列(1 < =i < =n,1 < =j & ...

  4. WebSockets Tutorial(教程一)WebSockets简介

    一.WebSockets简介 以字面意思来说,握手可以被定义为两个人抓住和握手右手,象征着问候,祝贺,同意或告别.在计算机科学中,握手是确保服务器与客户端同步的过程.握手是Web Socket协议的基 ...

  5. linux解压

    tar –xvf file.tar //解压 tar包tar -xzvf file.tar.gz //解压tar.gztar -xjvf file.tar.bz2 //解压 tar.bz2tar –x ...

  6. SSH 公钥登录

    一般使用SSH进行远程登录时需要提供密码,这也是我们所熟知的一种方式. 另外,就是通过公钥登录的方式,本文将简要介绍公钥登录的两种方法,建议使用方法二.本文也将简单演示公钥登录过程,以及强制使用公钥和 ...

  7. Git常见错误处理

      如果输入$ Git remote add origin git@github.com:djqiang(github帐号名)/gitdemo(项目名).git  提示出错信息:fatal: remo ...

  8. Dream_Spark定制第二课

    Spark版本定制第2天:通过案例对SparkStreaming透彻理解之二 本期内容: 1 解密Spark Streaming运行机制 2 解密Spark Streaming架构 一切不能进行实时流 ...

  9. Number of Islands I & II

    Given a boolean 2D matrix, find the number of islands. Notice 0 is represented as the sea, 1 is repr ...

  10. cancel_delayed_work和flush_scheduled_work【转】

    转自:http://blog.chinaunix.net/uid-9688646-id-4052595.html 是不是觉得很玄?像思念一样玄?那好,我们来看点具体的,比如935行,INIT_DELA ...