android与Socket通信

来源:互联网 发布:igmp端口号 编辑:程序博客网 时间:2024/06/06 17:58

Android 与Socket通信

注:本文有借鉴网络之处,再加上自己的理解。

Android 与服务器之间的通信方式主要有2种,一是Http通信,而是Socket通信。

二者最大的差别就在于:

http链接是使用“请求-响应方式”,即在请求时建立链接通道,当客户端向服务端发送请求后,服务器端才能向客户端返回数据;而Socket通信则是在双方建立起链接后就可以进行数据的传输,在连接时可实现信息的主动推送,而不需要每次由客户端向服务端请求。

那什么是Socket呢?

         Socket又称套接字,在程序内提供了与外界通信的端口,即端口通信。通过建立Socket链接,可为通信双方的数据传输提供通道。Socket的主要特点就是数据丢失率低,使用简单且易于移植。

         Socket是一种抽象层,在设计模式中,Socket就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。应用程序通过Socket发送和接收数据,使用Socket可以将应用程序添加到网络中,在处于同一网络中的其他应用程序进行通信。简单的说,Socket提供了程序内部与外界通信的端口并为通信双方提供了数据传输的通道。

 

Socket有哪几种呢?

根据不同的底层协议,Socket的实现是多样化的,在这里只介绍TCP/IP协议族的内容。在这个协议族中主要的Socket类型为流套接字(Stream Socket)和数据套接字(Datagram Socket)。

         流套接字将TCP作为其端对端协议,提供了一个可信赖的字节流服务。

         数据套接字使用UDP协议,提供数据打包发送服务。

Socket的基本实现模型:


下面是具体的模型:

1TCP通信模型


 

2UDP通信模型




 

好了,现在我们要根据上述模型简述Socket的基本实现原理。

 

Socket基本实现原理

1、基于TCP协议的Socket

 服务端:首先,服务器端要声明一个ServerSocket对象并且指定端口号,然后调用ServerSocket的accept方法接收客户端数据。Accept()方法在没有数据进行接收时处于堵塞状态。(Socket socket=Serversocket.accept()),一旦接收到数据,通过inputstrem读取接受的数据。

客户端:客户端要创建一个Socket对象,指定服务器端的ip地址和端口号(Socketsocket=new Socket(“10.10.10.10”,8081)),通过inputstream读取数据,获取服务器发出的数据(OutputStream outputstream =socket.getOutputStream()),最后将要发出的数据写入到outputstream即可进行TCP协议的socket数据传输。

2、基于UDP协议的数据传输

服务器:服务器端首先创建一个DatagramSocket对象,并且指点监听的端口。接下来创建一个空的DatagramSocket 对象用于接收数据( byte data[] = new byte[1024;] DatagramSocket packet = new DatagramSocket(data,data.length)),使用DatagramSocket的receive 方法接收客户端发送的数据, receive () 与serversocket的accepet()类似,在没有数据进行接收时处于堵塞状态。

客户端:客户端也创建个DatagramSocket对象,并且指点监听的端口。接下来创建一个

InetAddress 对象,这个对象类似于一个网络的发送地址(InetAddressserveraddress = InetAddress.getByName("10.10.10.102")).定义要发送的一个字符串,创建一个DatagramPacket 对象,并制定要将这个数据包发送到网络的哪个地址以及端口号,最后使用DatagramSocket 的对象的send()发送数据。(String str = "hello";byte data[] = str.getByte();DatagramPacketpacket = new DatagramPacket(data,data.length,serveraddress,8081

);socket.send(packet);)

 

也许,这样理解起来可能有点困难,我们通过下面的例子来更好的理解。

Socket的基本实现方式

1TCP方式

1.1 TCP协议客户端的实现

//创建一个Socket 对象,指定服务器端的IP 地址和端口号

Socket socket =new Socket("10.10.10.10",8081);

//使用InputStream 读取硬盘上的文件

InputStreaminputStream = new FileInputStream("f://file/words.txt");

//从Socket 当中得到OutputStream

