Android网络服务---Socket网络通信

       Sock又称“套接字”,应用程序通常通过“套接字”向网络发出请求或者应答网络请求。抽象出来,Socket实质上是提供了进程通信的的端点。在进程通信之前,双方首先必须各自创建一个端点,否则是没有办法建立联系并相互通信的。正如打电话之前,双方必须各自拥有一台电话机一样。


 每一个Socket有一个相关描述,这个描述包含:协议,本地地址,本地端口三个内容。

一个完整的Socket有一个本地唯一的Socket号,由操作系统分配。

最重要的是,Socket是面向客户/服务器模型而设计的,针对客户和服务器程序提供不同的Socket系统调用。客户随机申请一个Socket(相当于一个想打电话的人可以在任何一台入网电话上拨号呼叫),系统为之分配一个Socket号:服务器拥有全局公认的Socket,任何客户都可以向它发出连接请求和信息请求(相当于一个被呼叫的电话拥有一个呼叫方知道的电话号码)。Socket利用客户/服务器模式巧妙地解决了进程之间建立通信连接的问题。


1.连接过程

根据连接启动的方式以及本地套接字要连接的目标,套接字之间的连接过程可以分为三个步骤:服务器监听,客户端请求,连接确认。

(1)服务器监听:此时服务器端套接字并不定位具体的客户端套接字,而是处于等待连接的转态,实时监控网络状态。

(2)客户端请求:是指由客户端的套接字提出连接请求,要连接的目标是服务器端的套接字。为此,客户端的套接字必须首先描述它要连接的服务器的套接字,指出服务器端套接字的地址和端口号,然后再向服务器端套接字提出连接请求。

(3)连接确认:是指当服务器端套接字监听到客户端套接字的连接请求,它就响应客户端套接字的请求,建立一个新的线程,把服务器端套接字的描述发给客户端,一旦客户端确认了此描述,连接就建立好了。而服务器端套接字继续处于监听状态,继续接受其他客户端套接字的连接请求。

Java中的Socket的工作过程可以分为四个基本步骤:

(1)创建Socket:

(2)打开连接Socket的输入流或输出流:

(3)对Socket输入/输出流进行处理(读/写操作);

(4)关闭Socket。


在Java.net包中,有Socket和SocketServer两个类,它们分别负责客户端和服务器端。

它们两者的额构造方法如下:

Socket();

Socket(InetAddress address,int port);

Socket(InetAddress address,int port,boolean stream);

Socket(String host,int port);

Socket(String host,int port,boolean stream);

Socket(SocketImpl impl);

Socket(String host,int port,InetAddress localAddr,int localPort)

Socket(InetAddress address,int port,InetAddress localAddr,int localPort)


ServerSocket(int port);

ServerSocket(int port,int backlog);


ServerSocket(int port,int backlog,InetAddress bindAddr)只接收指定地址的指定接口发过来的数据。

其中address.host和port分别是双向连接中另一方的IP地址.主机名和端口号,stream指明socket是流socket还是数据报socket,localPort表示本机主机的端口号,localAddr和bindAddr是本地机器的地址(ServerSocket的主机地址),impl是socket的父类,既可以用来创建serverSocket又可以用来创建Socket。count则表示服务器所能支持的最大连接数。传入连接指示(对连接的请求)的最大队列长度被设置为backlog参数,如果队列满时收到连接指示,则拒绝该连接。


(1)创建Socket。

我们往往使用构造方法创建Socket,例如:

Socket  client=new Socket)(“127.0.01.”,80);

ServerSocket server=new ServerSocket(80);


也可以使用空的构造方法,例如:

Socket socket=new Socket();

SocketAddress  remoteAddr=new InetSocketAddress(“localhost”,8000);

socket.connect(remoteAddr,60000);//等待建立连接的超时时间为1分钟

以上代码用于连接到本地机器上的监听8000端口的服务器程序,等待连接的最长时间为1分钟

如果在1分钟内连接成功则connet()方法顺利返回;如果在1分钟内出现某种异常,则抛出该异常;

如果超过1分钟,即没有连接成功,也没有出现其他异常,那么会抛出SocketTimeoutException.Socket类的connect(SocketAddress endpoint,int timeout)方法负责连接服务器,参数endpoint指定服务器的地址,参数timeout设定超时数据,以毫秒为单位。如果参数timeout设为0,表示永远不会超时,默认是不会超时的。

        注意,在选择端口时,必须小心。每一个端口提供一种特定的服务,只有给出正确的端口,才能获得相应的服务。0~1023的端口号为系统所保留,例如http服务的端口号为80,telnet服务的端口号为21,ftp服务的端口号为23,所以我们在选择端口号时,最好选择一个大于1023的数以防止发生冲突。

在创建socket时如果发生错误,将产生IOException,在程序中必须对之作出处理。所以在创建Socket或ServerSocket时必须捕捉或抛出例外。所以编程时常使用try...catch块。

例如:

try{

//socket的一些处理

                    }catch(IOException e){

       //捕捉系统异常时的处理

                     }


