Android系统回顾(八):网络通信(三)

三、基于Socket的网络通信
这个例子实现的是客户端向服务器发送请求,服务器向客户端发送响应数据。

服务端(.net C#)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            //第一个参数指定寻址方案,AddressFamily.InterNetwork代表的是ipv4寻址方案
            //第二个参数指定连接类型,SocketType.Stream代表的是基于字节流的可靠双工连接
            //第三个参数指定运输层协议,这里指定用tcp协议承载
            Socket soc_server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            //指定套接字监听的IP及端口。IP是指客户端的IP,Any表示允许任何客户端接入。端口是本地端口。这样的设置意味着允许任意IP的客户端通过本机2222端口接入
            IPEndPoint ip = new IPEndPoint(IPAddress.Any, 2222);
            soc_server.Bind(ip);
            //10表示挂起连接队列的最大长度是10
            soc_server.Listen(10);
            while (true)
            {
                Console.WriteLine(soc_server.LocalEndPoint.ToString() + "等待客户端连接");
                //当有连接接入时,创建新套接字为这个连接服务。我们将使用这个新套接字与客户端通信
                //Accept方法是一个同步方法,会阻塞所在线程。也就是说,此处Accept函数调用时会停下来等待,直到有连接接入才会返回继续执行后面的代码
                Socket soc_new = soc_server.Accept();
                Console.WriteLine(soc_new.RemoteEndPoint.ToString() + "已连接");
                string all = string.Empty;
                while (true)
                {
                    string input = string.Empty;
                    //建立输入缓存
                    byte[] receiveBytes = new byte[1024];
                    //每次读取1024个字节,直到读到指定的字符串,一次读取结束。
                    //如果没有可读取的数据,则 Receive 方法将一直处于阻止状态
                    //如果远程主机使用 Shutdown 方法关闭了 Socket 连接,并且所有可用数据均已收到,则 Receive 方法将立即完成并返回零字节。
                    int byteNums = soc_new.Receive(receiveBytes);
                    if (byteNums == 0)
                    {
                        Console.WriteLine("远程主机主动关闭");
                        break;
                    }
                    //编码数据
                    input = Encoding.ASCII.GetString(receiveBytes, 0, byteNums);
                    all += input;
                    Console.WriteLine("收到:" + input);
                    //当接收到的"[FINAL]"时,会话结束
                    if (input.IndexOf("[final]") > -1)
                    {
                        Console.WriteLine("收到结束字符串");
                        break;
                    }
                }
                byte[] reply = Encoding.ASCII.GetBytes(all + ":receive succeed");
                soc_new.Send(reply);
                //禁止客户端收发数据
                soc_new.Shutdown(SocketShutdown.Both);
                soc_new.Close();
            }
            soc_server.Close();
        }
    }
}

客户端(Android)

/MyQQ/res/drawable-hdpi/sharp_liaotian.xml文件:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
    <!-- 外框 -->
    <stroke 
        android:width="2dp"
        android:color="#DD2ECCFA"
        />
    <!-- 内容 -->
    <solid
        android:color="#ffffffff"
        />
    <!-- 拐角 -->
    <corners
        android:radius="5dp"
        />
</shape>

/MyQQ/res/layout/main.xml文件:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#ffffffff"
    android:orientation="vertical" >
    <LinearLayout
        android:id="@+id/linearLayout1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:gravity="center_horizontal"
        android:orientation="horizontal" >
        <ImageView
            android:layout_width="25dp"
            android:layout_height="25dp"
            android:layout_gravity="center_vertical"
            android:background="@drawable/app_logo" >
        </ImageView>
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:paddingLeft="20dp"
            android:text="与XXX的聊天"
            android:textSize="20dp" >
        </TextView>
    </LinearLayout>
    <TextView
        android:id="@+id/txt_message"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="5dp"
        android:layout_weight="7"
        android:background="@drawable/sharp_liaotian"
        android:padding="5dp"
        android:text="聊天记录"
        android:textColor="#DD2ECCFA" >
    </TextView>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="5dp"
        android:layout_weight="1"
        android:orientation="horizontal" >
        <EditText
            android:id="@+id/edt_input"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_margin="3dp"
            android:layout_weight="3"
            android:background="@drawable/sharp_liaotian"
            android:gravity="center"
            android:hint="请输入聊天內容"
            android:textColor="#4F2F4F" >
        </EditText>
        <Button
            android:id="@+id/btn_send_message"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_margin="3dp"
            android:layout_weight="1"
            android:gravity="center"
            android:text="发送"
            android:textColor="#DD2ECCFA" />
    </LinearLayout>
