CSDN博客不再更新,新内容请移步掘金:https://juejin.im/user/5886d699128fe1006c455fb6,有问题也请直接发邮件至guoxiaoxingse@163.com...

【Android应用开发技术:网络通信】Socket套接字

作者:郭孝星
微博:郭孝星的新浪微博
邮箱:guoxiaoxingv@163.com
博客:http://blog.csdn.net/allenwells
Github:https://github.com/guoxiaoxing

Socket 是应用层与 TCP/IP 协议族通信的中间软件抽象层,它是一组接口。Socket把复杂的 TCP/IP 协议族隐藏在 Socket 接口后面,对用户来说,一组简单的接口就是全部,让 Socket 去组织数据,以符合指定的协议。Socket 用于描述 IP 地址和端口,是一个通信链的句柄。应用程序通常通过套接字向网络发出请求或者应答网络请求。

一 Socket基础

Socket的jiben操作包括以下几个方面:

  • 连接远程机器
  • 发送数据
  • 接收数据
  • 关闭连接
  • 绑定端口
  • 监听到达数据
  • 在绑定的端口上接受来自远程机器的连接

服务器要和客户端通信,两者都要实例化一个 Socket。服务器和客户端的 Socket 是不一样的,客户端可以实现连接远程机器、发送数据、接收数据、关闭连接等,服务器还需要实现绑定端口,监听到达的数据,接受来自远程机器的连接。Android 在包 java.net 里面提供了两个类:ServerSocket 和 Socket,前者用于实例化服务器的 Socket,后者用于实例化客户端的 Socket。在连接成功时,应用程序两端都会产生一个 Socket 实例,操作这个实例,完成客户端到服务器所需的会话。

1.1 Socket

1.1.1 Socket构造方法

Socket的构造方法有以下几种:

Soccket():

创建一个新的未连接的 Socket。

Socket(Proxy proxy):

使用指定的代理类型创建一个新的未连接的 Socket

Socket(String dstName,int dstPort);

使用指定的目标服务器的 IP 地址和目标服务器的端口号,创建一个新的 Socket。

Socket(String dstName,int dstPort,InetAddress localAddress,int localPort) : 

使用指定的目标主机、目标端口、本地地址和本地端口,创建一个新的 Socket。

Socket(InetAddress dstAddress,int dstPort) :

使用指定的本地地址和本地端口,创建一个新的 Socket。

Socket(InetAddress dstAddress,int dstPort,InetAddress localAddress,int localPort) :

使用指定的目标主机、目标端口、本地地址和本地端口,创建一个新的 Socket。

1.1.2 Socket重要方法

Public InputStream getInputStream()

读出该 Socket 中的数据

public OutputStream getOutputStream() 

向该 Socket 中写入数据

public synchronized void close() 

关闭该 Socket上面是构造 Socket 客户端的几种常用的方法,构造服务器端的 ServerSooket

1.2 ServerSocket

1.2.1 ServerSocket构造方法

erverSocket()

:构造一个新的未绑定的 ServerSocket。

ServerSocket(int port)

构造一个新的 ServerSocket 实例并绑定到指定端口。如果port 参数为 0,端口将由操作系统自动分配,此时进入队列的数目将被设置为 50。

ServerSocket(int port,int backlog)

构造一个新的 ServerSocket 实例并绑定到指定端口,并且指定进入队列的数目。如果 port 参数为 0,端口将由操作系统自动分配。

ServerSocket(int port,int backlog,InetAddress localAddress) 

构造一个新的 ServerSocket‰ ‰实例并绑定到指定端口和指定的地址。如果 localAddress 参数为 null,则可以使用任意地址,如果 port 参数为 0,端口将由操作系统自动分配。

1.2.2 ServerSocket重要方法

public Socket accept ()

等待 Socket 请求,直到连接被打开,该方法返回一个刚刚打开的连接 Socket 对象

public void close()

关闭该服务器 Socket

二 Socket分类

Socket 一般有两种类型::

  • TCP 套接字
  • UDP 套接字

TCP 和 UDP 在传输过程中的具体实现方法不同。两者都接收传输协议数据包并将其内容向前传送到应用层。TCP 把消息分解成数据包(数据报,datagrams)并在接收端以正确的顺序把它们重新装配起来,TCP 还处理对遗失数据包的重传请求,位于上层的应用层要处理的事情就少多了。UDP 不提供装配和重传请求这些功能,它只是向前传送信息包。位于上层的应用层必须确保消息是完整的,并且是以正确的顺序装配的。

