热门标签 | HotTags
当前位置:  开发笔记 > 运维 > 正文

详解Android基于TCP和UDP协议的Socket通信

这篇文章主要介绍了详解Android基于TCP和UDP协议的Socket通信,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

本来想讲一下基础的网络通信方面的知识点,发现太枯燥乏味了,不过笔试中也经常会问到这方面的问题,所以关于通信方面的知识点,小编会放到面试中去,因为实战中也就面试会用到这方面知识点

Android与服务器的通信方式主要有两种,一是Http通信,一是Socket通信。两者的最大差异在于,http连接使用的是“请求—响应方式”,即在请求时建立连接通道,当客户端向服务器发送请求后,服务器端才能向客户端返回数据。

而Socket通信中基于TCP/IP协议的通信则是在双方建立起连接后就可以直接进行数据的传输,在连接时可实现信息的主动推送,而不需要每次由客户端想服务器发送请求。而UDP则是提供无连接的数据报服务,UDP在发送数据报前不需建立连接,不对数据报进行检查即可发送数据包

1.什么是Socket?

2.Socket通信模型:

Socket通信实现步骤解析:

Step 1:创建ServerSocket和Socket

Step 2:打开连接到的Socket的输入/输出流

Step 3:按照协议对Socket进行读/写操作

Step 4:关闭输入输出流,以及Socket

好的,我们接下来写一个简单的例子,开启服务端后,客户端点击按钮然后链接服务端, 并向服务端发送一串字符串,表示通过Socket链接上服务器~

一、1.基于TCPSocket服务端的编写:

服务端要做的事有这些:

Step 1 :创建ServerSocket对象,绑定监听的端口

Step 2 :调用accept()方法监听客户端的请求

Step 3 :连接建立后,通过输入流读取客户端发送的请求信息

Step 4 :通过输出流向客户端发送响应信息 Step 5 :关闭相关资源

代码实现:

创建一个Java项目,然后把Java代码贴进去即可!这里可以用eclipse来写服务端,as来写安卓端

public class SocketServer {
 public static void main(String[] args) throws IOException {
  //1.创建一个服务器端Socket,即ServerSocket,指定绑定的端口,并监听此端口
  ServerSocket serverSocket = new ServerSocket(12345);
  InetAddress address = InetAddress.getLocalHost();
  String ip = address.getHostAddress();
  Socket socket = null;
  //2.调用accept()等待客户端连接
  System.out.println("~~~服务端已就绪,等待客户端接入~,服务端ip地址: " + ip);
  socket = serverSocket.accept();
  //3.连接后获取输入流,读取客户端信息
  InputStream is=null;
  InputStreamReader isr=null;
  BufferedReader br=null;
  OutputStream os=null;
  PrintWriter pw=null;
  is = socket.getInputStream();  //获取输入流
  isr = new InputStreamReader(is,"UTF-8");
  br = new BufferedReader(isr);
  String info = null;
  while((info=br.readLine())!=null){//循环读取客户端的信息
   System.out.println("客户端发送过来的信息" + info);
  }
  socket.shutdownInput();//关闭输入流
  socket.close();
 }
}

然后我们把代码run起来,控制台会打印:

好的,接下来到Android客户端了!

2.Socket客户端的编写: 客户端要做的事有这些:

Step 1 :创建Socket对象,指明需要链接的服务器的地址和端号

Step 2 :链接建立后,通过输出流向服务器发送请求信息

Step 3 :通过输出流获取服务器响应的信息

Step 4 :关闭相关资源

代码实现:

MainActivity.java:

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  Button btn_accept = (Button) findViewById(R.id.btn_accept);
  btn_accept.setOnClickListener(this);
 }

 @Override
 public void onClick(View v) {
  new Thread() {
   @Override
   public void run() {
    try {
     acceptServer();
    } catch (IOException e) {
     e.printStackTrace();
    }
   }
  }.start();
 }

 private void acceptServer() throws IOException {
  //1.创建客户端Socket,指定服务器地址和端口
  Socket socket = new Socket("172.16.2.54", 12345);
  //2.获取输出流,向服务器端发送信息
  OutputStream os = socket.getOutputStream();//字节输出流
  PrintWriter pw = new PrintWriter(os);//将输出流包装为打印流
  //获取客户端的IP地址
  InetAddress address = InetAddress.getLocalHost();
  String ip = address.getHostAddress();
  pw.write("客户端:~" + ip + "~ 接入服务器!!");
  pw.flush();
  socket.shutdownOutput();//关闭输出流
  socket.close();
 }
}

