Android学习笔记037之基于TCP的socket通信

news/2024/5/17 18:00:22 标签: android, socket, tcp, 网络, 通信

  我们说过Android中客户端与服务端通信有两种方式,HTTP通信和Socket通信,前面我们介绍过HTTP通信了,现在,我们来学习一下Socket通信。学习Socket之前,我们需要先学习一下TCP/IP协议和UDP协议。

socket基本通信模型">1、Socket基本通信模型

  我们需要学习的有两种网络通信参考模型,分别是TCP/IP参考模型和OSI参考模型,下面我们分别学习一下这两种参考模型:

tcpip参考模型">TCP/IP参考模型

  TCP/IP参考模型是计算机网络的祖父ARPANET和其后继的因特网使用的参考模型。TCP/IP参考模型将协议分为以下的四个层次:

  • 应用层:主要为用户提供各种服务,例如:Telnet、FTP、DNS等,数据传输单位是:数据段

  • 传输层:应用层实体提供端到端的通信功能,保证了数据包的顺序传送及数据的完整性。该层定义了两个主要的协议:传输控制协议(TCP)和用户数据报协议(UDP).数据传输单位是:数据包

  • 网际互联层:主要解决主机到主机的通信问题。它所包含的协议设计数据包在整个网络上的逻辑传输。注重重新赋予主机一个IP地址来完成对主机的寻址,它还负责数据包在多种网络中的路由。该层有三个主要协议:网际协议(IP)、互联网组管理协议(IGMP)和互联网控制报文协议(ICMP)。数据传输单位是:帧

  • 网络接入层:负责监视数据在主机和网络之间的交换。事实上,TCP/IP本身并未定义该层的协议,而由参与互连的各网络使用自己的物理层和数据链路层协议,然后与TCP/IP的网络接入层进行连接。数据传输单位是:比特

OSI参考模型

  OSI(Open System Interconnect)开放式系统互联,是ISO(国际标准化组织)组织在1985年研究的网络互联模型。该体系结构标准定义了网络互连的七层框架(物理层、数据链路层、网络层、传输层、会话层、表示层和应用层),即ISO开放系统互连参考模型。

  • 第7层应用层:OSI中的最高层。为特定类型的网络应用提供了访问OSI环境的手段。应用层确定进程之间通信的性质,以满足用户的需要。应用层不仅要提供应用进程所需要的信息交换和远程操作,而且还要作为应用进程的用户代理,来完成一些为进行信息交换所必需的功能。它包括:文件传送访问和管理FTAM、虚拟终端VT、事务处理TP、远程数据库访问RDA、制造报文规范MMS、目录服务DS等协议;应用层能与应用程序界面沟通,以达到展示给用户的目的。 在此常见的协议有:HTTP,HTTPS,FTP,TELNET,SSH,SMTP,POP3等。

  • 第6层表示层:主要用于处理两个通信系统中交换信息的表示方式。为上层用户解决用户信息的语法问题。它包括数据格式交换、数据加密与解密、数据压缩与终端类型的转换。

  • 第5层会话层:在两个节点之间建立端连接。为端系统的应用程序之间提供了对话控制机制。此服务包括建立连接是以全双工还是以半双工的方式进行设置,尽管可以在层4中处理双工方式 ;会话层管理登入和注销过程。它具体管理两个用户和进程之间的对话。如果在某一时刻只允许一个用户执行一项特定的操作,会话层协议就会管理这些操作,如阻止两个用户同时更新数据库中的同一组数据。

  • 第4层传输层:—常规数据递送-面向连接或无连接。为会话层用户提供一个端到端的可靠、透明和优化的数据传输服务机制。包括全双工或半双工、流控制和错误恢复服务;传输层把消息分成若干个分组,并在接收端对它们进行重组。不同的分组可以通过不同的连接传送到主机。这样既能获得较高的带宽,又不影响会话层。在建立连接时传输层可以请求服务质量,该服务质量指定可接受的误码率、延迟量、安全性等参数,还可以实现基于端到端的流量控制功能。

  • 第3层网络层:本层通过寻址来建立两个节点之间的连接,为源端的运输层送来的分组,选择合适的路由和交换节点,正确无误地按照地址传送给目的端的运输层。它包括通过互连网络来路由和中继数据 ;除了选择路由之外,网络层还负责建立和维护连接,控制网络上的拥塞以及在必要的时候生成计费信息。

  • 第2层数据链路层:在此层将数据分帧,并处理流控制。屏蔽物理层,为网络层提供一个数据链路的连接,在一条有可能出差错的物理连接上,进行几乎无差错的数据传输(差错控制)。本层指定拓扑结构并提供硬件寻址。常用设备有网卡、网桥、交换机;

  • 第1层物理层:处于OSI参考模型的最底层。物理层的主要功能是利用物理传输介质为数据链路层提供物理连接,以便透明的传送比特流。常用设备有(各种物理设备)集线器、中继器、调制解调器、网线、双绞线、同轴电缆。

      数据发送时,从第七层传到第一层,接收数据则相反。上三层总称应用层,用来控制软件方面。下四层总称数据流层,用来管理硬件。除了物理层之外其他层都是用软件实现的。数据在发至数据流层的时候将被拆分。在传输层的数据叫段,网络层叫包,数据链路层叫帧,物理层叫比特流,这样的叫法叫PDU(协议数据单元)

