android socketio_android多线程通信

android socketio_android多线程通信1、在Eclipse上创建ServerSocket,代码如下:public class Server{。

Android App开发基础篇—Socket通信

前言:离职闲置了半年,终于又开始上班了。重新工作的第一个项目就是Socket通信,虽然自己对Socket有一点点印象(读书的时候有过Java网络编程的课程),但是之前的工作中完全没接触过,于是,果断买了一本Android网络编程的书来开始学习。下面简单做一个学习笔记。

一、首先,看一下Socket和ServerSocket最基本的用法

1.1、在Eclipse上创建ServerSocket,代码如下:

public class Server{

public static void main(String[] args) { try { //获取ServerSocket实例,监听指定端口  ServerSocket server = new ServerSocket(8033);  System.out.println("服务端已启动,等待客户端连接...");  //accept()方法接收客户端的Socket连接请求,返回一个  //与客户端对应的Socket  Socket socket = server.accept();  System.out.println("客户端" + socket.getInetAddress() + "已连接...");  //获取Socket的输出流  OutputStream os = socket.getOutputStream();  //将输出流包装成PrintStream  PrintStream ps = new PrintStream(os);  //向客户端输出信息  ps.println("Hello客户端");  //关闭输出流  ps.close();  //关闭Socket  socket.close();  } catch (IOException e) { e.printStackTrace();  } }

}

1.2、编写Android客户端Socket程序,代码如下:

import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.View; import android.widget.Button;  import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.Socket;  public class MainActivity extends AppCompatActivity implements View.OnClickListener { private static String TAG = "MainActivity";   //服务端的IP地址,依据每个人的实际情况而不同,例如  //笔者的服务端是在自己电脑上用Eclipse写的应用程序,  //因此此处IP地址是用ipconfig(windows系统下)命令查询  //到的电脑的IPv4地址  private static final String IP = "192.168.1.132";  //端口号,有效值0~65535,避免使用已被占用的端口号  private static final int PORT = 8033;  private String data = "this is from socket client";  private Socket socket;  private Button mBtnConn;  private Button mBtnSend;   @Override  protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);  setContentView(R.layout.activity_main);  mBtnConn = (Button) findViewById(R.id.btn_connect);  mBtnSend = (Button) findViewById(R.id.btn_send);  mBtnConn.setOnClickListener(this);  mBtnSend.setOnClickListener(this);  } @Override  public void onClick(View v) { switch (v.getId()) { case R.id.btn_connect: //Android上不能将网络操作写在主线程,  //这里创建一个线程来执行Socket操作  new ClientThread().start();  break;  case R.id.btn_send: break;  } } //执行Socket操作的线程  private class ClientThread extends Thread { @Override  public void run() { super.run();  try { //获取Socket实例,通过指定的端口号向指定的IP地址Server发送连接请求  Socket socket = new Socket(IP, PORT);  //获取Socket输入流  InputStream is = socket.getInputStream();  //将Socket输入流包装成BufferedReader  BufferedReader buff = new BufferedReader(new InputStreamReader(is));  //读取数据  String s = buff.readLine();  //打印信息  Log.e(TAG, "run: 服务端:" + s);  //关闭输入流  buff.close();  //关闭Socket  socket.close();  } catch (IOException e) { e.printStackTrace();  } } } }

以上便是Socket通信最基本的过程。在Eclipse上运行Server端程序,然后运行Android客户端APP,点击“连接”按钮,连接成功后,Server端直接向Client端发送信息,Client端接收到消息后通过log打印出来。通信结束后双方都关闭各自的IO流和Socket连接。这里只是一个简单的例子,但实际应用中不会这么简单,通常都需要Server端和Client端都具备接收消息和发送消息的功能。因此,接下来需要修改一下程序。

二、Socket通信Server端和Client端互发信息

2.1、在Eclipse中书写Server端代码如下:

import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.PrintStream; import java.net.ServerSocket; import java.net.Socket;  import javax.swing.JButton; import javax.swing.JTextArea;   public class Server implements ActionListener { private JButton sendButton;  private Window window;  private ServerSocket serverSocket;  private JTextArea jTextArea;  private Socket mCurrentSocket;   public Server() { // 为了操作方便,这里写一个Java应用程序窗口  window = new Window("服务端");  // 获取一个按钮  sendButton = window.getSendButton();  // 给按钮设置事件监听  sendButton.addActionListener(this);  // 获取一个文本显示区  jTextArea = window.getJTextArea();  try { // 获取ServerSocket实例,开启端口监听  serverSocket = new ServerSocket(8033);  System.out.println("服务端已启动,等待客户端连接...");  jTextArea.append("服务端已启动,等待客户端连接...\n");  // 使用while循环实现持续监听客户端Socket连接请求  while (true) { // accept()方法接收客户端Socket连接请求,产生一个与客户端对应  // 的Socket  Socket socket = serverSocket.accept();  //这里用一个全局变量mCurrentSocket来记录当前正在与服务端互动的  //的客户端Socket,即最新连接Server端,或者最近一次向Server端发送  //消息的客户端Socket,服务端发送的消息会被该客户端接收  mCurrentSocket = socket;  System.out.println("客户端" + socket.getInetAddress() + "已连接...");  jTextArea.append("客户端:" + socket.getInetAddress() + "已连接...\n");  //每有一个客户端Socket接入,就开启一个线程单独监听来自该客户端的消息的输入  new Thread(new Runnable() { @Override  public void run() { // 当一个客户端Socket连接成功时,开启一个while循环  // 用于持续监听客户端的消息输入  while (true) { if(!socket.isClosed()) { // 读取客户端发送的消息  readMessage(socket);  } } } }).start();  } } catch (IOException e) { e.printStackTrace();  } } // main方法是Java程序启动的入口  public static void main(String[] args) { new Server();  } private void readMessage(Socket socket) { //记录最近一次向服务端发送消息的客户端Socket,服务端发送  //的消息将被该客户端接收  mCurrentSocket=socket;  try { // 获取客户端Socket输入流  InputStream is = socket.getInputStream();  // 将输入流包装成BufferedReader  BufferedReader buff = new BufferedReader(new InputStreamReader(is));  // 读取数据  String readLine = buff.readLine();  System.out.println("来自客户端:" + readLine);  jTextArea.append("来自客户端:" + readLine + "\n");  } catch (IOException e) { e.printStackTrace();  try { socket.close();  } catch (IOException e1) { e1.printStackTrace();  } } } @Override  public void actionPerformed(ActionEvent arg0) { sendMessage();  } private void sendMessage() { try { // 将Socket对应的输出流包装成PrintStream  PrintStream ps = new PrintStream(mCurrentSocket.getOutputStream());  // 进行普通IO操作,向客户端输出信息  ps.println("Hello客户端");  } catch (IOException e) { e.printStackTrace();  } } }

2.2、Android客户端代码如下:

import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Button; 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.io.PrintStream; import java.net.Socket;  public class MainActivity extends AppCompatActivity implements View.OnClickListener { private static String TAG = "MainActivity";  private static final String IP = "192.168.1.132";  private static final int PORT = 8033;  private String data = "this is from socket client";  private Button mBtnConn;  private Button mBtnSend;  private TextView mMessageTv;  private Socket mSocket;   @Override  protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);  setContentView(R.layout.activity_main);  mMessageTv = (TextView) findViewById(R.id.message_tv);  mBtnConn = (Button) findViewById(R.id.btn_connect);  mBtnSend = (Button) findViewById(R.id.btn_send);  mBtnConn.setOnClickListener(this);  mBtnSend.setOnClickListener(this);  } @Override  public void onClick(View v) { switch (v.getId()) { case R.id.btn_connect: //Android上不能将网络操作写在主线程,  //这里创建一个线程来执行Socket操作  new ConnectThread().start();  break;  case R.id.btn_send: new SendDataThread().start();  break;  } } //执行Socket连接操作的线程  private class ConnectThread extends Thread { @Override  public void run() { super.run();  try { //获取Socket实例,通过指定的端口号向指定的IP地址Server发送连接请求  mSocket = new Socket(IP, PORT);  if (mSocket != null) { //获取Socket输入流  InputStream is = mSocket.getInputStream();  //将Socket输入流包装成BufferedReader  BufferedReader buff = new BufferedReader(new InputStreamReader(is));  //连接成功以后,开启一个while循环来持续监听服务端的消息输入  while (true) { final String s = buff.readLine();  //将获取到的信息显示在TextView上,子线程中不能更新UI,这里直接用  //runOnUiThread来做  runOnUiThread(new Runnable() { @Override  public void run() { mMessageTv.append("来自服务端:" + s + "\n");  } });  } } } catch (IOException e) { e.printStackTrace();  } } } private class SendDataThread extends Thread { @Override  public void run() { super.run();  if (mSocket != null) { try { OutputStream os = mSocket.getOutputStream();  PrintStream ps = new PrintStream(os);  ps.println(data);  } catch (IOException e) { e.printStackTrace();  } } } } }

好了,上面的代码已经可以实现Socket服务端和客户端互相收发消息的通信 了。燃鹅,问题还没有结束。相信熟悉的同学马上就会发现了,上面的程序有问题,有BUG。那么,问题在哪里呢?很简单,我们多启动一个客户端,然后两个客户端分别与服务端互发消息,问题马上就出现 了。尝试过后发现,如果启动了两个客户端,服务端只能向其中一个客户端发送消息,而两个客户端也只有其中一个能向服务端发送消息。并且当两个客户端分别都执行了向客户端发消息的操作后,原本能向服务端发送消息的客户端再发消息时,服务端接收到的却是空数据。这里出现问题的原因,由于作者目前水平有限,暂时无法解释清楚。只是根据书本的说法,这里我们需要开启多线程。因此,下面我们继续对代码进行修改。修改的地方主要在服务端,将读取客户端消息的代码放到一个线程里面。即将上面服务端代码中的

// 当一个客户端Socket连接成功时,开启一个while循环
// 用于持续监听客户端的消息输入
while(true){
    // 读取客户端发送的消息
    readMessage(socket);
}

部分放入一个线程中去执行。修改如下:

new Thread(new Runnable() {
@Override
public void run() {
        // 当一个客户端Socket连接成功时,开启一个while循环
        // 用于持续监听客户端的消息输入
        while (true) {
        // 读取客户端发送的消息
        readMessage(socket);
        }
     }
}).start();

好了,现在服务端可以同时跟多个客户端正常通信了。然而,现在的程序虽然可以运行,也能实现功能,达到效果,但是还是存在一些问题的。这也是本人目前水平的局限,这里就暂且把问题留下,当做今后学习的方向之一。

最后贴上程序中用到的用于创建Java应用程序窗口的Window类

import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JTextArea;
import javax.swing.JTextField;

    public class Window extends JFrame {

        /**
         * 窗口类 定义客户端和服务器端的窗口
         */
        private static final long serialVersionUID = 2L;
        private String windowName;
        private JFrame myWindow;
        private JTextArea area;
        private JTextField field;
        private JButton btnSend;
        private JButton btnSendAll;

        public Window(String windowName) {
            this.windowName = windowName;
            myWindow = new JFrame(windowName);
            myWindow.setLayout(new FlowLayout());
            myWindow.setSize(new Dimension(500, 300));
            // 不能改变窗口大小
            myWindow.setResizable(false);

            area = new JTextArea();
            field = new JTextField();
            btnSend = new JButton("Send");
            btnSendAll = new JButton("Send To All");

            // 设置field的大小
            field.setPreferredSize(new Dimension(300, 30));
            myWindow.add(field);
            myWindow.add(btnSend);
            myWindow.add(btnSendAll);
            myWindow.add(area);
            // 改变area的大小
            area.setPreferredSize(new Dimension(470, 210));
            area.setBackground(Color.PINK);
            area.setEditable(false);
            // 设置窗口显示在电脑屏幕的某区域
            myWindow.setLocation(400, 200);

            myWindow.setVisible(true);
            // 点击关闭按钮时触发该方法
            closeMyWindow();
        }

        /**
         * 方法名:closeMyWindow()
         *
         * @param
         * @return 功能:当用户点击关闭按钮时,退出并且关闭该窗口
         */
        private void closeMyWindow() {
            myWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        }

        /**
         * 方法名:getFieldText()
         *
         * @param
         * @return string 功能:获取窗口的TextField中的字符串
         */
        public String getFieldText() {
            return field.getText().toString();
        }

        /**
         * 方法名:getSendButton()
         *
         * @param
         * @return JButton 功能:获得该窗口中的按钮
         */
        public JButton getSendButton() {
            return btnSend;
        }

        /**
         * 方法名:getSendAllButton()
         *
         * @param
         * @return JButton 功能:获得该窗口中的按钮
         */
        public JButton getSendAllButton() {
            return btnSendAll;
        }

        /**
         * 方法名:getJTextArea()
         *
         * @param
         * @return JTextArea 功能:返回窗口中的JTextArea
         */
        public JTextArea getJTextArea() {
            return area;
        }

        /**
         * 方法名:getTextField()
         *
         * @param
         * @return JTextField 功能:获得窗口中的textfield
         */
        public JTextField getTextField() {
            return field;
        }
    }

后记:Android 开发中的Socket通信主要用到了Java API中java.net中的两个类Socket和ServerSocket,分别用来表示Socket连接的客户端和服务端。Socket通信的过程大致可描述为:(1)Server端运行一个SocketServer程序,监听设备上指定的端口;(2)Client端通过指定的Server端的IP地址和端口号,向Server端发起连接请求;(3)Server端向Client发回Accept(接收)消息,Socket连接建立;(4)Server端和Client端开始通信。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
转载请注明出处: https://daima100.com/11787.html

(0)

相关推荐

  • MySQL5.7多实例安装及开机启动配置(亲测)[通俗易懂]

    MySQL5.7多实例安装及开机启动配置(亲测)[通俗易懂]安装环境: CentOS版本:CentOS7.6.1810 MySQL版本:5.7.9 以前一些很low的方法是: 解压两个mysql,分别放到不同文件夹。 其实在mysql中已经考虑到了多实例安装…

    2023-03-30
    144
  • Python Win32api 安装指南

    Python Win32api 安装指南Win32api是Python编程中非常重要的一个库,它为Python程序提供了非常强大和灵活的Windows API接口。Python Win32api主要用于Windows编程,可以通过WIN32 API调用操作系统的资源。本文将详细介绍Python Win32api的安装步骤。

    2024-06-23
    35
  • Python降低字符串大小写

    Python降低字符串大小写在Python中,字符串的大小写问题经常需要处理。一般情况下,我们需要将一个字符串的大小写进行统一,以方便后续的处理。Python内置的字符串处理函数可以实现这个功能。

    2024-02-05
    78
  • Linux-Shell脚本教学

    Linux-Shell脚本教学文章浏览阅读3.2k次,点赞7次,收藏54次。Shell脚本编写规则我们来看一段代码:#!/bin/shcd~mkdirshell_tutcdshell_tutfor((i=0;i10;i++));dotouchtest_$i.txtdo

    2023-11-06
    110
  • MySql的回顾一:基础[亲测有效]

    MySql的回顾一:基础[亲测有效]周末的时光是短暂,也是轻松愉快的,在这炎炎的夏日坐在小板凳上,吹着空调喝着茶的我带你点轻轻的点开我的文章链接,带领屏幕前的你回顾一下MySql的内容,希望你能有所收获。本篇随笔分上下两部分,上半部分理

    2023-03-20
    133
  • 世界读书日:感受中文的力量[通俗易懂]

    世界读书日:感受中文的力量[通俗易懂]今年4月23日,是第27个世界读书日。值此全世界爱书人共同的节日,我们特邀请海外中文学习者和来华留学生介绍给他们留下深刻印象的中文书籍,让更多的

    2023-07-10
    125
  • Mariadb之日志相关配置 – Linux「终于解决」

    Mariadb之日志相关配置 – Linux「终于解决」前面我们聊到了mariadb的事务,以及事务隔离级别,回顾请参考https://www.cnblogs.com/qiuhom-1874/p/13198186.html;今天我们来聊一聊mariadb的

    2023-03-16
    121
  • python的math库用法(python语言math库)

    python的math库用法(python语言math库)不能。Python由荷兰数学和计算机科学研究学会的GuidovanRossum1990年代初设计,作为一门叫做ABC语言的替代品。math库中的函数包括math.lcm不能直接使用,需要首先使用保留字import引用该库才可进行使用。

    2023-12-02
    128

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注