转载

Java网络编程-初始Socket

Socket套接字计算机网络通信的基本技术之一。大多数基于网络的软件,如浏览器、即时通讯工具(QQ)或者P2P下载(迅雷)都是基于Socket实现的。本文介绍了Socket的一些基础知识点,对UDP协议没有过多的涉及,简要分析了Socket和HTTP,最后有一个类似于QQ多人聊天的小示例可以下载 示例源代码 查看。

Socket介绍

在了解Socket之前,首先要了解什么是客户端/服务器(client/server)模式。服务器通常采用高性能的PC、工作站或小型机,并采用大型数据库系统,如ORACLE、SYBASE、InfORMix或 SQL Server。客户端需要安装专用的客户端软件。客户端与服务器要进行通信,提供它们之间互相通信的接口就是Socket,所以说Socket本身并不是一种协议,而是基于一组协议设计而提供的对外操作的接口。

Socket可以说是对TCP/IP协议的封装和应用(程序员层面上),但是它也适用于其它协议如UDP协议,Socket 是一种应用接口, TCP/IP 是网络传输协议,虽然接口相同, 但是不同的协议会有不同的服务性质。创建Socket 连接时,可以指定使用的传输层协议,Socket 可以支持不同的传输层协议(TCP 或UDP ),当使用TCP 协议进行连接时,该Socket 连接就是一个TCP 连接。因此也可以说Socket跟TCP/IP协议没有必然的联系。Socket的出现只是可以更方便的使用TCP/IP 协议栈而已。

Java语言中Socket通信机制采用了IO流操作模型。首先通信的双方,客户端和服务器需要建立Socket连接;之后双方都有各自的Socket对象。该Socket对象包含两个流:一个是输入流InputStream,其作用是接收数据;另一个是输出流OutputStream,作用是向外发送数据。

Java为TCP协议提供了两个类:Socket类和ServerSocket类。一个Socket实例代表了TCP连接的一端。一个TCP连接(TCP connection)是一条抽象的双向信道,两端分别由IP地址和端口号确定。在开始通信之前,要建立一个TCP连接,这需要先由客户端TCP向服务器端TCP发送连接请求。ServerSocket实例则监听TCP连接请求,并为每个请求创建新的Socket实例。也就是说,服务器端要同时处理ServerSocket实例和Socket实例,而客户端只需要使用Socket实例。

Socket与HTTP区别

网络七层协议物理层、数据链路层、网络层、传输层、回话层、表示层和应用层,Socket位于传输层,而HTTP位于应用层。HTTP协议即超文本传送协议(Hypertext Transfer Protocol ),是Web联网的基础,也是手机联网常用的协议之一,HTTP协议是建立在TCP协议之上的一种应用。HTTP连接最显著的特点是客户端发送的每次请求都需要服务器回送响应,在请求结束后,会主动释放连接。从建立连接到关闭连接的过程称为“一次连接”。

  • 在HTTP 1.0中,客户端的每次请求都要求建立一次单独的连接,在处理完本次请求后,就自动释放连接。
  • 在HTTP 1.1中则可以在一次连接中处理多个请求,并且多个请求可以重叠进行,不需要等待一个请求结束后再发送下一个请求。

由于HTTP在每次请求结束后都会主动释放连接,因此HTTP连接是一种“短连接”,要保持客户端程序的在线状态,需要不断地向服务器发起连接请求。通常的做法是即使不需要获得任何数据,客户端也保持每隔一段固定的时间向服务器发送一次“保持连接”的请求,服务器在收到该请求后对客户端进行回复,表明知道客户端“在线”。若服务器长时间无法收到客户端的请求,则认为客户端“下线”,若客户端长时间无法收到服务器的回复,则认为网络已经断开。

由于通常情况下Socket连接就是TCP连接,因此Socket连接一旦建立,通信双方即可开始相互发送数据内容,直到双方连接断开。而HTTP连接使用的是“请求—响应”的方式,不仅在请求时需要先建立连接,而且需要客户端向服务器发出请求后,服务器端才能回复数据。

很多情况下,需要服务器端主动向客户端推送数据,保持客户端与服务器数据的实时与同步。此时若双方建立的是Socket连接,服务器就可以直接将数据传送给客户端;若双方建立的是HTTP连接,则服务器需要等到客户端发送一次请求后才能将数据传回给客户端,因此,客户端定时向服务器端发送连接请求,不仅可以保持在线,同时也是在“询问”服务器是否有新的数据,如果有就将数据传给客户端。

  • HTTP协议手机都支持,Socket不一定(Android对于WebSocket的支持很差)。
  • HTTP只能是一问一答(即以request/response 的方式连网收发信息), 而Socket可以双向通讯( 定位到某一URL 后, 就可以双方收发信息, 无需request/response) 。
  • Socket 可能会被防火墙屏蔽, 但HTTP可以穿越防火墙。

HTTP 是基于Socket 通信的子协议, Socket 收发信息自由, 协议都可由使用者定义。 HTTP 在Socket 基础上做了协议规范, 通信只能按照特定的格式去做, 用户可在HTTP 上做自己的子协议, 如网页浏览,webservice,soap等

Socket编程示例

交互过程

Java网络编程-初始Socket

Socket类

Socket常用的构造方法如下:

Socket(InetAddressaddress, int port)throws UnknownHostException, IOException Socket(String host, int port)throws UnknownHostException, IOException 

如果失败会抛出IOException错误。如果成功,则返回Socket对象。InetAddress是一个用于记录主机的类,其静态getHostByName(String msg)可以返回一个实例,其静态方法getLocalHost()也可以获得当前主机的IP地址,并返回一个实例。