两种参考模型比较

两种参考模型比较图

共同点

  • (1)OSI参考模型和TCP/IP参考模型都采用了层次结构的概念。

  • (2)都能够提供面向连接和无连接两种通信服务机制。

不同点

  • (1)OSI采用的七层模型,而TCP/IP是四层结构。

  • (2)TCP/IP参考模型的网络接口层实际上并没有真正的定义,只是一些概念性的描述。而OSI参考模型不仅分了两层,而且每一层的功能都很详尽,甚至在数据链路层又分出一个介质访问子层,专门解决局域网的共享介质问题。

  • (3)OSI模型是在协议开发前设计的,具有通用性。TCP/IP是先有协议集然后建立模型,不适用于非TCP/IP网络

  • (4)OSI参考模型与TCP/IP参考模型的传输层功能基本相似,都是负责为用户提供真正的端对端的通信服务,也对高层屏蔽了底层网络的实现细节。所不同的是TCP/IP参考模型的传输层是建立在网络互联层基础之上的,而网络互联层只提供无连接的网络服务,所以面向连接的功能完全在TCP协议中实现,当然TCP/IP的传输层还提供无连接的服务,如UDP;相反OSI参考模型的传输层是建立在网络层基础之上的,网络层既提供面向连接的服务,又提供无连接的服务,但传输层只提供面向连接的服务。

  • (5)OSI参考模型的抽象能力高,适合与描述各种网络;而TCP/IP是先有了协议,才制定TCP/IP模型的。

  • (6)OSI参考模型的概念划分清晰,但过于复杂;而TCP/IP参考模型在服务、接口和协议的 区别上不清楚,功能描述和实现细节混在一起。

  • (7)TCP/IP参考模型的网络接口层并不是真正的一层;OSI参考模型的缺点是层次过多,划分意义不大但增加了复杂性。

  • (8)OSI参考模型虽然被看好,由于没把握好时机,技术不成熟,实现困难;相反,TCP/IP参考模型虽然有许多不尽人意的地方,但还是比较成功的。

tcpip协议简介">2、TCP/IP协议简介

  TCP/IP(transmission Control Protocol/Internet Protocol)传输控制协议/互联网络协议,是一种网络通信协议,它规范了网络上的所有通信设备,尤其是一个主机与另一个主机之间的数据往来格式以及传送方式。举个简单的例子就是:TCP和IP就像两个信封,我们把要传输的数据分割开,放入到TCP信封里面,TCP信封里面会记录有分段号;然后将TCP信封装入IP这个大信封里面,传输到网络上。在接收端,一个TCP软件包收集信封,抽出数据,按发送前的顺序还原,并加以校验,若发现差错,TCP将会要求重发。
  

TCP/IP网络连接需要经过三次握手,断开连接需要三次挥手,在这里就不在做很详细的描述了,推荐一篇博文:TCP/IP三次握手与四次挥手 ,这里已经很详细的描述三次握手和四次挥手的过程,建议大家一定要阅读并理解,面试经常会被问到。关于TCP/IP协议就简单介绍到这里,我们做应用开发不是网络工程师,不需要做很深入的学习。