</LinearLayout>

/MyQQ/src/com/yongyu/myqq/IConnectSuccess.java文件:

package com.yongyu.myqq;
import java.net.Socket;
public interface IConnectSuccess
{
    public void onGetSocket(Socket s);
}
/MyQQ/src/com/yongyu/myqq/IGetMessage.java文件:

package com.yongyu.myqq;
public interface IGetMessage
{
    public void onGetMessage(String str);
}

/MyQQ/src/com/yongyu/myqq/MainActivity.java文件:

package com.yongyu.myqq;
import java.net.Socket;
import java.util.concurrent.ArrayBlockingQueue;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends Activity implements IConnectSuccess,
        IGetMessage
{
    private Socket             connect;
    private EditText           edt_input;
    private ArrayBlockingQueue queue;
    private TextView           txt_message;
    private Handler            handler = null;
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        Button btn_send_message = (Button) findViewById(R.id.btn_send_message);
        // 每个Handler实例,都会绑定到创建他的线程中。Handler实例可以分发Message对象和Runnable对象其绑定的线程中并伺机执行
        handler = new Handler();
        edt_input = (EditText) findViewById(R.id.edt_input);
        txt_message = (TextView) findViewById(R.id.txt_message);
        btn_send_message.setOnClickListener(new OnClickListener()
        {
            @Override
            public void onClick(View arg0)
            {
                if (connect == null)
                {
                    Toast t = Toast.makeText(MainActivity.this, "网络异常,请检查设置!",
                            2000);
                    t.show();
                    return;
                }
                if (edt_input.getText().toString().equals(""))
                {
                    Toast t = Toast.makeText(MainActivity.this, "请输入消息!", 2000);
                    t.show();
                    return;
                } else
                {
                    String str = edt_input.getText().toString();
                    try
                    {
                        MainActivity.this.queue.put(str);
                    } catch (InterruptedException e)
                    {
                        Log.e("com.yongyu.myqq.MainActivity.onCreate",
                                e.getMessage());
                    }
                    txt_message.setText(txt_message.getText() + "\n发出的信息是:"
                            + str);
                    edt_input.setText("");
                }
            }
        });
        Task_Connect task_conn = new Task_Connect(this);
        task_conn.execute();
    }
    @Override
    public void onGetSocket(Socket s)
    {
        this.connect = s;
        this.queue = new ArrayBlockingQueue<String>(10);
        Thread t_s = new Thread(new MyRunnableSendMessage(connect, queue));
        t_s.start();
        Thread t_r = new Thread(new MyRunnableReceiveMessage(connect, this));
        t_r.start();
    }
    @Override
    public void onGetMessage(String str)
    {
        myUpdateUIRunnable runnable = new myUpdateUIRunnable(str, txt_message);
        handler.post(runnable);
    }
}
class myUpdateUIRunnable implements Runnable
{
    private String   msg;
    private TextView text;
    public myUpdateUIRunnable(String str, TextView t)
    {
        msg = str;
        text = t;
    }
    @Override
    public void run()
    {
        text.setText(text.getText() + "\n收到的信息是:" + msg);
    }
}
/MyQQ/src/com/yongyu/myqq/MyRunnableReceiveMessage.java文件:

