从零开始学android -- 简易的socket通信

Posted 从Android出发

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从零开始学android -- 简易的socket通信相关的知识,希望对你有一定的参考价值。

先来介绍下socket,网上摘抄点资料,免得自己打字了

网络中进程之间如何通信?

     本地的进程间通信(IPC)有很多种方式,但可以总结为下面4类:

  • 1、消息传递(管道、FIFO、消息队列)
  • 2、同步(互斥量、条件变量、读写锁、文件和写记录锁、信号量)
  • 3、共享内存(匿名的和具名的)
  • 4、远程过程调用(Solaris门和Sun RPC)

     但这些都不是本文的主题!我们要讨论的是网络中进程之间如何通信?首要解决的问题是如何唯一标识一个进程,否则通信无从谈起!在本地可以通过进程PID来唯一标识一个进程,但是在网络中这是行不通的。其实TCP/IP协议族已经帮我们解决了这个问题,网络层的“ip地址”可以唯一标识网络中的主机,而传输层的“协议+端口”可以唯一标识主机中的应用程序(进程)。这样利用三元组(ip地址,协议,端口)就可以标识网络的进程了,网络中的进程通信就可以利用这个标志与其它进程进行交互。

使用TCP/IP协议的应用程序通常采用应用编程接口:UNIX  BSD的套接字(socket)和UNIX System V的TLI(已经被淘汰),来实现网络进程之间的通信。就目前而言,几乎所有的应用程序都是采用socket,而现在又是网络时代,网络中进程通信是无处不在,这就是我为什么说“一切皆socket”。

2、什么是Socket?

      上面我们已经知道网络中的进程是通过socket来通信的,那什么是socket呢?socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,都可以用“打开open –> 读写write/read –> 关闭close”模式来操作。我的理解就是Socket就是该模式的一个实现,socket即是一种特殊的文件,一些socket函数就是对其进行的操作(读/写IO、打开、关闭)。

     socket一词的起源:在组网领域的首次使用是在1970年2月12日发布的文献IETF RFC33中发现的,撰写者为Stephen Carr、Steve Crocker和Vint Cerf。根据美国计算机历史博物馆的记载,Croker写道:“命名空间的元素都可称为套接字接口。一个套接字接口构成一个连接的一端,而一个连接可完全由一对套接字接口规定。”计算机历史博物馆补充道:“这比BSD的套接字接口定义早了大约12年。”

 

TCP/IP协议基础

  Ip协议是internet上使用的一个关键协议,它的全称是Intentnet Protocol ,即Internet协议,通常简称IP协议。通过IP协议,使Internet成为一个允许连接不同类型的计算机和不同操作系统的网络。

  要使两台计算机彼此之间进行通信,必须使两台计算机使用一种“语言” ,IP协议只保证计算机能发送和接收分组数据。IP协议负责将消息从一个主机传送到另一个主机,消息在传送的过程中被分割成一个个小包。

尽管计算机通过安装IP软件,保证了计算机之间可以发送和接收数据,但IP协议还不能解决数据分组在传输过程中可能出现的问题。因此,若要解决可能出现的问题,连接上Internet的计算机还需要安装TCP协议来提供可靠并且无差错的通信服务。

  TCP协议被称为一种端对端协议。这是因为它为两台计算机之间的连接起了重要作用:当一台计算机需要与另一台远程计算机连接时,TCP协议会让他们建立一个连接:用于发送和接收数据的虚拟链路。

TCP协议负责收集这些信息包。并将其按照适当的次序放好传送,在接收端收到后再将其正确的还原。TCP协议保证了数据包在传送中准确无误。TCP协议使用重发机制:当一个通信实体发送一个消息给另一个通信实体后,需要收到另一个通信实体的确认消息,如果没有收到另一个通信实体的确认消息,则会再次重发刚才发送的消息。

  通过这种重发机制。TCP协议向应用程序提供可靠的通信连接,使他能够自动适应网上的各种变化。即使在Internet暂时出现堵塞的情况下,TCP也能保证通信的可靠。

  综上所述,虽然IP和TCP协议的功能不同,也可以分开单独使用,但它们是在同一时期作为一个协议来设计的,并且在功能上也是互补的。只有两者结合,才能保证Internet在复杂的环境下正常运行。凡是要连接Internet的计算机,都必须同时安装和使用这两个协议,因此在实际中常把这两个协议统称为TCP/IP协议。

 

使用ServerSocket创建TCP服务端

  上图中我们可以看出Tcp通信的两个通信实体是没有区分服务端和客户端的,但是实在他们建立起连接后的状态。如果没有建立起连接的话,必须需要有一端释放出“主动”的姿态,等待连接,那么这一端就是服务端,连接的一端就是客户端。

 

先来看看效果:

  服务端:pc上

  客户端:真机和模拟器

  目的:建立一个多客户端的简易聊天室。(下图你也能看到,模拟器连接上了,pc服务端会打印一个连接数1,真机连接上服务端了,pc端会打印一个连接数2)

 

PC服务端代码编写:

import java.io.IOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;

public class MyServer {

    
    public static ArrayList<Socket> socketList = new ArrayList<>();
    