3、UDP协议简介

  前面我们介绍了TCP/IP协议,接着我们介绍一下UDP协议。UDP(User Datagram Protocol)用户数据报协议,是OSI(Open System Interconnection,开放式系统互联) 参考模型中一种无连接的传输层协议,提供面向事务的简单不可靠信息传送服务,IETF RFC 768是UDP的正式规范。UDP在IP报文的协议号是17。

  UDP协议全称是用户数据报协议 ,在网络中它与TCP协议一样用于处理数据包,是一种无连接的协议。在OSI模型中,在第四层——传输层,处于IP协议的上一层。UDP有不提供数据包分组、组装和不能对数据包进行排序的缺点,也就是说,当报文发送之后,是无法得知其是否安全完整到达的。UDP用来支持那些需要在计算机之间传输数据的网络应用。包括网络视频会议系统在内的众多的客户/服务器模式的网络应用都需要使用UDP协议。UDP协议从问世至今已经被使用了很多年,虽然其最初的光彩已经被一些类似协议所掩盖,但是即使是在今天UDP仍然不失为一项非常实用和可行的网络传输层协议。

  与所熟知的TCP(传输控制协议)协议一样,UDP协议直接位于IP(网际协议)协议的顶层。根据OSI(开放系统互连)参考模型,UDP和TCP都属于传输层协议。UDP协议的主要作用是将网络数据流量压缩成数据包的形式。一个典型的数据包就是一个二进制数据的传输单位。每一个数据包的前8个字节用来包含报头信息,剩余字节则用来包含具体的传输数据。

socket基本概念">4、Socket基本概念

  前面介绍的都是概念性的东西,非常枯燥,下面正式开始学习基于TCP的Socket通信

  什么是Socket?称作”套接字”,用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。在Internet上的主机一般运行了多个服务软件,同时提供几种服务。每种服务都打开一个Socket,并绑定到一个端口上,不同的端口对应于不同的服务。Socket通常用来实现客户方和服务方的连接。Socket是TCP/IP协议的一个十分流行的编程界面,一个Socket由一个IP地址和一个端口号唯一确定。

Socket通信模型

socket的通信流程">5、Socket的通信流程

  Server端Listen(监听)某个端口是否有连接请求,Client端向Server 端发出Connect(连接)请求,Server端向Client端发回Accept(接受)消息。一个连接就建立起来了。Server端和Client 端都可以通过Send,Write等方法与对方通信

对于一个功能齐全的Socket,都要包含以下基本结构,其工作过程包含以下四个基本的步骤:

  • 1、创建ServerSocket和Socket

  • 2、打开连接到的Socket的输入/输出流

  • 3、按照协议对Socket进行读/写操作

  • 4、关闭输入输出流,以及Socket

socket搭建简单的聊天室">6、Socket搭建简单的聊天室

  下面我们通过Socket搭建一个简单聊天室来体会一下Socket通信

首先是服务端的编写,服务端编写步骤:

  • 创建ServerSocket对象,绑定监听的端口

  • 通过调用accept()方法监听客户端的请求

  • 建立连接之后,通过输入流读取客户端的请求信息

  • 获取输出流向客户端输出信息

  • 关闭连接

客户端的编写步骤:

  • 创建Socket对象,指明需要连接的服务器的地址和端号

  • 连接建立后,通过输出流向服务器发送请求信息

  • 通过输出流获取服务器响应的信息

  • 关闭连接,释放相关资源

具体实例代码:

服务端:

package com.example.socket;

import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Created by Devin on 2016/7/28.
 */
public class SocketServer {
public static boolean isServerStart = true;
private ServerSocket serverSocket;
private static int LISTENER_PART = 10010;
public List<Socket> socketList = new ArrayList<>();
private ExecutorService executorService;

public void startServer() {
    try {
        serverSocket = new ServerSocket(LISTENER_PART);
        executorService = Executors.newCachedThreadPool();
        InetAddress address = InetAddress.getLocalHost();
        System.out.println("服务器的IP地址是:" + address.getHostAddress() + ",监听的端口号是:" + LISTENER_PART);
        System.out.println("--------------服务器启动,等待连接-----------------");
        Socket socket = null;
        while (isServerStart) {
            System.out.println("有用户登录进来了");
            socket = serverSocket.accept();
            socketList.add(socket);
            executorService.execute(new ServerThread(socket));
        }

        serverSocket.close();
    } catch (Exception e) {
        e.printStackTrace();
    }

}

public static void main(String[] args) {
    SocketServer socketServer = new SocketServer();
    socketServer.startServer();
}

}