因为Android不允许在主线程(UI线程)中做网络操作,所以这里需要我们自己 另开一个线程来连接Socket!

运行结果:

点击按钮后,服务端控制台打印:

3.简易聊天室

那么通过上面的案例,我们就可以做一个简单的聊天软件,这里知道怎么实现的就可以了,实战中我们都是采用的第三方API,比如网易云,我会专门写一个网易云的IM通信

实现的效果图:

先把我们的服务端跑起来:

接着把我们的程序分别跑到两台模拟器上:

接下来我们来写代码:

首先是服务端,就是将读写socket的操作放到自定义线程当中,创建ServerSocket后,循环 调用accept方法,当有新客户端接入,将socket加入集合当中,同时在线程池新建一个线程!

另外,在读取信息的方法中,对输入字符串进行判断,如果为bye字符串,将socket从集合中 移除,然后close掉!

public class Server {
 //定义相关的参数,端口,存储Socket连接的集合,ServerSocket对象
 //以及线程池
 private static final int PORT = 12345;
 private List mList = new ArrayList();
 private ServerSocket server = null;
 private ExecutorService myExecutorService = null;
 
 
 public static void main(String[] args) {
  new Server();
 }

 public Server()
 {
  try
  {
   server = new ServerSocket(PORT);
   //创建线程池
   myExecutorService = Executors.newCachedThreadPool();
   System.out.println("服务端运行中...\n");
   Socket client = null;
   while(true)
   {
    client = server.accept();
    mList.add(client);
    myExecutorService.execute(new Service(client));
   }
   
  }catch(Exception e){e.printStackTrace();}
 }
 
 class Service implements Runnable
 {
  private Socket socket;
  private BufferedReader in = null;
  private String msg = "";
  
  public Service(Socket socket) {
   this.socket = socket;
   try
   {
    in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
     msg = "用户:" +this.socket.getInetAddress() + "~加入了聊天室" 
       +"当前在线人数:" +mList.size(); 
    this.sendmsg();
   }catch(IOException e){e.printStackTrace();}
  }
  
  
  
  @Override
  public void run() {
   try{
    while(true)
    {
     if((msg = in.readLine()) != null)
     {
      if(msg.equals("bye"))
      {
       System.out.println("~~~~~~~~~~~~~");
       mList.remove(socket);
       in.close();
       msg = "用户:" + socket.getInetAddress() 
         + "退出:" +"当前在线人数:"+mList.size(); 
       socket.close(); 
       this.sendmsg(); 
       break;
      }else{
       msg = socket.getInetAddress() + " 说: " + msg; 
       this.sendmsg(); 
      }
     }
    }
   }catch(Exception e){e.printStackTrace();}
  }
  
  //为连接上服务端的每个客户端发送信息
  public void sendmsg()
  {
   System.out.println(msg);
   int num = mList.size();
   for(int index = 0;index 

接着到客户端,客户端的难点在于要另外开辟线程的问题,因为Android不允许直接在 主线程中做网络操作,而且不允许在主线程外的线程操作UI,这里的做法是自己新建 一个线程,以及通过Hanlder来更新UI,实际开发不建议直接这样做!!!

布局文件:activity_main.xml:



 
 
 
 

MainActivity.java:

public class MainActivity extends AppCompatActivity implements Runnable {

 //定义相关变量,完成初始化
 private TextView txtshow;
 private EditText editsend;
 private Button btnsend;
 private static final String HOST = "172.16.2.54";
 private static final int PORT = 12345;
 private Socket socket = null;
 private BufferedReader in = null;
 private PrintWriter out = null;
 private String cOntent= "";
 private StringBuilder sb = null;

 //定义一个handler对象,用来刷新界面
 public Handler handler = new Handler() {
  public void handleMessage(Message msg) {
   if (msg.what == 0x123) {
    sb.append(content);
    txtshow.setText(sb.toString());
   }
  }

  ;
 };


 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  sb = new StringBuilder();
  txtshow = (TextView) findViewById(R.id.txtshow);
  editsend = (EditText) findViewById(R.id.editsend);
  btnsend = (Button) findViewById(R.id.btnsend);

  //当程序一开始运行的时候就实例化Socket对象,与服务端进行连接,获取输入输出流
  //因为4.0以后不能再主线程中进行网络操作,所以需要另外开辟一个线程
  new Thread() {

   public void run() {
    try {
     socket = new Socket(HOST, PORT);
     in = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
     out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(
       socket.getOutputStream())), true);
    } catch (IOException e) {
     e.printStackTrace();
    }
   }
  }.start();

  //为发送按钮设置点击事件
  btnsend.setOnClickListener(new View.OnClickListener() {

   @Override
   public void onClick(View v) {
    String msg = editsend.getText().toString();
    if (socket.isConnected()) {
     if (!socket.isOutputShutdown()) {
      out.println(msg);
     }
    }
   }
  });
  new Thread(MainActivity.this).start();
 }