2.1 TCP套接字

TCP 建立连接之后,通信双方都同时可以进行数据的传输 ;在保证可靠性上,采用超时重传和捎带确认机制 ;在流量控制上,采用滑动窗口协议,协议中规定,对于窗口内未经确认的分组需要重传 ;在拥塞控制上,采用慢启动算法。

TCP 套接字工作流程示意图如下所示:

这里写图片描述

2.1.1 TCP套接字服务端

TCP套接字服务端具体流程如下所示:

  1. 调用 ServerSocket(int port)创建一个 ServerSocket,并绑定到指定端口上。
  2. 调用 accept(),监听连接请求,如果客户端请求连接,则接受连接,返回通信
    套接字。
  3. 调 用 Socket 类 的 getOutputStream() 和 getInputStream() 获 取 输 出 和 输 入 流,
    开始网络数据的发送和接收。
  4. 关闭通信套接字。

对应代码如下所示:

// 创建一个 ServerSocket 对象
ServerSocket serverSocket = null;
try {
// TCP_SERVER_PORT 为指定的绑定端口,为 int 类型
serverSocket = new ServerSocket(TCP_SERVER_PORT);
// 监听连接请求
Socket socket = serverSocket.accept();
// 写入读 Buffer 中
BufferedReader in = new BufferedReader(new
// 获取输入流
InputStreamReader(socket.getInputStream()));
// 放到写 Buffer 中
BufferedWriter out = new BufferedWriter(new
// 获取输出流
OutputStreamWriter(socket.getOutputStream()));
// 读取接收信息,转换为字符串
String incomingMsg = in.readLine() + System.getProperty("line.separator");
// 生成发送字符串
String outgoingMsg = "goodbye from port " + TCP_SERVER_PORT +
System.getProperty("line.separator");
// 将发送字符串写入上面定义的 BufferedWriter 中
out.write(outgoingMsg);
// 刷新,发送
out.flush();
// 关闭
socket.close();
} catch (InterruptedIOException e) {
// 超时错误
e.printStackTrace();
// IO 异常
} catch (IOException e) {
// 打印错误
e.printStackTrace();
} finally {
// 判定是否初始化 ServerSocket 对象,如果初始化则关闭 serverSocket
if (serverSocket != null) {
try {
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

2.1.2 TCP套接字客户端

TCP套接字客户端具体流程如下所示:

  1. 调用 Socket() 创建一个流套接字,并连接到服务器端。
  2. 调 用 Socket 类 的 getOutputStream() 和 getInputStream() 方 法 获 取 输 出 和 输 入
    流,开始网络数据的发送和接收。
  3. 关闭通信套接字。

对应的代码如下所示:

try {
}
// 初始化 Socket,TCP_SERVER_PORT 为指定的端口,int 类型
Socket socket = new Socket("localhost", TCP_SERVER_PORT);
// 获取输入流
BufferedReader in = new BufferedReader(new
InputStreamReader(socket.getInputStream()));
// 生成输出流
BufferedWriter out = new BufferedWriter(new
OutputStreamWriter(socket.getOutputStream()));
// 生成输出内容
String outMsg = "TCP connecting to " + TCP_SERVER_PORT +
System.getProperty("line.separator");
// 写入
out.write(outMsg);
// 刷新,发送
out.flush();
// 获取输入流
String inMsg = in.readLine() + System.getProperty("line.separator");
Log.i("TcpClient", "received: " + inMsg);
// 关闭连接
socket.close();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();

2.2 UDP套接字

UDP 有不提供数据报分组、组装和不能对数据包排序的缺点,也就是说,当报文发送之后,是无法得知其是否安全完整到达的。UDP 用来支持那些需要在计算机之间传输数据的网络应用,包括网络视频会议系统在内的众多的客户端 / 服务器模式的网络应用都需要使用 UDP 协议。UDP 协议的主要作用是将网络数据流量压缩成数据报的形式。一个典型的数据报就是一个二进制数据的传输单位。

UDP 传输原理示意图如下图所示:

这里写图片描述

2.2.1 UDP 服务端

UDP 服务器端工作的主要步骤如下所示:

  1. 调用 DatagramSocket(int port) 创建一个数据报套接字,并绑定到指定端口上。
  2. 调用 DatagramPacket(byte[]buf,int length),建立一个字节数组以接收 UDP 包。
  3. 调用 DatagramSocket 类的receive()方法,接收UDP包。
  4. 关闭数据报套接字。

对应代码如下所示:

byte[] lMsg = new byte[MAX_UDP_DATAGRAM_LEN];
// 实例化一个 DatagramPacket 类
DatagramPacket dp = new DatagramPacket(lMsg, lMsg.length);
// 新建一个 DatagramSocket 类
DatagramSocket ds = null;
try {
// UDP 服务器监听的端口
ds = new DatagramSocket(UDP_SERVER_PORT);
// 准备接收数据
ds.receive(dp);
} catch (SocketException e) {
} catch (IOException e) {
e.printStackTrace();
} finally {
// 如果 ds 对象不为空,则关闭 ds 对象
if (ds != null) {
ds.close();
}
}

2.2.2 UDP客户端

UDP 客户端工作的主要步骤如下所示:

  1. 调用 DatagramSocket() 创建一个数据包套接字。
  2. 调用 DatagramPacket(byte[]buf,int offset,int length,InetAddress address,int port),
    建立要发送的 UDP 包。
  3. 调用 DatagramSocket 类的 send() 发送 UDP 包。
  4. 关闭数据报套接字。

对应的代码如下所示:

// 定义需要发送的信息
String udpMsg = "hello world from UDP client " + UDP_SERVER_PORT;
// 新建一个 DatagramSocket 对象
DatagramSocket ds = null;
try {
// 初始化 DatagramSocket 对象
ds = new DatagramSocket();
// 初始化 InetAddress 对象
InetAddress serverAddr = InetAddress.getByName("127.0.0.1");
DatagramPacket dp;
// 初始化 DatagramPacket 对象
dp = new Datagram
Packet(udpMsg.getBytes(), udpMsg.length(), serverAddr, UDP_SERVER_PORT);
// 发送
ds.send(dp);
}
// 异常处理
// Socket 连接异常
catch (SocketException e) {
e.printStackTrace();
// 不能连接到主机
}catch (UnknownHostException e) {
e.printStackTrace();
// 数据流异常
} catch (IOException e) {
e.printStackTrace();
// 其他异常
} catch (Exception e) {
e.printStackTrace();
} finally {
// 如果 DatagramSocket 已经实例化,则需要将其关闭
if (ds != null) {
ds.close();
    }
}

举例

Socket聊天室

服务端功能:ChatServer 类负责开启 Server 端服务;ReceiveMsg 负责接收消息;
SendMsg 负责发送消息;Server 端响应请求,向 Client 端返回数据。

ChatServer主要代码如下所示:

// Socket 聊天服务器端
// 继承 Thread 类,创建一个线程
public class ChatServer extends Thread {
private ChatServer() throws IOException {
// 创建 Socket 服务器
CreateSocket();
}
// 重写 run() 方法
public void run() {
Socket client;
// 定义接收的文本
String txt;
try {
// 始终在监听
while (true) {
// 开启线程,实时监听 socket 端口
// 获取应答消息
client=ResponseSocket();
// 响应客户端连接请求
while(true) {
// 接收客户端消息
txt=ReceiveMsg(client);
System.out.println(txt);
// 连接获得客户端发来消息
// 并将其显示在 Server 端的屏幕上
SendMsg(client,txt);
// 向客户端返回消息
if(true)break;
// 中断,继续等待连接请求
}
// 关闭此次连接
CloseSocket(client);
}
}
catch (IOException e) {
System.out.println(e);
}
}
}
// 定义一个 ServerSocket 对象
private ServerSocket server = null;
// 定义端口号
private static final int PORT = 5000;
// 定义写 Buffer
private BufferedWriter writer;
// 定义读 Buffer
private BufferedReader reader;
// 实例化 ServerSocket
private void CreateSocket() throws IOException{
server = new ServerSocket(PORT, 100);
}
// 接收消息
private Socket ResponseSocket() throws IOException{
Socket client = server.accept();
return client;
}
// 关闭打开的连接和缓存
private void CloseSocket(Socket socket) throws IOException{
reader.close();
writer.close();
socket.close();
}
// 封装发送消息的方法
private void SendMsg(Socket socket,String Msg) throws IOException{
// 要发送的消息写入 BufferedWriter 中
writer = new BufferedWriter(
new OutputStreamWriter(socket.getOutputStream()));
// 添加空行分隔符
writer.write(Msg+"\n");
// 刷新,发送
writer.flush();
}
// 接收消息的方法
private String ReceiveMsg(Socket socket) throws IOException{
// 保存到读 Buffer
reader = new BufferedReader(
// 将接收到的信息写入缓冲区
new InputStreamReader(socket.getInputStream()));
// 将接收到的信息保存到字符串中
String txt="Sever send:"+reader.readLine();
// 以字符串的方式返回接收到的信息
return txt;
}

启动服务器的代码如下所示:

// 创建聊天服务器
ChatServer chatserver = new ChatServer ();
// 检测服务器是否已经启动,如果没有则启动服务器
if (chatserver != null) {
chatserver.start();
}

客户端功能:客户端发起连接请求,并向服务器端发送数据 ;客户端接收服务器端的数据。

主要代码如下所示:

// 客户端获取消息类
private Socket RequestSocket(String host,int port)throws UnknownHostException,
IOException{
// 新建 Socket 类
Socket socket = new Socket(host, port);
return socket;
}
// 客户端发送消息类
private void SendMsg(Socket socket,String msg) throws IOException{
// 将要发送的消息写入 Buffer 中
BufferedWriter writer = new BufferedWriter(new
OutputStreamWriter(socket.getOutputStream()));
// 格式转换
writer.write(msg.replace("\n", " ")+"\n");
// 刷新,发送
writer.flush();
}
// 客户端接收消息
private String ReceiveMsg(Socket socket) throws IOException{
// 写入读 Buffer 中
BufferedReader reader = new BufferedReader(new
// 获取接收的消息到数据流中
InputStreamReader(socket.getInputStream()));
// 读取消息到字符串中
String Msg=reader.readLine();
// 以字符串的方式返回消息
return Msg;
}

2.3 FTP

文件传输协议(File Transfer Protocol,FTP)是用于在网络上进行文件传输的一套标
准协议。它属于网络传输协议的应用层。

FTP 是一个 8 位的客户端 - 服务器协议,能操作任何类型的文件。FTP 服务一般运行在 20 和 21 两个端口。端口 20 用于在客户端和服务器之间传输数据流,而端口 21 用于传输控制流,并且是命令通向 FTP 服务器的进口。当数据通过数据流传输时,控制流处于空闲状态。运行 FTP 服务的许多站点都开放匿名服务,在这种设置下,用户不需要账号就可以登录服务器,默认匿名用户的用户名是 anonymous,这个账号不需要密码 ;不开放匿名服务的则要求输入用户名和密码。

FTP 支持以下两种模式 :

  • 主动模式 : FTP 客户端向服务器的 FTP 控制端口 ( 默认是 21 端口 ) 发送请求 ,
    服务器接受连接 , 建立一条命令链路 , 当需要传送数据时候 , 客户端在命令链路上
    用 PORT 命令通知服务器 , 服务器从 20 端口向客户端的该端口发送连接请求 , 建立
    一条数据链路来传送数据 。 在数据链路建立的过程中因为是服务器主动请求的 , 所
    以称为主动模式 。
  • 被动模式 : FTP 客户端向服务器的 FTP 控制端口发送连接请求 , 服务器接收连
    接 , 建立一条命令链路 , 当需要传送数据时候 , 服务器在命令链路上用 PASV 命令
    通知客户端 ; 客户端向服务器的该端口发送连接请求 , 建立一条数据链路来传送数
    据 。 在数据链路建立的过程中因为是服务器被动等待客户端请求的 , 所以称为被动
    模式 。

在 Android 中可以使用第三方的库来操作 FTP,这里使用 Apache 的包,具体流程如下所示:

  1. 在项目引入包 commons-net-3.2.jar 之后,导入需要其中的 FTPClient 类“`
  2. 初始化 FTPClient,“`
  3. 设置登录地址和端口号
  4. 设置登录用户名和密码,
  5. 设置文件类型和采用被动传输方式,
  6. 传输文件
  7. 关闭连接,

主要代码如下所示:

// 导入需要的 FTPClient 类
import org.apache.commons.net.ftp.FTPClient;
// 初始化 FTPClient 对象
FTPClient ftpClient = new FTPClient();
try {
// 连接到指定的 FTP 服务器
ftpClient.connect(InetAddress.getByName(SERVER));
// 使用用户名和密码登录 FTP 服务器
ftpClient.login(USERNAME, PASSWORD);
// 检测返回的字符串里面是否包含 250
// 250 响应代码表示“行为完成”
if (ftpClient.getReplyString().contains("250")) {
// 设置文件类型
ftpClient.setFileType(
// 默认使用的是 ASCII 编码的,这里设置为二进制文件
org.apache.commons.net.ftp.FTP.BINARY_FILE_TYPE);
// 定义一个输入缓冲区
BufferedInputStream buffIn = null;
// 将文件加载到缓冲区中
buffIn = new BufferedInputStream(new
FileInputStream(FULL_PATH_TO_LOCAL_FILE));
// 设置客户端 PASV 模式 ( 客户端主动连服务器端;端口用 20)
ftpClient.enterLocalPassiveMode();
// 存储文件,返回是否成功
boolean result = ftpClient.storeFile(localAsset.
getFileName(), progressInput);
// 关闭缓冲区
buffIn.close();
// 登出
ftpClient.logout();
// 断开连接44 
ftpClient.disconnect();
}
} catch (SocketException e) {
} catch (UnknownHostException e) {
} catch (IOException e) {
}

2.4 Telnet

elnet 协议是 TCP/IP 协议族中的一员,是 Internet 远程登录服务的标准协议和主要方
式。它为用户提供了在本地计算机上完成远程主机上的工作的能力。

在终端使用者的计算机上使用 Telnet 程序,用它连接到服务器。终端使用者可以在 Telnet 程序中输入命令,这些命令会在服务器上运行,就像直接在服务器的控制台上输入一样。通过 Telnet 在本地就能控制服务器。要开始一个 Telnet 会话,必须输入用户名和密码来登录服务器。Telnet 是常用的远程控制 Web 服务器的方法。

在 Android 中可以使用第三方的库来操作 Telnet,这里使用 Apache 的包,具体流程如下所示:

  1. 在项目引入包 commons-net-3.2.jar 之后,导入其中的 TelnetClient 类,
  2. 初始化 TelnetClient,
  3. 打开连接,
  4. 读写数据,
  5. 断开 Telnet 连接

Android Telnet客户端代码如下所示:

/ 定义一个 TelnetClient
TelnetClient telnet;
telnet = new TelnetClient();
try{
// 建立连接
telnet.connect(remoteip, remoteport);
}
catch (IOException e) {
e.printStackTrace();
// 捕获到异常,停止程序
System.exit(1);
}
IOUtil.readWrite(telnet.getInputStream(), telnet.getOutputStream(),
System.in, System.out);
try{
// 断开连接
telnet.disconnect();
}
catch (IOException e) {
e.printStackTrace();
}
}

IOUtil封装了一些读写操作,如下所示:

// IOUtil 类封装了一些读写操作
public final class IOUtil{
public static final void readWrite(final InputStream remoteInput,
final OutputStream remoteOutput,
final InputStream localInput,
final OutputStream localOutput){
// 定义读写的线程
Thread reader, writer;
// 定义读线程的具体操作
reader = new Thread(){
@Override
public void run(){
int ch;
try{
// 判断没有被中断的时候
while (!interrupted()
// 不是结束标志
&& (ch = localInput.read()) != -1) {
// 写字节到远程输入里面
remoteOutput.write(ch);
// 刷新,发送
remoteOutput.flush();
}
}
catch (IOException e){
e.printStackTrace();
}
}
};
// 定义写线程的具体操作
writer = new Thread(){
@Override
public void run(){
try{
// 把数据从输入流复制到输出流
Util.copyStream(remoteInput,
localOutput);
}
catch (IOException e){
e.printStackTrace();
// 发生异常的时候退出该操作
System.exit(1);
}
}
};
// 设置 writer 线程
writer.setPriority(Thread.currentThread().getPriority() + 1);
// 启动 writer 线程
writer.start();
// 设置 reader 为后台运行
reader.setDaemon(true);
// 启动 reader 线程
reader.start();
try{
// 使得 writer 线程完成 run() 方法后,再执行 join() 方法后的代码
writer.join();
// 中断 reader 线程
reader.interrupt();
}
catch (InterruptedException e){
}
阅读更多
想对作者说点什么? 我来说一句

android ,套接字socket的用法

2011年10月28日 2KB 下载

没有更多推荐了,返回首页

不良信息举报

【Android应用开发技术:网络通信】Socket套接字

最多只允许输入30个字

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