package com.yongyu.myqq;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Socket;
import java.net.SocketException;
public class MyRunnableReceiveMessage implements Runnable
{
    private Socket      socket;
    private IGetMessage iget;
    private String      message = "";
    public MyRunnableReceiveMessage(Socket _socket, IGetMessage _get)
    {
        this.socket = _socket;
        this.iget = _get;
    }
    @Override
    public void run()
    {
        InputStream in = null;
        if (!isConnected())
            return;
        try
        {
            // 尝试打开输入流
            in = this.socket.getInputStream();
        } catch (IOException e1)
        {
            return;
        }
        while (true)
        {
            try
            {
                if (!isConnected())
                {
                    break;
                }
                // 读取消息-------------------------
                InputStreamReader inReader = new InputStreamReader(
                        socket.getInputStream());
                BufferedReader reader = new BufferedReader(inReader);
                message = reader.readLine();
                // --------------------------------
                // 直接在异步线程里更新UI会导致:Only the original thread that created a view
                // hierarchy can touch its views.
                if (message == null)
                    continue;
                this.iget.onGetMessage(message);
                message = "";
            } catch (SocketException e)
            {
                // 如果本地套接字关闭,退出输入尝试
                if (e.getMessage().toLowerCase().trim()
                        .equals(("Socket is closed").toLowerCase().trim()))
                    break;
            } catch (Exception e0)
            {
                // 继续输入尝试
                continue;
            }
        }
        try
        {
            socket.shutdownOutput();
        } catch (IOException e)
        {
        }
    }
    private boolean isConnected()
    {
        // 注意,在套接字编程中,我们要时刻注意对网络连接状态进行判断,java中的Socket提供的isClosed(),isConnected(),isInputShutdown(),sOutputShutdown()都是用于检查本地Socket状态的,不是用于检查远程Socket状态的,所以是无效的的。
        // 要检查远程Socket的状态可以用:
        try
        {
            socket.sendUrgentData(0xFF);
            return true;
        } catch (Exception e)
        {
            return false;
        }
    }
}

/MyQQ/src/com/yongyu/myqq/MyRunnableSendMessage.java文件:

package com.yongyu.myqq;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.net.SocketException;
import java.util.concurrent.ArrayBlockingQueue;
public class MyRunnableSendMessage implements Runnable
{
    private Socket             socket;
    private ArrayBlockingQueue queue;


    public MyRunnableSendMessage(Socket _socket, ArrayBlockingQueue _que)
    {
        this.socket = _socket;
        this.queue = _que;
    }
    @Override
    public void run()
    {
        OutputStream out;
        if (!isConnected())
            return;
        try
        {
            // 尝试打开输出流
            out = this.socket.getOutputStream();
        } catch (IOException e1)
        {
            return;
        }
        while (true)
        {
            try
            {
                String message = (String) queue.take();
                if (!isConnected())
                {
                    break;
                }
                out.write((message).getBytes());
            } catch (SocketException e)
            {


                // 如果本地套接字关闭,退出输出尝试
                if (e.getMessage().toLowerCase().trim()
                        .equals(("Socket is closed").toLowerCase().trim()))
                    break;
            } catch (Exception e0)
            {


                // 继续输出尝试
                continue;
            }
        }
        try
        {
            // 对于在socket上建立的输入(输出)流一旦调用close函数关闭,会造成socket关闭。这样基于socket的输出(输入)流也将不可用
            // 调用socket.shutdownOutputStream()只会单方面关闭输出流,不会导致socket关闭,此时基于socket的输入流还是可用的
            socket.shutdownOutput();
        } catch (IOException e)
        {
        }
    }
    private boolean isConnected()
    {
        // 注意,在套接字编程中,我们要时刻注意对网络连接状态进行判断,java中的Socket提供的isClosed(),isConnected(),isInputShutdown(),sOutputShutdown()都是用于检查本地Socket状态的,不是用于检查远程Socket状态的,所以是无效的的。
        // 要检查远程Socket的状态可以用:
        try
        {
            socket.sendUrgentData(0xFF);
            return true;
        } catch (Exception e)
        {
            return false;
        }
    }
}
/MyQQ/src/com/yongyu/myqq/Task_Connect.java文件:

package com.yongyu.myqq;
import java.net.Socket;
import android.os.AsyncTask;
import android.util.Log;
public class Task_Connect extends AsyncTask<Void, Void, Socket>
{
    private IConnectSuccess iget;
    public Task_Connect(IConnectSuccess _iget)
    {
        this.iget = _iget;
    }
    @Override
    protected Socket doInBackground(Void... arg0)
    {
        Socket connect = null;
        try
        {
            connect = new Socket("192.168.10.111", 2222);
        } catch (Exception e)
        {
            connect = null;
            Log.e(" com.yongyu.myqq.Task_Connect.doInBackground",
                    e.getMessage());
        }
        return connect;
    }
    @Override
    protected void onPostExecute(Socket result)
    {
        this.iget.onGetSocket(result);
    }
}
效果:

技术分享

技术分享



















郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。