客户端:

package com.example.socketdemo;

import android.content.Intent;
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 com.example.socketdemo.activity.FriendsActivity;
import com.example.socketdemo.comm.Constans;
import com.example.socketdemo.comm.ServerConn;
import com.example.socketdemo.comm.ToastUtils;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;

public class MainActivity extends AppCompatActivity {
private EditText et_user_id;
private EditText et_user_name;
private Button btn_user_login;

private Handler mHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        Bundle bundle = msg.getData();
        String isSuccess = bundle.getString("isSuccess");
        int userID = bundle.getInt("userID");
        if (isSuccess.equals("success")) {
            Intent intent = new Intent();
            intent.setClass(MainActivity.this, FriendsActivity.class);
            intent.putExtra("userID", userID);
            startActivity(intent);
        } else {
            ToastUtils.showToast(MainActivity.this, "登录失败");
            return;
        }
    }
};

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    et_user_id = (EditText) findViewById(R.id.et_user_id);
    et_user_name = (EditText) findViewById(R.id.et_user_name);
    btn_user_login = (Button) findViewById(R.id.btn_user_login);
    btn_user_login.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            String userID = et_user_id.getText().toString().trim();
            if (userID != null && !userID.equals("")) {
                login(Integer.parseInt(userID));
            } else {
                ToastUtils.showToast(MainActivity.this, "请输入用户ID");
                return;
            }
        }
    });
}

private void login(int userID) {
    final int id = userID;
    new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                Socket socket = new Socket();
                socket.connect(new InetSocketAddress(Constans.SERVER_ADDRESS, Constans.SERVER_PORT), 5000);
                InetAddress address = InetAddress.getLocalHost();

                PrintWriter writer = new PrintWriter(socket.getOutputStream());
                writer.println(Constans.TAG_LOGIN + ":" + id + ":" + address.getHostAddress());
                writer.flush();


                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                String isSuccess = bufferedReader.readLine();

                ServerConn.mSocket = socket;
                Message message = mHandler.obtainMessage();
                Bundle bundle = new Bundle();
                bundle.putString("isSuccess", isSuccess);
                bundle.putInt("userID", id);
                message.setData(bundle);
                mHandler.sendMessage(message);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }).start();
}
}

package com.example.socketdemo.activity;

import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;

import com.example.socketdemo.R;
import com.example.socketdemo.comm.Constans;
import com.example.socketdemo.comm.OnItemClickListener;
import com.example.socketdemo.domain.UserInfo;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by Devin on 2016/8/1.
 */
public class FriendsActivity extends AppCompatActivity {
private RecyclerView rv_friends;
private FriendsAdapter mAdapter;
private List<UserInfo> mUserInfos;
private int userID;
private FriendHandler mHandler;

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_friends);
    userID = getIntent().getIntExtra("userID", 0);
    mHandler = new FriendHandler();
    rv_friends = (RecyclerView) findViewById(R.id.rv_friends);
    LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
    rv_friends.setLayoutManager(linearLayoutManager);
    rv_friends.setItemAnimator(new DefaultItemAnimator());
    mUserInfos = new ArrayList<>();
    initData(userID);
    mAdapter = new FriendsAdapter(this, mUserInfos);
    rv_friends.setAdapter(mAdapter);
    mAdapter.setOnItemClickListener(new OnItemClickListener() {
        @Override
        public void onItemClick(View view, int position) {
            UserInfo userInfo = mUserInfos.get(position);
            Intent intent = new Intent(FriendsActivity.this, ChatActivity.class);
            intent.putExtra("sendID", userID);
            intent.putExtra("receiveID", userInfo.getUserID());
            intent.putExtra("userName", userInfo.getUserName());
            startActivity(intent);
        }
    });
}