host一般为客户端的IP地址,port就是服务器端用来监听请求的端口,也即是服务端的端口。在选择端口时,需要注意一点,就是0~1023这些端口都已经被系统预留了。这些端口为一些常用的服务所使用,比如邮件,FTP和HTTP。当你在编写服务器端的代码,选择端口时,请选择一个大于1023的端口。

当Socket实例化完成,就表示与服务器建立好了连接。但是数据的发送和接收还需要从Socket对象中获取输入流InputStream和输出流OutputStream,IO流的获取主要通过以下方法:

public InputStreamgetInputStream()throws IOException public OutputStreamgetOutputStream()throws IOException 

由上面可以知道,客户端操作主要包括两个步骤:

  1. 建立连接;
  2. 进行流的读写操作。
public class TCPClient {  private static int PORT=1001;  private static String HOST="127.0.0.1";  public static void main(String[] args) {  String str="hello world!";  try {  //建立连接  Socketclient=new Socket(HOST, PORT);  OutputStreamos=client.getOutputStream();  InputStreamis=client.getInputStream();  //IO流写入操作  os.write(str.getBytes());  byte buffer[]=new byte[1024];  int len=-1;  //IO流的读取操作  while((len=is.read(buffer))!=-1){  System.out.println(new String(buffer, 0, len));  }  os.close();  is.close();  }catch (Exception e) {  e.printStackTrace();  }  } } 

ServerSocket类

上述客户端仅仅表示通信的一方,若要真正完成通信,还需要相应的、能根据客户的请求作出相应的服务器程序。服务器这一端的功能实现就是通过ServerSocket实现的,ServerSocket常用的构造方法如下:

ServerSocket(int port)throws IOException 

该构造方法创建一个ServerSocket对象,并绑定到所指定的端口port上面。ServerSocket对象一旦建立,就可以完成其监听端口和等待连接的功能,所采用的实例方法是:

public Socketaccept()throws IOException 

上述该方法是一个阻塞方法, 阻塞的含义是其将一直处于等待状态,直到有连接请求才从方法中返回。方法的返回值是一个Socket对象,服务端就是通过该对象与客户端进行通信的。

服务端的操作一般分为以下三个步骤:

  1. 监听端口
  2. 接收连接
  3. 进行流的读写操作
public class TCPServer {    private static int PORT=1001;    public static void main(String[] args) {  try {  //监听端口  ServerSocketserver=new ServerSocket(PORT);  while(true){  //接收连接  Socketclient=server.accept();    //流的读写操作  InputStreamis=client.getInputStream();  OutputStreamos=client.getOutputStream();    byte buffer[]=new byte[1024];  int len=-1;  while((len=is.read(buffer))!=-1){  System.out.println(new String(buffer, 0, len));  os.write("tcp server".getBytes());  }  }    } catch (IOException e) {  e.printStackTrace();  }  } } 

多线程ServerSocket

在上面的ServerSocket示例代码中,我们采用的是一种顺序处理方式,当有多个客户向服务器发送请求时,服务器是一个一个轮流处理得;若是服务器对每个请求都有较为复杂的处理,就会导致某些客户有较长的等待时间。这非常类似于在 银行排队等候处理个人业务,所排的队伍越长,则等待的时间越长,银行的服务窗口可类比于服务器。那么如何才能减少排队等候的时间呢,可以多开几个服务窗口,相对应的,我们服务器采用多线程处理,为每一个客户端分配一个子线程进行单独处理,由该线程完成客户端的处理工作。

public class TCPServer {    private static int PORT = 1001;    public static void main(String[] args) {  new TCPServer().startUp();  }    public void startUp() {  try {  // 监听端口  ServerSocketserver = new ServerSocket(PORT);  while (true) {  // 接收连接  Socketclient = server.accept();  // 每一个连接代表了一个子线程  new Thread(new ServerThread(client)).start();  }  } catch (IOException e) {  e.printStackTrace();  }  }    private class ServerThread implements Runnable {  private InputStreamis;  private OutputStreamos;  private boolean isRunning = true;    public ServerThread(Socketclient) {  try {  is = client.getInputStream();  os = client.getOutputStream();  } catch (IOException e) {  isRunning = false;  e.printStackTrace();  }    }    public void run() {  while (isRunning) {  byte buffer[] = new byte[1024];  int len = -1;  try {  while ((len = is.read(buffer)) != -1) {  System.out.println(new String(buffer, 0, len));  os.write("tcp server".getBytes());  }  } catch (IOException e) {  isRunning = false;  e.printStackTrace();  }  }  }  } } 

小结

在开发中用到Socket感觉是很高大上的,在Java中有关Socket相关的类都位于java.net包下,sun.*这个包也包含了很多的网络编程相关的类,但是不建议使用这个包下面的API,因为这个包可能会改变,另外这个包不能保证在所有的平台都有包含。

本文还有一个 示例源代码 没有贴出来,一个类似于QQ群的多人聊天代码,可以下载示例源代码查看,对于服务端的处理跟已经贴出来多线程ServerThread差不多,只是在多线程基础上,加入了一个储存ServerThread的集合列表,列表的目的就是为了在进行群发消息的时候可以方面识别出来自己。同样客户端也采用了多线程处理,读取消息和写入消息分别位于不同的子线程中。

Socket编程重点就在于如何避免多个Socket的读写阻塞,将读和写分别放在不同的子线程中是一种处理方式。在JDK1.4版本中引入了NIO,引入了非阻塞socket,可以不用堵塞进行网络操作。当然了也可以借助于第三方框架如Apache MINA包。

原文  http://www.sunnyang.com/410.html
正文到此结束
Loading...