Android网络应用之Socket(一)

socket编程是网络通信的一个基础应用,无论是手机端还是PC端都需要socket技术来建立网络通信。在本章小编主要从以下几个方面来介绍socket的相关知识:

分别是“什么是socket?”,“socket有什么特点?”,“socket与Http以及TCP的区别”,“移动端socket的Demo”。写的不好的地方请大家批评指正。

一、何为socket?

socket也被称为“套接字”,它是一种网络通信的方式,它不是一种协议,而是提供给程序员实现TCP/IP封装与数据传输的接口。用于在客户端和服务端之间建立一个通信管道,使得应用程序/客户端可以通过这个通信管道向服务器/另一台主机发送请求,同时服务器也可以进行相应,建立两者之间的数据传输与交换。在Android中,我们知道每个应用程序都可以使用多线程来进行操作,用于网络传输的app的多个线程可以都建立一个socket连接,每个线程维护自己的socket管道,从而实现并发的网络通信。

socket的组成部分主要包括5个:连接使用的协议(TCP/UDP),客户端IP,客户端端口号,服务器端IP地址,服务器端的端口号。在Android中,serverSocket用于服务器端而socket是用于建立网络连接时用的,在连接成功时两端都会产生一个socket对象。

二、socket的特点及通信原理

我们可以看到,一个socket建立时所使用的协议可以是TCP的,也可以是UDP的。TCP是可靠的而UDP是不可靠的,原因就是TCP建立时需要三次握手,双方建立通信管道才可以数据传输,且是双向的。而UDP在数据传输时只是把应用层发来的数据进行打包并且附上目的的地址,不负责对方是否接收到。

一个Socket的通信建立可以分为客户端和服务端两部分:

服务端:1、创建服务器套接字并绑定到自己的一个端口上,这个端口最好大于1024,因为0~1023端口号是被系统预留的,会被一些系统功能所调用。

2、建立套接字的监听器,监听是否有别的客户端程序来请求访问本端口的serverSocket。

3、如果接受到,则接受请求建立连接通信

客户端:1、创建一个客户端套接字,绑定要访问的目标服务器的IP地址及端口号

2、连接服务端(这里与PC上的socket连接不同,不需要connect(),因为Android上的客户端socket在创建时,如果创建成功会自动内部调用连接方法)

3、与服务端进行通信

4、主动关闭客户端socket

了解了客户端与服务端的通信过程,我们都能了解到:服务端其实一直保持着监听客户端请求的状态,socket连接只能由发起请求的客户端主动关闭。两端在进行通信的时候,数据是通过InputStream和OutputStream来实现的,且首先是服务端收到输入流,再将结果通过输出流返回给客户端。而客户端方面是先发送输出流,再接收到输入流。

大致模式如下:

服务器和客户端的连接:服务器监听------->客户端请求------->建立连接

三、socket与HTTP、TCP的区别

首先需要知道HTTP是应用层协议,主要解决的是如何包装数据。而TCP是传输层协议,主要解决的是如何传输数据。socket是TCP/IP协议的封装,本身并不是协议。

socket的连接模式与HTTP、TCP的连接模式不一致。从上节已经了解到socket的连接是从服务器监听开始的,而在TCP方面,客户端与服务器的连接需要经过三次握手之后才正式开始数据的传输,而HTTP则是基于"请求-响应"才能建立数据传输,什么意思呢?只有先请求,服务器才能对外发数据,不能主动建立传输。在HTTP经过了0.9、1.0和1.1时代,这种连接模式可以在一次TCP连接建立时一直保持,响应多次请求。

当socket是传输层的一个封装,传输层主要由TCP和UDP协议,当socket使用的是TCP协议而不是UDP协议时,一个socket连接其实就是TCP连接。

四、Demo

这里我们将创建一个demo,在Android上使用socket。我们的Demo主要功能是在编辑框中编写一段文字,然后按发送按钮,最后内部实现通过socket的通信方式,反馈到textview上进行显示编辑的内容。里面的细节重点会在末尾处分析。

下面是xml布局文件:

        android:id="@+id/btn_conn"/>
    <EditText
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:hint="请在这里编辑要发送的内容"
        android:id="@+id/et_message"/>

    <Button
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="发送"
        android:id="@+id/btn_sent"/>

    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:id="@+id/tv"
        android:hint="接收发来的数据"/>
</LinearLayout>

布局效果如下:

首先来描述下我们要实现的功能逻辑:

1、通过点击连接先建立socket通信管道

2、在编辑框中输入我们要发送的内容,按下发送按钮,数据会显示到文本控件中;

3、在内部自己建立一个线程,不能在UI线程中更新,会造成ANR;