 //重写run方法,在该方法中输入流的读取
 @Override
 public void run() {
  try {
   while (true) {
    if (socket.isConnected()) {
     if (!socket.isInputShutdown()) {
      if ((cOntent= in.readLine()) != null) {
       content += "\n";
       handler.sendEmptyMessage(0x123);
      }
     }
    }
   }
  } catch (Exception e) {
   e.printStackTrace();
  }
 }
}

二、基于UDP协议的Socket通信

TCP和UDP最大的区别在于是否需要客户端与服务端建立连接后才能进行 数据传输,

TCP:传输前先开服务端,accept,等客户端接入,然后获得 客户端socket然后进行IO操作,而UDP则不用

UDP:以数据报作为数据的传输载体,在进行传输时 首先要把传输的数据定义成数据报(Datagram),在数据报中指明数据要到达的Socket(主机地址 和端口号),然后再将数据以数据报的形式发送出去

1.服务端实现步骤:

Step 1:创建DatagramSocket,指定端口号

Step 2:创建DatagramPacket

Step 3:接收客户端发送的数据信息

Step 4:读取数据

示例代码:

public class UPDServer {
 public static void main(String[] args) throws IOException {
  /*
   * 接收客户端发送的数据
   */
  // 1.创建服务器端DatagramSocket,指定端口
  DatagramSocket socket = new DatagramSocket(12345);
  // 2.创建数据报,用于接收客户端发送的数据
  byte[] data = new byte[1024];// 创建字节数组,指定接收的数据包的大小
  DatagramPacket packet = new DatagramPacket(data, data.length);
  // 3.接收客户端发送的数据
  System.out.println("****服务器端已经启动,等待客户端发送数据");
  socket.receive(packet);// 此方法在接收到数据报之前会一直阻塞
  // 4.读取数据
  String info = new String(data, 0, packet.getLength());
  System.out.println("我是服务器,客户端说:" + info);

  /*
   * 向客户端响应数据
   */
  // 1.定义客户端的地址、端口号、数据
  InetAddress address = packet.getAddress();
  int port = packet.getPort();
  byte[] data2 = "欢迎您!".getBytes();
  // 2.创建数据报,包含响应的数据信息
  DatagramPacket packet2 = new DatagramPacket(data2, data2.length, address, port);
  // 3.响应客户端
  socket.send(packet2);
  // 4.关闭资源
  socket.close();
 }
}

2.客户端实现步骤:

Step 1:定义发送信息

Step 2:创建DatagramPacket,包含将要发送的信息

Step 3:创建DatagramSocket