OutputStreamoutputStream = socket.getOutputStream();

byte buffer [] =new byte[4*1024];

int temp = 0 ;

//将InputStream 当中的数据取出,并写入到OutputStream 当中

while((temp = inputStream.read(buffer))!= -1)

{

outputStream.write(buffer, 0, temp);

}

outputStream.flush();

 

 

 

1.2 TCP协议服务端的实现

//声明一个ServerSocket 对象

ServerSocket serverSocket = null;

try {

//创建一个ServerSocket 对象,并让这个Socket在8081端口监听

serverSocket = new ServerSocket(8081);

//调用ServerSocket 的accept()方法,接受客户端所发送的请求,

//如果客户端没有发送数据,那么该线程就停滞不继续

Socket socket = serverSocket.accept();

//从Socket 当中得到InputStream对象

InputStream inputStream =socket.getInputStream();

byte buffer [] = new byte[1024*4];

int temp = 0;

//从InputStream 当中读取客户端所发送的数据

while((temp = inputStream.read(buffer)) !=-1){

System.out.println(newString(buffer,0,temp));

}

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

serverSocket.close();

}

2UDP方式

2.1 UDP协议客户端的实现

public static voidmain(String[] args) {

try {

//首先创建一个DatagramSocket 对象

DatagramSocketsocket = new DatagramSocket(8081);

//创建一个InetAddree

InetAddressserverAddress = InetAddress.getByName("10.10.10.102");

String str ="hello"; //这是要传输的数据

byte data [] =str.getBytes(); //把传输内容分解成字节

//创建一个DatagramPacket 对象,并指定要讲这个数据包发送到网

//络当中的哪个地址,以及端口号

DatagramPacketpacket = new DatagramPacket (data,data.length,serverAddress,

8081);

//调用socket 对象的send 方法,发送数据

socket.send(packet);

} catch (Exceptione) {

// TODOAuto-generated catch block

e.printStackTrace();

}

}

2.2 UDP协议服务端的实现

//创建一个DatagramSocket 对象,并指定监听的端口号

DatagramSocket socket = new DatagramSocket(8081);

byte data [] = new byte[1024];

//创建一个空的DatagramPacket 对象

Datagram Packet packet = new DatagramPacket(data,data.length);

//使用receive 方法接收客户端所发送的数据,

//如果客户端没有发送数据,该进程就停滞在这里

socket.receive(packet);

String result = newString(packet.getData(),packet.getOffset(),

packet.getLength());

System.out.println("result--->"+ result);

 

 

说道这里如果还不理解,那么我们就通过一个实例去了解一下吧!

 

Socket通信实例

通过上面的例子,我们最起码可以了解到Socket通信的基本实现方式和处理过程。接下来,我们就通过TCP协议的通信方式,结合一个简单的实例,去加深对socket通信的了解。

实例简述:该实例是一个简单的登录操作,当服务器处于监听状态时,客户端向服务端发送登录请求。当所请求的登录信息和服务器验证信息吻合,返回信息“1”,在客户端中通过Toast实现,当登录信息错误,返回登录信息错误,用“2”表示。代码如下:

客户端:

importjava.io.DataInputStream;

importjava.io.DataOutputStream;

importjava.io.IOException;

importjava.net.Socket;

importjava.net.UnknownHostException;

importandroid.app.Activity;

importandroid.os.Bundle;

importandroid.view.View;

importandroid.widget.Button;

importandroid.widget.Toast;