(2)通过构造的Socket获取Socket的信息,如输入,输出流等。

在一个Socket对象中同时包含了远程服务器的IP地址和端口信息,以及客户本地的IP地址和端口信息。此外,从Socket对象中还可以获得输出流和输入流,分别用于向服务器发送数据,以及接收从服务器端发来的数据。以下方法用于获取Socket的有关信息:

getInetAddress():获得远程服务器的IP地址。

getPort():获得远程服务器的端口。

getLocalAddress():获得客户本地的IP地址。

getLocalPort():获得客户本地的端口。

getInputStream():获得输入流。如果Socket还没有连接,或者已经关闭,或者已经通过shutdownInput()方法关闭输入流,那么此方法会抛出IOException。

getOutputStream():获得输出流,如果Socket还没有连接,或者已经关闭,或者已经通过shutdownOutput()方法关闭输出流,那么此方法会抛出IOException。

记住:输入\输出流一般是以字节为单位的,我们有时需要把字符串转化为字节进行处理。

(3)对Socket输入/输出流进行处理(读/写操作)。

使用getInputStream()方法可以获得一个InputStream实例,然后使用InputStream的一些方法进行处理输入流。

使用getInputStream()方法可以获得一个InputStream实例,然后使用InputStream的一些方法进行处理输出流。



2.流的概念

流是字节序列的抽象概念

文件是数据的静态存储形式,而流是指数据传输时的形态,而流是指数据传输时的形态。

流分为两个大类:节点流和过滤流(过滤流也叫处理流)

1.InputStream类

程序可以从中连续读取字节的对象叫输入流,在JAVA中,用InputStream类来描述所有输入流的抽象概念。FileInputStream类是InputStream类的。

InputStream类的方法:

int read()从输入流中读取一个字节的内容,并且把这个内容以整数的形式返回。如果碰到流的结束处,那么返回的值就是“-1”;如果流没有结果,但临时没有数据可读,那read方法就将阻塞运行程序的执行过程,直到流中有新的数据可读。(流可以看作是一个通道)。read方法将读取的每一个字节复制到int类型(int类型占用4个字节)中的最低字节其他高字节的部分全部设置为零。

int read(byte[] b)用于从输入流读取若干个字节的内容到字节数组b中,最多读取的字节个数就是这个字节数组的长度,由于流中不一定有这么多的字节可读。

int read(byte[]b,int off,int len)这每次读取len个字节,并放入到字节数组b中,并且是以角标为off的位置依次放入。实际上读取的个数以返回值为准。

long skip(long  n)跳过输入流中的n个字节,并返回实际跳过的字节数。这个方法主要用于包装流中,包装流中流可以跳跃,一般的低层流不能跳跃。

int  available()返回当前输入流中可读的字节数,在使用时我们可以先用available方法来判断流中是否有可读数据,再用read方法进行读取,这样可以防止程序发生阻塞。

void mark(int readlimit)在输入流中建立一个标记,readlimit表示在建立标记地方开始最多还能读取多少个字节的内容(用于包装类的方法)

void reset()与mark方法配合使用,用mark方法在a处做标记后再读取b个字节并调回reset方法,当下次再度时就从a的地方开始读取,也就是说,reset方法是让指针回到以做的标记处。

boolean markSupported()返回当前流对象是否支持mark和reset操作

void close()用于完成一个流的所有操作以后,关闭这个流,放弃与这个流相关的所有资源。

InputStream是抽象类,程序中实际使用的是InputStream的各种子类对象,不是所有子类都会支持InputStream中定义的某些方法。比如skip。mark。reset在节点流中不适合,它们用于包装流。

2 OutputStream类

程序可以向其中连续写入字节的对象叫输出流,在JAVA中,用OutputStream类来描述所有输出流的抽象概念。FileOutputStream类是OutputStream类的子类。

OutputStream类的方法:

void write(int b)  将一个整数中的最低一个字节中的内容写到输出流中,高字节部分被弃。

void write(byte[] b)将字节数组中的所有内容写入到输出流对象中。

void write(byte[] b,int off,int len)将字节数组b中从off位置开始的len个字节写入到输出流对象中。

voId flush()将内存缓冲区的内容完全清空,新输入到I/O设备当中。

void close()关闭输出流对象。

(4)关闭Socket.

使用close()方法关闭。

当客户与服务器的通信结束,应该及时关闭Socket,以释放Socket占用的包括端口在内的各种资源。Socket的close()方法负责关闭Socket。当一个Socket对象被关闭,就不能再通过它的输入流和输出流进行I/O操作,否则会导致IOException。

为了确保关闭Socket的操作总是被执行,强烈建议把这个操作放在finally代码块中:

举个例子:
Socket socket=null;

try{

socket=new Socket(127.0.0.1,80);

//执行接收和发送数据的操作

。。。

}catch(IOException e){

e.printStackTrace();

}finally{

try{

if(socket!=null) socket.close();

}catch(IOException e){

eprintStackTrace();}

}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值