 Step 4:发送数据

public class UDPClient {
 public static void main(String[] args) throws IOException {
  /*
   * 向服务器端发送数据
   */
  // 1.定义服务器的地址、端口号、数据
  InetAddress address = InetAddress.getByName("localhost");
  int port = 8800;
  byte[] data = "用户名:admin;密码:123".getBytes();
  // 2.创建数据报,包含发送的数据信息
  DatagramPacket packet = new DatagramPacket(data, data.length, address, port);
  // 3.创建DatagramSocket对象
  DatagramSocket socket = new DatagramSocket();
  // 4.向服务器端发送数据报
  socket.send(packet);

  /*
   * 接收服务器端响应的数据
   */
  // 1.创建数据报,用于接收服务器端响应的数据
  byte[] data2 = new byte[1024];
  DatagramPacket packet2 = new DatagramPacket(data2, data2.length);
  // 2.接收服务器响应的数据
  socket.receive(packet2);
  // 3.读取数据
  String reply = new String(data2, 0, packet2.getLength());
  System.out.println("我是客户端,服务器说:" + reply);
  // 4.关闭资源
  socket.close();
 }
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。


推荐阅读
  • 【具体报错信息】ErrorparsingD:\android-sdks\system-images\android-22\android-wear\armeabi-v7a\devi ... [详细]
  • POCOCLibraies属于功能广泛、轻量级别的开源框架库,它拥有媲美Boost库的功能以及较小的体积广泛应用在物联网平台、工业自动化等领域。POCOCLibrai ... [详细]
  • 本文介绍了Python高级网络编程及TCP/IP协议簇的OSI七层模型。首先简单介绍了七层模型的各层及其封装解封装过程。然后讨论了程序开发中涉及到的网络通信内容,主要包括TCP协议、UDP协议和IPV4协议。最后还介绍了socket编程、聊天socket实现、远程执行命令、上传文件、socketserver及其源码分析等相关内容。 ... [详细]
  • 本文介绍了使用kotlin实现动画效果的方法,包括上下移动、放大缩小、旋转等功能。通过代码示例演示了如何使用ObjectAnimator和AnimatorSet来实现动画效果,并提供了实现抖动效果的代码。同时还介绍了如何使用translationY和translationX来实现上下和左右移动的效果。最后还提供了一个anim_small.xml文件的代码示例,可以用来实现放大缩小的效果。 ... [详细]
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • 本文介绍了使用AJAX的POST请求实现数据修改功能的方法。通过ajax-post技术,可以实现在输入某个id后,通过ajax技术调用post.jsp修改具有该id记录的姓名的值。文章还提到了AJAX的概念和作用,以及使用async参数和open()方法的注意事项。同时强调了不推荐使用async=false的情况,并解释了JavaScript等待服务器响应的机制。 ... [详细]
  • 本文介绍了一款名为TimeSelector的Android日期时间选择器,采用了Material Design风格,可以在Android Studio中通过gradle添加依赖来使用,也可以在Eclipse中下载源码使用。文章详细介绍了TimeSelector的构造方法和参数说明,以及如何使用回调函数来处理选取时间后的操作。同时还提供了示例代码和可选的起始时间和结束时间设置。 ... [详细]
  • Activiti7流程定义开发笔记
    本文介绍了Activiti7流程定义的开发笔记,包括流程定义的概念、使用activiti-explorer和activiti-eclipse-designer进行建模的方式,以及生成流程图的方法。还介绍了流程定义部署的概念和步骤,包括将bpmn和png文件添加部署到activiti数据库中的方法,以及使用ZIP包进行部署的方式。同时还提到了activiti.cfg.xml文件的作用。 ... [详细]
  • 开发笔记:spring boot项目打成war包部署到服务器的步骤与注意事项
    本文介绍了将spring boot项目打成war包并部署到服务器的步骤与注意事项。通过本文的学习,读者可以了解到如何将spring boot项目打包成war包,并成功地部署到服务器上。 ... [详细]
  • 初探PLC 的ST 语言转换成C++ 的方法
    自动控制软件绕不开ST(StructureText)语言。它是IEC61131-3标准中唯一的一个高级语言。目前,大多数PLC产品支持ST ... [详细]
  • 概述H.323是由ITU制定的通信控制协议,用于在分组交换网中提供多媒体业务。呼叫控制是其中的重要组成部分,它可用来建立点到点的媒体会话和多点间媒体会议 ... [详细]
  • Allegro总结:1.防焊层(SolderMask):又称绿油层,PCB非布线层,用于制成丝网印板,将不需要焊接的地方涂上防焊剂.在防焊层上预留的焊盘大小要比实际的焊盘大一些,其差值一般 ... [详细]
  • android ... [详细]
  • 篇首语:本文由编程笔记#小编为大家整理,主要介绍了VoLTE端到端业务详解|VoLTE用户注册流程相关的知识,希望对你有一定的参考价值。书籍来源:艾怀丽 ... [详细]
  • Maven构建Hadoop,
    Maven构建Hadoop工程阅读目录序Maven安装构建示例下载系列索引 序  上一篇,我们编写了第一个MapReduce,并且成功的运行了Job,Hadoop1.x是通过ant ... [详细]
author-avatar
-孙立佳得
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有