4、本身即作为发送方,又作为接收方,主要过程是把数据打包发送,通过outputstream进行输出传输,创建为消息的内容;接着被handler收到,扔进消息队列,处理的时候再把数据解压,进行ui更新。主要功能handler.sentmessage()和handler.handmessage()。还有就是outputstream和inputstream时的数据打包,encoding必须一致。中文可以使用GB2312.

代码如下:

package com.example.mysocketdemo;

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.app.ActionBar;
import android.app.Fragment;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.text.Editable;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import android.os.Build;

public class MainActivity extends Activity {
	private TextView tv;
	private Button btnsent,btnconn;
	private EditText ed_message;
	private OutputStream output;
	private Socket clientSocket;
	private Handler mHandler;
	private MyThread mythread;
	boolean stop = true;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        if (savedInstanceState == null) {
            getFragmentManager().beginTransaction()
                    .add(R.id.container, new PlaceholderFragment())
                    .commit();
        }
        init();
        //onClickEvent---connect
        btnconn.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				try {
					clientSocket = new Socket("127.0.0.1", 8888);
				} catch (UnknownHostException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				Toast.makeText(getApplicationContext(), "connect ok", Toast.LENGTH_SHORT).show();
				//把socket绑定到自己的线程对象
				try {
					mythread = new MyThread(clientSocket);
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				mythread.start();//启动线程

				//更新UI,大部分的数据工作已经交给了mythread对象
				btnsent.setEnabled(true);
				btnconn.setEnabled(false);
				stop = false;
			}
		});
        //sent Message
        btnsent.setOnClickListener(new View.OnClickListener() {

			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				//当点击按钮时,会获取编辑框中的数据,然后提交给线程
				//先将发送内容进行打包
				byte[]  msgBuffer = null;
				try {
					msgBuffer = ed_message.getText().toString().getBytes("GB2312");
				} catch (UnsupportedEncodingException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				//打包完成之后,添加socket的输出流对象
				try {
					output = clientSocket.getOutputStream();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				try {
					//输出流对象将字节写入
					//无论是输出流还是输入流,操作的都是字节,如果向变成字符串,需要自己构建String对象
					output.write(msgBuffer);
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				Toast.makeText(getApplicationContext(), "发送成功", Toast.LENGTH_SHORT).show();
				ed_message.setText("");
			}
		});

        //在连接和发送数据之后,接下来就是处理了,发送的数据会通过message的方式传递到消息队列,再由handl进行获取
        mHandler = new Handler(){
        	public void handleMessage(android.os.Message msg) {
        		tv.setText(msg.obj.toString());
        	};
        };
    }
    public void init()
    {
    	tv = (TextView) findViewById(R.id.tv);
    	btnsent = (Button) findViewById(R.id.btn_sent);
    	ed_message = (EditText) findViewById(R.id.et_message);
    	btnconn = (Button) findViewById(R.id.btn_conn);
    	btnconn.setEnabled(true);
    	btnsent.setEnabled(false);
    }

    //自定义线程类;
    private class MyThread extends Thread{
    	//构建自己的socket对象,用来在线程内使用
    	private Socket socket;
    	private byte[] buf = null;
    	private InputStream inputstream;
    	String str = null;
    	public MyThread(Socket socket) throws IOException
    	{
    		this.socket = socket;
    		inputstream = this.socket.getInputStream();
    	}
    	@Override
    	public void run() {
    		// TODO Auto-generated method stub
    		while(!stop)
    		{
    			buf = new byte[512];
    			//将inputstream内的数据读到buf里
    			try {
					this.inputstream.read(buf);
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
    			try {
    				//将buf里的字符流进行解析,得到
					this.str = new String(buf, "GB2312").trim();
				} catch (UnsupportedEncodingException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
    			//线程内获取了来自socket的Inputstream字节流,并且转换成字符串后,线程就获取了消息的实体内容
    			//此时线程将执行自己的一个使命,就是创建消息,并发送出去
    			Message msg = new Message();
    			msg.obj = this.str;
    			mHandler.sendMessage(msg);
    		}
    	}
    }

    @Override
    protected void onDestroy() {
    	// TODO Auto-generated method stub
    	if (mythread!=null) {
			mythread.interrupt();
		}
    	super.onDestroy();
    }

了解代码的逻辑之后,我再理一下整个思路:

当我们点击了connnet之后,就会创建连接目标ip地址及端口的socket连接,此时,什么数据都没有,socket的Outputstream和InputStream里面都是空的。尽管我们自定义的线程在连接后就启动了,并且必须执行run方法,但是此时没有数据来接收。

当我们编辑好文笔点击了sent按钮之后,编辑框里的文本会由字符串向字节的包装,使用的是String.getbyte()方法,为啥呢,因为outputstream和inputstream只接受字节流的数据。当socket的getOutputStream获取了outputstream对象之后,执行了write方法进行数据的写入,而此时线程依然在执行,它突然发现连接的socket里有数据了,就使用getIupteStream获取了inputstream对象,采用了read方法读取了字节流,最后打包成字符串。

打包成字符串之后,线程要执行一个自己的使命,就是将其封装成消息,以消息的形式塞给主线程的消息队列,此时UI线程的使者Hanlder就完成了sendmessage的任务。

最后回到主线程,Handler对象使用handlmessage方法将消息的内容显示在了textView上。

以上就是整个逻辑。

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-10 16:13:27

Android网络应用之Socket(一)的相关文章

android网络应用开发完全解析

??Android网络应用开发,主要有两种方式,一种是socket(是对tcp/udp协议的封装),另外一种就是使用Http协议,Android中主要提供了两种方式,HttpURLConnection和Apache HttpClient.下面对Android网络应用开发进行具体的阐述. 一.基于socket的网络通信 1.基于udp的socket编程步骤 定义码头 即:定义一个DatagramSocket对象ds 定义可以用来接收或者发送数据的集装箱 即:定义DatagramPacket对象dp

android中非阻塞socket通信

1.什么是同步与异步,阻塞与非阻塞 首先我们要明白搞明白:同步就等于阻塞?异步就等于非阻塞?这是不对的,同步不等于阻 塞,而异步也不等于非阻塞. 1)那什么是同步编程? 什么是同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回.根据这个定义,android中绝大多数函数都是同步调用.但是一般而言,我们在谈论同步.异步的时候,特指那些需要其他部件协作或者需要一定时间完成的任务.在android中,由于主线程(UI线程的不安全性),我们经常会用到handler的SendMessage

Android客户端通过Socket连接服务器

Android客户端通过Socket连接服务器. Android互联网项目中,绝大部分都有连接远程服务器的需求,连接的方式有多种,可以是TCP的方式,当然也可以通过Socket的方式. 相对于TCP的方式,Socket的方式略显的较为原始,对于客户端来说,复杂度反而比TCP的方式还要高一些,毕竟TCP的连接有现成的框架可以使用, 比如Spring等. 而使用socket方式这些工作完全需要客户端来做,也增加了客户端的工作量,不过凡事有利弊,通过socket的方式,流量上 相对于TCP等的方式更加

Android 网络编程 Socket Http

在Android的网络通讯中,通常会使用Socket进行设备间数的数据通讯,使用Http来对网络数据进行请求. 1.Socket(套接字) 不管是有过Java开发经验还是.NET开发经验的同学都应该对Socket有或多或少的了解,常见的TCP或者UDP协议其实都是基于Socket来实现的. Socket是用于描述网络上的一个设备中的一个进程或者应用程序的,Socket由IP地址和端口号两部分组成.IP地址用来定位设备,端口号用来定位应用程序或者进程,比如我们常见的运行在80端口上的HTTP协议.

Android 网络编程 Socket

1.服务端开发 创建一个Java程序 public class MyServer { // 定义保存所有的Socket,与客户端建立连接得到一个Socket public static List<Socket> socketList = new ArrayList<Socket>(); public static void main(String[] args) throws IOException { ServerSocket server = new ServerSocket(

android网络通信之socket教程实例汇总

一.socket基础1.Socket通讯机制(详细),如何将socket通信的客户端与服务器http://www.eoeandroid.com/thread-61727-1-1.html 2.Http和Socket区别http://www.eoeandroid.com/thread-96927-1-1.html 二.实例教程1.Android开发之socket通信 向PC机发信息 获取本机IPhttp://www.eoeandroid.com/thread-97477-1-1.html 2.PC

Android TCP/IP Socket Test

TCP/IP协议:Transmission Control Protocol/Internet Protocol的简写,中译名为传输控制协议/因特网互联协议,又名网络通讯协议,是Internet最基本的协议.Internet国际互联网络的基础,由网络层的IP协议和传输层的TCP协议组成.TCP/IP 定义了电子设备如何连入因特网,以及数据如何在它们之间传输的标准.协议采用了4层的层级结构,每一层都呼叫它的下一层所提供的网络来完成自己的需求.通俗而言:TCP负责发现传输的问题,一有问题就发出信号,

Android客户端通过socket与服务器通信

android端--Client package com.sec.chatroomandroid; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.net.Socket; import android.ap

Android网络编程Socket【实例解析】

Socket 其实和JavaWeb 里面的Socket一模一样 建立客服端,服务器端,服务器开一个端口供客服端访问 第一步创建服务器端:(这里把为了便于讲解,把服务器端,和客服端都放在手机上了) 创建Android工程 socketserver package com.example.socketserver; import java.io.IOException; import java.io.InputStream; import java.net.ServerSocket; import