private void initData(final int userID) {
    new Thread(new Runnable() {
        @Override
        public void run() {
            Socket socket = new Socket();
            try {
                socket.connect(new InetSocketAddress(Constans.SERVER_ADDRESS, Constans.SERVER_PORT), 5000);

                PrintWriter writer = new PrintWriter(socket.getOutputStream());
                writer.println(Constans.TAG_FRIENDS + ":" + userID);
                writer.flush();

                BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                String result = reader.readLine();
                System.out.println("-----result---->>"+result);
                Bundle bundle = new Bundle();
                bundle.putString("result", result);
                Message message = mHandler.obtainMessage();
                message.setData(bundle);
                mHandler.sendMessage(message);

            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }).start();
}

private class FriendHandler extends Handler {
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        Bundle bundle = msg.getData();
        String result = bundle.getString("result");
        List<UserInfo> infos = new ArrayList<>();
        try {
            JSONArray jsonArray = new JSONArray(result);
            for (int i = 0; i < jsonArray.length(); i++) {
                JSONObject jsonObject = jsonArray.getJSONObject(i);
                UserInfo userInfo = new UserInfo();
                userInfo.setUserID(jsonObject.getInt("userID"));
                userInfo.setUserIcon(jsonObject.getInt("userIcon"));
                userInfo.setUserName(jsonObject.getString("userName"));
                userInfo.setUserSign(jsonObject.getString("userSign"));
                userInfo.setUserState(jsonObject.getInt("userState"));
                infos.add(userInfo);
            }
        } catch (JSONException e) {
            e.printStackTrace();
        }
        mAdapter.addList(infos);
    }
}
}

实现效果图

这样可以实现简单的Socket通信,当然只是很简单的应用。


http://www.niftyadmin.cn/n/1167988.html

相关文章

Android学习笔记038之WebView网页视图

前面我们介绍过了HTTP协议和Socket&#xff0c;这一篇我们来介绍一下Android的一个网络控件&#xff1a;WebView-网页视图。我们知道&#xff0c;现在移动端有两种开发方向&#xff1a;原生开发和H5移动端开发。 所谓的H5移动端开发就是通过HTML5CSSJS来构建一个网页版的移动应…

多项式简明百科全书

\(0\)、写在前面 为了方便本文的描述&#xff0c;定义如下内容&#xff1a; 我们说\(f\)是一个次数界为\(n\)&#xff08;下文不严谨地简称为\(n\)次&#xff09;的多项式&#xff0c;是指\(f\)是一个下标集合为\(0,1,2,...,n-1\)的数组&#xff0c;并代表&#xff1a; \[\sum_…

win10下右键菜单添加“打开cmd”

早期版本的win10是可以在文件夹的左上角打开cmd的&#xff0c;更新后发现现在只有powershell能用了。这不方便。 通过修改注册表&#xff0c;可以实现这个功能。 具体做法&#xff1a;新建一个.reg文件win10_add_cmd_mouse_right_click.reg&#xff0c;内容如下&#xff1a; Wi…

Android学习笔记039之文件上传和下载

文件上传和下载在我们开发中经常需要用到&#xff0c;现在也有很多的网络框架封装了文件上传和下载功能。不过这一篇&#xff0c;我们介绍一下Android系统提供的文件下载服务–DownLoadManager。在API 9之后&#xff0c;Android提供了Download Manager来优化和处理长时间的下载…

Android学习笔记040之Handler

Android开发中我们常常会用到多线程&#xff0c;但是进行UI界面的更新只能在UI线程&#xff0c;而请求网络获取数据不能在UI线程&#xff0c;这就涉及到了线程之间的通信问题&#xff0c;Android系统给我提供了一个线程间通信的解决办法–Handler&#xff0c;下面我们来介绍一下…

Android学习笔记041之屏幕适配

1、屏幕适配原因 我们都知道Android系统是开发源代码的&#xff0c;任何用户、开发者、OEM厂商、运营商都可以对Android进行定制&#xff0c;修改成他们喜欢的样子。这样就导致Android碎片化非常严重&#xff0c;具体严重到什么程度呢&#xff1f;我们来看一张图 这里的每一个矩…

go连接RabbitMQ no access to this vhost错误

连接的失败报错&#xff1a;RabbitMQ Exception (403) Reason: "no access to this vhost" 因为没有配置该用户的访问权限,可以通过 rabbitmqctl add_vhost admin 来添加&#xff0c;并赋予权限&#xff1a; rabbitmqctl set_permissions -p 用户名 admin ".&qu…

Android学习笔记042之事件处理机制

Android提供了两套事件处理机制&#xff0c;分别是&#xff1a;基于监听的事件处理机制和基于回调的事件处理机制。 基于监听的事件处理机制 事件监听机制由事件源、事件、事件监听器三类对象组成&#xff0c;基本处理流程如下&#xff1a; 为某个事件设置一个监听器&#x…