接上一篇文章:Android网络通信(2):HTTP通信.

Socket通信
如果要开发一款多人联网的游戏,Http已经不能很好的满足要求了。这时就需要Socket通信了。
Socket通常称为"套接字",用于描述IP地址和端口,是一个通信链的句柄。应用程序通常通过“套接字”向网络发出请求或者应答网络请求。它是通信的基石,是支持TCP/IP协议的网络
通信的基本操作单元。它是网络通信过程中端点的抽象表示,包含进行网络通信必需的5种信息:
连接使用的协议、本地主机的IP地址、本地进程的协议端口、远地主机的IP地址、远地进程的协议端口。
1、Socket传输模式
Socket有两种主要操作方式:面向连接的 和 无连接的
面向连接的Socket操作就像一部电话机,必须要等对方接上之后才能通话。所有的数据到达的顺序与它出发时的顺序是一样的。面向连接的操作使用TCP协议,即此模式下必须先连接上目的地的Socket,连接上后Socket就可以使用一个流接口进行打开、读、写、关闭等操作。所有所发信息都会在另一端以同样的顺序被接收。安全性高,但效率低。
无连接的就像是一个邮件投递,没有保证,多个邮件到达时的顺序可能与出发时的顺序不一样。无连接的操作使用数据报协议,一个数据报是一个独立的单元,它包含了这次投递的所有信息。可将其想象成一个信封,这个模式下的Socket不需要连接一个目的Socket,它只是简单的投出数据报。无连接的操作时快速和高效的,但是数据安全性不高。
到底用哪种由应用程序的需要决定。如:文件服务器需要数据的正确性和有序性,因选面向连接的。
2、Socket编程原理
Socket构造
java.net包中提供两个类Socket和ServerSocket,分别用来表示双向连接的客户端和服务器端.

两类中其构造方法如下:

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 双向连接中另一方的IP地址
host 双向连接中另一方的主机名
port 双向连接中另一方的端口号
stream 指明Socket是流Socket还是数据报Socket
localPort 本地主机的端口号
localAddr和bindAddr是本地机器的地址(ServerSocket的主机地址)
impl 是Socket的父类,即可以用来创建ServerSocket,又可以用来创建Socket
例:

//count表示服务端所支持的最大连接数
Socket client = new Socket("192.168.1.110",54321);
ServerSocket server = new ServerSocket(54321);


注意:在选择端口时每一个端口对应一个服务,只有给出正确的端口,才能获得相应的服务。0~1023的端口号为系统所保留,例如http服务的端口号为80,telent服务的端口号为21,ftp服务的端口号为23,所以选择端口号时最好选择一个大于1023的数 如上的54321,防止发生冲突。在创建Socket时如果发生错误,将产生IOException,所以在创建Socket
和ServerSocket时必须捕获或抛出异常。
Socket 客户端
要想使用Socket来与一个服务器通信,就必须先在客户端创建一个Socket,并指出需要连接的服务器端的IP地址和端口,代码如下:

try{
Socket socket = new Socket("192.168.1.110",33221);//"192.168.1.110"是IP地址,33221是端口号
}catch(IOException e){
}


ServerSocket 服务器端
创建一个服务器端的代码:

ServerSocket server = null;
try{


server = new ServerSocket(33221);//服务器端在33221端口号监听客户请求,在这里服务器端只能接收一个请求,接收后服务器端就退出了。实际的应用中总是让他不
停地循环接收,一旦有客户请求,服务器端总是会创建一个服务器线程来服务新来的客户,而自己则继续监听。

}catch(IOException e){ 
}
try{


Socket socket = new server.accpet();//accpet()为一个阻塞函数,即该方法被调用后将等待客户的请求,直到有一个客户启动并请求连接到相同的端口,然后accept
返回一个对应于客户端的Socket.这时,客户方和服务方都建立了用于通信的Socket,接下来就由各个Socket分别打开各自的输入
输出流。

}catch(IOExcetion e){
}


输入、输出流
Socket 提供了getInputStream()和getOutPutStream()来得到对应的输入(输出)流以进行读(写)操作,这两个方法分别返回InputStream和OutputStream类对象。
为了便于读(写)数据,可以在返回输入、输出流对象上建立过滤流。如:DataInputStream、DataOutPutStream、或PrintStream类对象。对于文本方式流对象,可以采用
InputStreamReader和OutputStreamWriter、PrintWirter处理 代码如下:

PrintStream os = new PrintStream(new BufferedOutputStream(Socket.getOutputStream()));
DataInputStream is = new DataInputStream(socket.getInputStream());
PrintWriter out = new PrintWriter(socket.getOutStream(),true);
BufferedReader in = new ButfferedReader(new InputStreamReader(Socket.getInputStream()));


关闭Socket和流
在Socket使用完毕后需要将其关闭,以释放资源。
注意:在关闭Socket之前,应将与Socket相关的所有的输入、输出流先关闭,以释放资源。要注意关闭的顺序。
os.close();//输出流先关闭
is.close();//输入流其次
socket.close();//最后关闭Socket

通过上例总结了一下:
使用Socket实现客户端的步骤;
1、通过IP地址和端口实例化Socket,请求连接服务器
2、获取Socket上的流以进行读写
3、把流包装进BufferReader/PrintWriter的实例
4、对Socket进行读写
5、关闭打开的流
创建服务器的步骤:
1、指定端口实例化一个ServerSocket
2、调用ServerSocket的accept()以在等待连接期间造成阻塞
3、获取位于该层Socket的流以进行读写操作
4、将数据封装成流
5、对Socket进行读写
6、关闭打开的流

Socket应用---简易聊天室
直接看我以前的帖子:http://www.apkbus.com/android-116858-1-1.html

网络通信的中文乱码问题
要想解决Java中文乱码问题,需了解如下内容:
字符:是文字与符号的总称,包括文字、图形符号、数学符号等。
字符集:就是一组抽象字符的集合。字符集常常和一种具体的语言文字对应起来,该文字中所有字符或者大部分字符就构成了该字符集。比如英文字符集、繁体汉字字符集、日文字符集等。
字符编码:计算机要处理各种字符,就需要将字符和二进制内码对应起来,这种对应关系就是字符编码。要确定编码首先要确定字符集,并将字符集内的字符排序,然后和二进制数字对应起来。根据字符集内字符的多少,确定用几个字节来编码。
ASCII编码是目前用得最广的字符集及编码。
Unicode编码是计算机上使用的字符编码。UTF-8就是Unicode编码的实现方式。
GB2312字集是简体字集
BIG5字集台湾繁体字集
GBK字集是简繁字集
GB18030是国家制定的一个强制性大字集标准,它的推出使汉字有了统一的标准。
Linux系统默认使用的是ISO-8859-1编码
Win32系统默认使用的是GB2312编码
网络通信中,产生乱码的原因主要是通信过程中使用了不同的编码方式:如:服务器中的编码方式、传输过程中的编码方式、传输到达终端设备的编码方式。因此在传输过程中至少需要两次编码转换。首先从服务器编码转换为网络编码,再从网络编码转换为终端设备编码。在转换过程中发生任何情况都可能引起编码混乱,可以通过两种方式来避免编码混乱:
第一种:由于大部分终端设备都支持Unicode字符集,所以在连接网页时,我们希望网页数据在网络传输时使用UTF-8方式传输,这样就可以将UTF-8转换成Unicode字符集了。
下面为我们将通信过程中得到的流先转换为字节,然后再将字节按GB2312的方式进行转换得到字符串。

InputStream is = conn.getInputStream();
BufferedInputStream bis =new.BufferedInputStream(is);
byte bytearray[] = new byte[1024];
int current = -1;
int i = 0;
while((current = bis.read()) != -1){
bytearray[i] = (byte) current;
i++;
}
resultData = new String(bytearray,"GB2312");//resultData字符串便可显示中文效果


第二种:在数据传输过程中使用ISO-8859-1字符集,这样就是直接使用ASCII编码方式,当然在传递到终端设备时,需要将其数据反转才能正常显示。
下面将一个字符串按ISO-8859-1字符集进行转换。

public static String FormatStr(String str){
if(str = null||str.length() ==0){
return "";
}
try{
return new String(str.getBytes("ISO-8859-1"),"bgk");//使用getBytes("编码方式") 对汉字进行重编码,得到它的字节数组。再使用new String(Bytes[],"解码方式")


来对字节数组进行相应的解码。其实只要掌握了什么时候应该编码,什么时候应该解码,怎么编码、解码就不怕中文乱码问题了。

}catch(UnsupportedEncodingException ex){
return str;
}
}

代码下载链接:http://www.apkbus.com/forum.php?mod=viewthread&tid=116890&page=1&extra=#pid1121721