    public static void main(String[] args) {
        
        try {
            //建立服务端
            InetAddress inetAddress = InetAddress.getLocalHost();
            //打印本地的ip
            System.out.println("ip:"+inetAddress.getHostAddress());
            ServerSocket serverSocket = new ServerSocket(30000);
            while(true){
                //循环接收socket
                Socket socket = serverSocket.accept();
                
                OutputStream os = socket.getOutputStream();
                os.write(("欢迎连接Mdm服务端socket...\\n").getBytes("utf-8"));
//                os.close();
                socketList.add(socket); //join in socketList
                new Thread(new ServerThread(socket)).start(); //new thread Deal with this matter. 
                
                System.out.println("当前连接数"+socketList.size());
            }
            
            //that end java code written.
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    
}

 

ServerThread.java

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.Socket;

public class ServerThread implements Runnable {

    BufferedReader br = null;
    
    Socket s;
    
    public ServerThread(Socket s){
        
        try {
            this.s = s;
            br = new BufferedReader(new InputStreamReader(s.getInputStream(),"utf-8"));
            
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    @Override
    public void run() {
        
        String content = null;
        //循环判断client发送过来的数据不为null
        while((content = readFromClient()) != null){
            
            //遍历每一列表中的每一个socket给他们发送消息类似聊天室的感觉每个人都收到了消息
            for (Socket otherClient : MyServer.socketList) {
                
                try {
                    //输出流进行输出
                    OutputStream os = otherClient.getOutputStream();
                    os.write((content+"\\n").getBytes("utf-8"));
//                    os.close();//关闭流
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            
        }
    }
    
    /**
     * 获取从clientSocket中发过来的消息
     * @return
     */
    private String readFromClient(){
        
        try {
            return br.readLine();
        } catch (IOException e) {
            e.printStackTrace();
            //if hava excption  remove this socket in socketList.
            MyServer.socketList.remove(s);
        }
        return null;
    }
}

 

android 客户端代码

activity_multi_thread_client.xml

<?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">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <!--用来发送给服务端的数据-->
        <EditText
            android:id="@+id/input"
            android:layout_weight="1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

        <Button
            android:id="@+id/send"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="send"
            />
    </LinearLayout>


    <!--用来获取显示服务端发送过来的数据-->
    <TextView
        android:id="@+id/show"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="top"
        android:background="#ffff"
        android:textSize="14dp"/>

</LinearLayout>
MultiThreadClient.java

public class MultiThreadClient extends AppCompatActivity {


    private EditText input;
    private TextView show;

    private Handler handler;

    private ClientThread clientThread;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_multi_thread_client);

        input = (EditText) findViewById(R.id.input);
        show = (TextView) findViewById(R.id.show);

        handler = new Handler(){
            @Override
            public void handleMessage(Message msg) {
                //receive ServerSocket data.
                if(msg.what == 0x123)
                show.append("\\n"+msg.obj.toString());
            }
        };
        clientThread = new ClientThread(handler);
        new Thread(clientThread).start();

        Button send = (Button) findViewById(R.id.send);
        send.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                Message msg = Message.obtain();
                msg.what = 0x345;
                msg.obj = input.getText().toString();
                clientThread.sendHandler.sendMessage(msg);
                input.setText("");
            }
        });

    }
}

 

ClientThread.java

public class ClientThread implements Runnable {

    //接收服务端发送的数据
    private Handler handler;

    //send client data.
    public Handler sendHandler;

    private BufferedReader br;
    private OutputStream os;


    public ClientThread(Handler handler){
        this.handler = handler;
    }
    @Override
    public void run() {
        try {
            //connection server Socket
            Socket socket = new Socket("192.168.1.110",30000);
            br = new BufferedReader(new InputStreamReader(socket.getInputStream(),"utf-8"));
            os = socket.getOutputStream();

            //print server data in TextView
            new Thread(){
                @Override
                public void run() {
                    try {
                        String content = null;
                        while ((content = br.readLine()) != null) {
                            Message msg = Message.obtain();
                            msg.what = 0x123;
                            msg.obj = content;
                            handler.sendMessage(msg);
                        }
                    }catch (IOException ex){
                        ex.printStackTrace();
                    }
                }
            }.start();

            //send client data to server
            Looper.prepare();
            sendHandler = new Handler(){
                @Override
                public void handleMessage(Message msg) {
                    try {
                        if(msg.what == 0x345) {
                            os.write((msg.obj.toString()+"\\r\\n").getBytes("utf-8")); //这地方后面一定得加上\\n因为服务端那边readLine()读取的是以换行为结束标识。之前发不出去数据,代码调试了好久,头都大了。
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            };
            Looper.loop();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

 

ok。

 

以上是关于从零开始学android -- 简易的socket通信的主要内容,如果未能解决你的问题,请参考以下文章

从零开始学android -- dialog

从零开始学android -- CilpDrawable 徐徐展开的风景

Android自定义View——从零开始实现书籍翻页效果(一)

从零开始学android -- notification通知

大厂都在进军车载应用?Android车载应用开发,从零开始一起学

第24章OnLongClickListener长按事件(从零开始学Android)