public class SocketActivity extendsActivity {

/** Called when the activity is firstcreated. */

@Override

public void onCreate(BundlesavedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

Button button =(Button)findViewById(R.id.button);

button.setOnClickListener(newView.OnClickListener() {

@Override

public void onClick(View arg0) {

// TODO Auto-generated method stub

login();

}

});

}

public void login() {

// TODO Auto-generated method stub

try {

//建立一个socket通信端口

Socket socket=new Socket("10.10.10.102",8081);

System.out.println("-------------------------------");

DataOutputStream out=newDataOutputStream(socket.getOutputStream());

DataInputStream din=new DataInputStream(socket.getInputStream());

//通过输出流发出数据

out.writeUTF("<#login#>"+"yy");

System.out .println("-------------------------------");

while(true){

String msg="";

//从输入流读入数据

msg=din.readUTF();

System.out .println(msg);

if(!msg.equals("")){

Toast.makeText(SocketActivity.this, msg,1).show();

out.close();

din.close();

socket.close();

break;

}}}

catch (UnknownHostException e)

{

e.printStackTrace();

}catch (IOException e)

{

// TODO Auto-generated catch block

e.printStackTrace();

}

}

}

服务端:

import java.io.DataInputStream;

import java.io.DataOutputStream;

import java.io.IOException;

import java.net.ServerSocket;

import java.net.Socket;

public class Service {

public static void main(String args[]){

try {

ServerSocket ss = new ServerSocket(8081);

System.out .println("llistening...");

while(true){

Socket socket = ss.accept();

System.out .println("client connected...");

DataInputStream in = new DataInputStream(socket.getInputStream());

DataOutputStream out = newDataOutputStream(socket.getOutputStream());

String oo=in.readUTF();

System.out .println(oo);

if(oo.startsWith("<#login#>"))//解析数据,对数据进行判断

{

String content = oo.substring(9);

if(content.equals("yy"))

{

out.writeUTF("1");

} else

{

out.writeUTF("2");

} in.close();

socket.close();

}

}

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

}

 

好了,以上就是Socket中最常见的字符串通信,如果要传输图片或者文件的话,基本思路也都是转化为字符串数组在进行传输。

好了,下面我们就讨论一下Socket通信过程中最常见的问题及解决方案

Socket通信过程中最常见的问题及解决方案

问题一、客户端接收数据超时,导致客户端无响应,一段时间后,弹出进程错误。

问题分析:造成该问题主要有两个方面, 一个是服务器繁忙,没有及时返回数据;二是服务器有返回数据,但由于网络繁忙,客户端没能及时接收到数据。开始,程序在等待数据接收,从用户的视觉,则是‘卡机’状态,当长时间没有响应后,在系统保护机制下,系统会自动发送一个singal3 信号杀掉该进程,此时会弹出一个询问是否关闭程序的进程错误对话框。

解决办法:有2个,1是自设超时时间,避免延时过长,造成‘卡机’的假象。同时逃避系统的查杀;2是使用异常处理方法,Catch 到超时错误,并通知用户访问超时。

 

问题二:服务器接收数据超时,导致服务器崩溃

问题分析:造成该问题主要是由于网络繁忙,和网络信号不好,导致服务器一直处在数据接收的状态,超出服务器的超时时间后,服务器会崩溃。考虑到该问题的出现对用户是隐形的,即出现该问题时,用户不会察觉到是服务器出现崩溃,而只会看到网络繁忙的提醒。

解决办法:服务器采用多线程机制,把数据接收代码写到子线程里,避免因该问题造成主线程出错,导致服务器崩溃。

 

问题三:IP 复用问题,也容易造成服务器崩溃

问题分析:造成该问题的主要原因是由于socket 是采用套接字方式进行数据传输的机制,如果有多个ip相同的客户端同时访问服务器,容易导致数据传输紊乱和服务器崩溃。客户端出现相同ip 的原因是当几个客户端接入同一个局域网,并同时访问‘外网’的服务器,此时,这些客户端表现出相同的IP。

解决办法:1是服务器采用多线程机制 2是尽量避免特殊的网络环境。

 

如果大家有遇到其他的什么问题,我们可以共同讨论,共同进步

 

另外:Socket通信还应该注意一下几点:

1、客户端设计过程中,网络访问权限申请:

<uses-permissionandroid:name=”android.permission.INTERNET ”/>

2、服务器的端口最好选择比较靠后,避免与系统中其他功能冲突

3、客户端代码中的端口要与服务器的监听端口一致

4、 完成数据传输最好关闭流

5、服务器最好使用多线程操作

0 0
原创粉丝点击