在讲述Socket之前,先简单了解一下相关网络基础知识。
OSI七层模型
从上往下分别是:应用层、表示层、会话层、传输层、网络层、数据链路层、物理层。
OSI是一个理想的模型,一般的网络系统只涉及其中的几层,在七层模型中,每一层都提供一个特殊的网络功能,从网络功能角度分类:
下面4层(物理层、数据链路层、网络层和传输层)主要提供数据传输和交换功能, 即以节点到节点之间的通信为主。
第4层作为上下两部分的桥梁,是整个网络体系结构中最关键的部分。
上3层(会话层、表示层和应用层)则以提供用户与应用程序之间的信息和数据处理功能为主。
TCP&UDP
TCP和UDP协议属于传输层协议。其中TCP提供IP环境下的数据可靠传输,它提供的服务包括数据流传送、可靠性、有效流控、全双工操作和多路复用。通过面向连接、端到端和可靠的数据包发送。
通俗说,它是事先为所发送的数据开辟出连接好的通道,然后再进行数据发送;而UDP则不为IP提供可靠性、流控或差错恢复功能。
一般来说,TCP对应的是可靠性要求高的应用,而UDP对应的则是可靠性要求低、传输经济的应用。
两台计算机之间的通信通过IP地址和端口号标识,同时还得有相同的协议。
端口号的范围为0-65535,其中0-1023未系统的保留端口。
java提供了InetAddress类,可获取计算机IP地址和计算机名:
public static void main(String[] args) throws UnknownHostException { InetAddress address = InetAddress.getLocalHost(); System.out.println("计算机名:" + address.getHostName()); System.out.println("IP地址:" + address.getHostAddress()); byte[] bytes = address.getAddress();// 获取字节数组形式的IP地址 System.out.println("字节数组形式的IP:" + Arrays.toString(bytes)); System.out.println(address);// 直接输出InetAddress对象 /* output: 计算机名:LAPTOP-0VPRTMH4 IP地址:192.168.0.110 字节数组形式的IP:[-64, -88, 0, 110] LAPTOP-0VPRTMH4/192.168.0.110 */ }
URL
URL可以从网络上读取或者向网络写入数据,在这里就不多说了。
下面为URL获取标识信息的一些方法:
public static void main(String[] args) { try { //创建一个URL实例 URL imooc=new URL("https://www.baidu.com:8080"); //?后面表示参数,#后面表示锚点 URL url=new URL(imooc, "/index.html?name=mao#test"); System.out.println("协议:"+url.getProtocol()); System.out.println("主机:"+url.getHost()); //如果未指定端口号,则使用默认的端口号,此时getPort()方法返回值为-1 System.out.println("端口:"+url.getPort()); System.out.println("文件路径:"+url.getPath()); System.out.println("文件名:"+url.getFile()); System.out.println("相对路径:"+url.getRef()); System.out.println("查询字符串:"+url.getQuery()); } catch (MalformedURLException e) { e.printStackTrace(); } /* output: 协议:https 主机:www.baidu.com 端口:-1 文件路径:/index.html 文件名:/index.html?name=mao 相对路径:test 查询字符串:name=mao */ }
Socket
Socket通过建立一个服务器端和客户端来进行通信。
下面为Socket的一个简单示例:
服务器端:
public class Server { public static void main(String[] args) { try { //1.创建一个服务器端Socket,即ServerSocket,指定绑定的端口,并监听此端口 ServerSocket serverSocket=new ServerSocket(8888); Socket socket=null; //记录客户端的数量 int count=0; System.out.println("***服务器即将启动,等待客户端的连接***"); //循环监听等待客户端的连接 while(true){ //调用accept()方法开始监听,等待客户端的连接 socket=serverSocket.accept(); //创建一个新的线程 ServerThread serverThread=new ServerThread(socket); //启动线程 serverThread.start(); count++;//统计客户端的数量 System.out.println("客户端的数量:"+count); InetAddress address=socket.getInetAddress(); System.out.println("当前客户端的IP:"+address.getHostAddress()); } } catch (IOException e) { e.printStackTrace(); } } } class ServerThread extends Thread { // 和本线程相关的Socket Socket socket = null; public ServerThread(Socket socket) { this.socket = socket; } //线程执行的操作,响应客户端的请求 public void run(){ InputStream is=null; InputStreamReader isr=null; BufferedReader br=null; OutputStream os=null; PrintWriter pw=null; try { //获取输入流,并读取客户端信息 is = socket.getInputStream(); isr = new InputStreamReader(is); br = new BufferedReader(isr); String info=null; while((info=br.readLine())!=null){//循环读取客户端的信息 System.out.println("我是服务器,客户端说:"+info); } socket.shutdownInput();//关闭输入流 //获取输出流,响应客户端的请求 os = socket.getOutputStream(); pw = new PrintWriter(os); pw.write("欢迎您!"); pw.flush();//调用flush()方法将缓冲输出 } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); }finally{ //关闭资源 try { if(pw!=null) pw.close(); if(os!=null) os.close(); if(br!=null) br.close(); if(isr!=null) isr.close(); if(is!=null) is.close(); if(socket!=null) socket.close(); } catch (IOException e) { e.printStackTrace(); } } } }
客户端:
/* * 客户端 */ public class Client { public static void main(String[] args) { try { //1.创建客户端Socket,指定服务器地址和端口 Socket socket=new Socket("localhost", 8888); //2.获取输出流,向服务器端发送信息 OutputStream os=socket.getOutputStream();//字节输出流 PrintWriter pw=new PrintWriter(os);//将输出流包装为打印流 pw.write("用户名:alice;密码:789"); pw.flush(); socket.shutdownOutput();//关闭输出流 //3.获取输入流,并读取服务器端的响应信息 InputStream is=socket.getInputStream(); BufferedReader br=new BufferedReader(new InputStreamReader(is)); String info=null; while((info=br.readLine())!=null){ System.out.println("我是客户端,服务器说:"+info); } //4.关闭资源 br.close(); is.close(); pw.close(); os.close(); socket.close(); } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }
Mina
当我们要实现的功能比较复杂时,自己编写代码会非常困难,所以选择一个框架非常有必要,Mina是一个不错的选择。
下面是一个Mina样例,贴代码有些不方便,还是直接给个Demo链接吧——MinaDemo。
androidPn
这是较为常用的开源项目,在这个项目的基础上,我们可以较为容易地搭建自己的消息推送平台。