`
阿尔萨斯
  • 浏览: 4147527 次
社区版块
存档分类
最新评论

Java4Android之socket网络通信基础

 
阅读更多

本节主要介绍Socket编程,发现Java里面的socket编程和C语言的还是有一些不一样,比如TCP socket ,在Java中区分了serverSocket。不过原理都一样,在流程处理上也非常相似,所以,理解起来并不难。我们会先从基础说起,从如何建立socket连接,到如何实现一个合理的设计例如在android中,我们发送一条消息,然后监听一个回复,如何做到不卡死UI,本文将会由浅入深的为大家呈现一个相对完整的android socket编程。

在进入Java 的socket编程之前,我们从原理上切入,然后提及相应的代码说明。

Socket可以说是一种针对网络的抽象,应用通过它可以来针对网络读写数据。就像通过一个文件的file handler就可以都写数据到存储设备上一样。根据TCP协议和UDP协议的不同,在网络编程方面就有面向两个协议的不同socket,一个是面向字节流的一个是面向报文的


TCP

TCP主要是面向连接的协议,它包含有建立和拆除连接,保证数据流的顺序和正确性等功能。每次对TCP中间的数据操作相当于对一个数据流进行访问。它最典型的特征就是那三次握手的建立连接过程。TCP的连接建立和撤销过程如下图:

tcp

Server端

Server端所要做的事情主要是建立一个通信的端点,然后等待客户端发送的请求。典型的处理步骤如下:
1. 构建一个ServerSocket实例,指定本地的端口。这个socket就是用来监听指定端口的连接请求的。
2.重复如下几个步骤:
a. 调用socket的accept()方法来获得下面客户端的连接请求。通过accept()方法返回的socket实例,建立了一个和客户端的新连接。
b.通过这个返回的socket实例获取InputStream和OutputStream,可以通过这两个stream来分别读和写数据。
c.结束的时候调用socket实例的close()方法关闭socket连接。

单线程版本


这个流程的典型示例代码如下:

//1. 构造ServerSocket实例,指定服务端口。
ServerSocket servSock = new ServerSocket(servPort);


while(true)
{
	   // 2.调用accept方法,建立和客户端的连接
           Socket clntSock = servSock.accept();
           SocketAddress clientAddress =    
                clntSock.getRemoteSocketAddress();
           System.out.println("Handling client at " + clientAddress);

	    // 3. 获取连接的InputStream,OutputStream来进行数据读写
            InputStream in = clntSock.getInputStream();
            OutputStream out = clntSock.getOutputStream();

            while((recvMsgSize = in.read(receiveBuf)) != -1)
            {
                out.write(receiveBuf, 0, recvMsgSize);
            }   
	    // 4.操作结束,关闭socket.
            clntSock.close();
}  

多线程版本

上述是单线程版本的服务器流程,也可是是多线程的。大体代码如下:

try   
{ file://建立服务器    
 ServerSocket server = new ServerSocket(9998);    
 int i=1;    
 for(;;)    
 {    
<span style="white-space:pre">	</span>Socket incoming = server.accept();    
<span style="white-space:pre">	</span>new ServerThread(incoming,i).start();    //开启线程来处理客户端的请求
<span style="white-space:pre">	</span>i++;    
 }    
}catch (IOException ex){ ex.printStackTrace(); }   

Client端

客户端的请求过程稍微有点不一样:
1.构建Socket实例,通过指定的远程服务器地址和端口来建立连接。
2.通过Socket实例包含的InputStream和OutputStream来进行数据的读写。
3.操作结束后调用socket实例的close方法,关闭。
示例代码如下;

// 1.根据指定的server地址和端口,建立socket连接。
Socket socket = new Socket(server, servPort);

// 2. 根据socket实例获取InputStream, OutputStream进行数据读写。
InputStream in = socket.getInputStream();
OutputStream out = socket.getOutputStream();
out.write(data);

//3.操作结束,关闭socket.
socket.close();


上述代码就是一个完整的客户端/服务器结构的socket通信代码。



不卡死UI的socket设计

但是,在android中的socket通信中,我们可以想象一下这样的场景。我在EditText里面输入了一句“你好”,然后我按发送Button按钮,就把消息发送给了远端的服务器,然后服务器给我回了一个“你好,欢迎您!”。我们发送完第一条信息的时候,我们要阻塞在那里等服务器的话,会发生什么?我们 UI是不是会卡死?所以,很明显,这样的设计是不合理的。

客户端


MyClientActivity.java文件,主要是定义了一个继承自Thread类的用于接收数据的类,覆写了其中的run()方法,在这个函数里面接收数据,接收到数据后就通过Handler发送消息,收到消息后在UI线程里更新接收到的数据。完整的内容如下:
package com.nan.client;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.net.UnknownHostException;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;


public class MyClientActivity extends Activity 
{
    private EditText mEditText = null;
    private Button connectButton = null;
    private Button sendButton = null;
    private TextView mTextView = null;
    
    private Socket clientSocket = null;
    private OutputStream outStream = null;
    
    private Handler mHandler = null;
    
    private ReceiveThread mReceiveThread = null;
    private boolean stop = true;
    
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) 
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        mEditText = (EditText)this.findViewById(R.id.edittext);
        mTextView = (TextView)this.findViewById(R.id.retextview);
        connectButton = (Button)this.findViewById(R.id.connectbutton);
        sendButton = (Button)this.findViewById(R.id.sendbutton);
        sendButton.setEnabled(false);      
        
        //连接按钮监听
        connectButton.setOnClickListener(new View.OnClickListener() 
        {
            
            @Override
            public void onClick(View v) 
            {
                // TODO Auto-generated method stub
                try 
                {
                    //实例化对象并连接到服务器
                    clientSocket = new Socket("113.114.170.246",8888);
                } 
                catch (UnknownHostException e) 
                {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } 
                catch (IOException e) 
                {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                
                displayToast("连接成功!");                        
                //连接按钮使能
                connectButton.setEnabled(false);
                //发送按钮使能
                sendButton.setEnabled(true);
                
                mReceiveThread = new ReceiveThread(clientSocket);
                stop = false;
                //开启线程
                mReceiveThread.start();
            }
        });
        
        //发送数据按钮监听
        sendButton.setOnClickListener(new View.OnClickListener() 
        {
            
            @Override
            public void onClick(View v) 
            {
                // TODO Auto-generated method stub
                byte[] msgBuffer = null;
                //获得EditTex的内容
                String text = mEditText.getText().toString();
                try {
                    //字符编码转换
                    msgBuffer = text.getBytes("GB2312");
                } catch (UnsupportedEncodingException e1) {
                    // TODO Auto-generated catch block
                    e1.printStackTrace();
                }
                            
                
                try {
                    //获得Socket的输出流
                    outStream = clientSocket.getOutputStream();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }                                                    
                
                
                try {
                    //发送数据
                    outStream.write(msgBuffer);
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                //清空内容
                mEditText.setText("");
                displayToast("发送成功!");
            }
        });
              
        //消息处理
        mHandler = new Handler()
        {
            @Override
            public void handleMessage(Message msg)
            {
                //显示接收到的内容
                mTextView.setText((msg.obj).toString());
            }
        };
        
    }
    
    //显示Toast函数
    private void displayToast(String s)
    {
        Toast.makeText(this, s, Toast.LENGTH_SHORT).show();
    }
    
    
    private class ReceiveThread extends Thread
    {
        private InputStream inStream = null;
        
        private byte[] buf;  
        private String str = null;
        
        ReceiveThread(Socket s)
        {
            try {
                //获得输入流
                this.inStream = s.getInputStream();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }      
        
        @Override
        public void run()
        {
            while(!stop)
            {
                this.buf = new byte[512];
                
                try {
                    //读取输入数据(阻塞)
                    this.inStream.read(this.buf);
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } 
                
                //字符编码转换
                try {
                    this.str = new String(this.buf, "GB2312").trim();
                } catch (UnsupportedEncodingException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                
                Message msg = new Message();
                msg.obj = this.str;
                //发送消息
                mHandler.sendMessage(msg);
                
            }
        }
        
        
    }
    
    @Override
    public void onDestroy()
    {
        super.onDestroy();
        
        if(mReceiveThread != null)
        {
            stop = true;
            mReceiveThread.interrupt();
        }
    }
     
}

服务端

MyServerActivity.java文件,定义了两个Thread子类,一个用于监听客户端的连接,一个用于接收数据,其他地方与MyClientActivity.java差不多。完整的内容如下:
package com.nan.server;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.ServerSocket;
import java.net.Socket;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;


public class MyServerActivity extends Activity 
{
    private TextView ipTextView = null;
    private EditText mEditText = null;
    private Button sendButton = null;
    private TextView mTextView = null;
    
    private OutputStream outStream = null;
    private Socket clientSocket = null;
    private ServerSocket mServerSocket = null;
    
    private Handler mHandler = null;
    
    private AcceptThread mAcceptThread = null;
    private ReceiveThread mReceiveThread = null;
    private boolean stop = true;
    
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) 
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        ipTextView = (TextView)this.findViewById(R.id.iptextview);
        mEditText = (EditText)this.findViewById(R.id.sedittext);
        sendButton = (Button)this.findViewById(R.id.sendbutton);
        sendButton.setEnabled(false);
        mTextView = (TextView)this.findViewById(R.id.textview);
        
        //发送数据按钮监听
        sendButton.setOnClickListener(new View.OnClickListener() 
        {
            
            @Override
            public void onClick(View v) 
            {
                // TODO Auto-generated method stub
                byte[] msgBuffer = null;
                //获得EditTex的内容
                String text = mEditText.getText().toString();
                try {
                    //字符编码转换
                    msgBuffer = text.getBytes("GB2312");
                } catch (UnsupportedEncodingException e1) {
                    // TODO Auto-generated catch block
                    e1.printStackTrace();
                }
                            
                
                try {
                    //获得Socket的输出流
                    outStream = clientSocket.getOutputStream();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }                                                    
                
                
                try {
                    //发送数据
                    outStream.write(msgBuffer);
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                //清空内容
                mEditText.setText("");
                displayToast("发送成功!");
                
            }
        });
        //消息处理
        mHandler = new Handler()
        {
            @Override
            public void handleMessage(Message msg)
            {
                switch(msg.what)
                {
                    case 0:
                    {
                        //显示客户端IP
                        ipTextView.setText((msg.obj).toString());
                        //使能发送按钮
                        sendButton.setEnabled(true);
                        break;
                    }
                    case 1:
                    {
                        //显示接收到的数据
                        mTextView.setText((msg.obj).toString());
                        break;
                    }                 
                }                                           
                
            }
        };
        
        
        mAcceptThread = new AcceptThread();
        //开启监听线程
        mAcceptThread.start();
              
    }
    
    //显示Toast函数
    private void displayToast(String s)
    {
        Toast.makeText(this, s, Toast.LENGTH_SHORT).show();
    }
    
    
    private class AcceptThread extends Thread
    {
        @Override
        public void run()
        {
            try {
                //实例化ServerSocket对象并设置端口号为8888
                mServerSocket = new ServerSocket(8888);
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            while(1)
{
            try {
                //等待客户端的连接(阻塞)
                clientSocket = mServerSocket.accept();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            
            mReceiveThread = new ReceiveThread(clientSocket);
            stop = false;
            //开启接收线程
            mReceiveThread.start();
            
            Message msg = new Message();
            msg.what = 0;
            //获取客户端IP
            msg.obj = clientSocket.getInetAddress().getHostAddress();
            //发送消息
            mHandler.sendMessage(msg);
  }
        }
        
    }
    
    
    private class ReceiveThread extends Thread
    {
        private InputStream mInputStream = null;
        private byte[] buf ;  
        private String str = null;
        
        ReceiveThread(Socket s)
        {
            try {
                //获得输入流
                this.mInputStream = s.getInputStream();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        
        @Override
        public void run()
        {
            while(!stop)
            {
                this.buf = new byte[512];
                
                //读取输入的数据(阻塞读)
                try {
                    this.mInputStream.read(buf);
                } catch (IOException e1) {
                    // TODO Auto-generated catch block
                    e1.printStackTrace();
                }
                
                //字符编码转换
                try {
                    this.str = new String(this.buf, "GB2312").trim();
                } catch (UnsupportedEncodingException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                
                Message msg = new Message();
                msg.what = 1;        
                msg.obj = this.str;
                //发送消息
                mHandler.sendMessage(msg);
                
            }
        }
    }
    
      
    @Override
    public void onDestroy()
    {
        super.onDestroy();
        
        if(mReceiveThread != null)
        {
            stop = true;
            mReceiveThread.interrupt();
        }
    }
    
    
}
上述代码就是实现了真实环境下android的socket通信。

UDP

UDP和TCP有两个典型的区别,一个就是它不需要建立连接,另外就是它在每次收发的报文都保留了消息的边界。

server端

因为UDP协议不需要建立连接,它的过程如下:
1. 构造DatagramSocket实例,指定本地端口。
2. 通过DatagramSocket实例的receive方法接收DatagramPacket.DatagramPacket中间就包含了通信的内容。
3. 通过DatagramSocket的send和receive方法来收和发DatagramPacket.
典型的交互流程代码如下:
// 1. 构建DatagramSocket实例,指定本地端口。
DatagramSocket socket = new DatagramSocket(servPort);

// 2. 构建需要收发的DatagramPacket报文
DatagramPacket packet = new DatagramPacket(new byte[ECHOMAX], ECHOMAX);

while(true)
{
	// 3. 收报文
	socket.receive(packet);
	System.out.println("Handling client at " + packet.getAddress().getHostAddress()
	    + " on port " + packet.getPort());
	// 4. 发报文
	socket.send(packet);
	packet.setLength(ECHOMAX);
}

client端

UDP客户端的步骤也比较简单,主要包括下面3步:
1. 构造DatagramSocket实例。
2.通过DatagramSocket实例的send和receive方法发送DatagramPacket报文。
3.结束后,调用DatagramSocket的close方法关闭。
因为和TCP不同,UDP发送报文的时候可以在同一个本地端口随意发送给不同的服务器,一般不需要在UDP的DatagramSocket的构造函数中指定目的服务器的地址。
另外,UDP客户端还有一个重要的不同就是,TCP客户端发送echo连接消息之后会在调用read方法的时候进入阻塞状态,而UDP这样却不行。因为UDP中间是可以允许报文丢失的。如果报文丢失了,进程一直在阻塞或者挂起的状态,则进程会永远没法往下走了。所以会一般设置一个setSoTimeout方法,指定在多久的时间内没有收到报文就放弃。也可以通过指定一个数字,循环指定的次数来读取报文,读到就返回,否则就放弃。

一个典型的UDP Client代码示例如下:

// 1. 构造UDP DatagramSocket对象
DatagramSocket socket = new DatagramSocket();

// 2。指定timeout时间,防止进入无限等待状态
socket.setSoTimeout(TIMEOUT);

// 3. 构造收发的报文对象
DatagramPacket sendPacket = new DatagramPacket(bytesToSend,
    bytesToSend.length, serverAddress, servPort);
DatagramPacket receivePacket =
    new DatagramPacket(new byte[bytesToSend.length], bytesToSend.length);

// 4.指定尝试的次数
int tries = 0;
boolean receivedResponse = false;
 do
{
	socket.send(sendPacket);
	try
	{
		socket.receive(receivePacket);
 
		if(!receivePacket.getAddress().equals(serverAddress))
		{
			throw new IOException("Received packet from an unknown source");
		}
		receivedResponse = true;
	}
	catch(InterruptedIOException e)
	{
		tries += 1;
		System.out.println("Timed out, " + (MAXTRIES - tries) + "");
	}
}while((!receivedResponse) && (tries < MAXTRIES));

// 根据是否接收到报文进行反馈
if(receivedResponse)
{
	System.out.println("Received: " + new String(receivePacket.getData()));
}
else
{
	System.out.println("No response -- giving up.");
}

// 5. 关闭socket
socket.close();

这次的socket编程就说道这里,里面参考和引用转载了不少别人的东西,mark一下。

Reference:

http://shmilyaw-hotmail-com.iteye.com/blog/1556187

http://www.cnblogs.com/lknlfy/archive/2012/03/04/2379628.html




分享到:
评论

相关推荐

    Android中socket通信简单实现

    Android中socket通信简单实现 socket通信需要有一个服务器和客户端,可以把同一个APP作为服务器跟客户端,也可以分开成两个APP。 先上个图: 这里以一个APP作为服务器跟客户端为示例 1、添加网络访问权限 2、写...

    java基于Socket的网络通信

    可以直接运行ServerView运行服务器和客户端

    Android代码-轻量级 Android Socket 通信框架。

    An blocking socket client for Java application or Andorid. Open source licenses Basic Socket Library Socket Server Plugin Library OkSocket Introduce OkSocket is a Java library project ...

    Android-Socket长连接通信心跳包消息回调Java服务端

    Socket长连接、通信、心跳包、消息回调、Java服务端

    Android移动开发-使用Socket进行通信的实现

    Java对基于TCP协议的网络通信提供了良好的封装,Java使用Socket对象来代表两端通信接口,并通过Socket产生IO流来进行网络通信。 客户端通常可以使用Socket的构造器来连接到指定服务器,其实客户端程序也非常简单,它...

    [android开发]Android socket通信(含代码示例).txt

    Android手机的应用,除了...java在socket通信方面上已经有了很好的范例,我只是抛砖引玉,将其引入到android之中,疏漏之处在所难免,很多地方也值得深思与考量,下面,用敲门的方式演示Android如何实现套接字的通信。

    基于Java的TCP Socket通信(计算机端/Android手机端)

    基于Java的TCP Socket通信(计算机端/Android手机端)http://www.paincker.com/java-tcp-socket

    基于Socket的Android聊天室

    Socket通信是网络通信中最常用的技术之一,通过Socket建立的可靠连接,可以让多个终端与服务器保持通信,最典型的应用是建立一个多人聊天程序。本实例使用ServerSocket建立聊天服务器。将服务器端所有的通讯线程保存...

    Android-netty和socket通信的demo

    Netty是基于Java NIO client-server的网络应用框架,使用Netty可以快速开发网络应用

    android socket网络通信编程实例

    android socket网络通信编程实例,TCP/IP Socket、ServerSocket的形式。在JAVA编程中, 包来开发socket程序,下面列出所有常用的类,并简单介绍。再此说明一下:Socket仅仅是对TCP、UDP网络接口的封装,不涉及上层...

    java socket 实现手机客户端与服务端通信

    里面包含完整工程代码,这对学习android下socket通信很有帮助,最好在真机测试,里面可以显示所连接的wifi网络名与所分配的IP,以及要连的可以填写服务器IP与端口,非常灵活使用。

    Android中socket通信的简单实现

    Android中socket通信简单实现,供大家参考,具体内容如下 socket通信需要有一个服务器和客户端,可以把同一个APP作为服务器跟客户端,也可以分开成两个APP。 先上个图: 这里以一个APP作为服务器跟客户端为示例 1、...

    Android_Socket_UDP_Test:Android网络接口通信Socket的UDP实验

    Android_Socket_UDP_Test Android网络接口通信的第二个实验 Socket的UDP方法实现通信 其中发送方是Test2,接收方是Test1,主要用的类是DatagramSocket类

    网络编程课程设计报告-Android-Socket-聊天室.doc

    网络编程课程 设计报告 课程名称 java程序设计之网络编程 题 目 Android Socket 聊天室 专 业 信息管理与信息系统 班 级 组 长 副 组 长 组 员 指导教师 目 录 1设计目的 3 2 开发工具和开发环境 3 3 设计原理 3 3.1...

    android开发揭秘PDF

    8.1 网络通信基础 8.1.1 无线网络技术 8.1.2 Android网络基础 8.2 HTTP通信 8.2.1 HttpURLConnection接口 8.2.2 HttpClient接口 8.2.3 实时更新 8.3 Socket通信 8.3.1 Socket基础 8.3.2 Socket应用(简易聊天室) 8.4 ...

    谈谈Android的三种网络通信方式

    Android平台有三种网络接口可以使用,他们分别是:java.net.*(标准Java接口)、Org.apache接口和Android.net.*(Android网络接口)。下面分别介绍这些接口的功能和作用。 1.标准Java接口 java.net.*提供与联网有关的...

    Android实验七.doc

    列举4种Android网络编程API? Apache、socket、URLConnection、httpConnection。 2. 常用的数据传输格式有哪两种? JSON、XML。 3. Android网络编程必须申请的权限是? &lt;uses-permission android:name="andriod....

    Android_Socket_TCP_Test:Android网络接口通信的实验,Socket基于TCP协议

    Android_Socket_TCP_Test Android网络接口通信实验 用Socket基于TCP的方法实验 发送方SocketServiceTcpTest 接收方SocketTcpTest 主要用的类是ServerSocket

    《Android应用开发揭秘》附带光盘代码.

     8.1 网络通信基础  8.1.1 无线网络技术  8.1.2 Android网络基础  8.2 HTTP通信  8.2.1 HttpURLConnection接口  8.2.2 HttpClient接口  8.2.3 实时更新  8.3 Socket通信  8.3.1 Socket基础  8.3.2 Socket...

Global site tag (gtag.js) - Google Analytics