restful soap rpc socket websocket rmi jms遠程通訊方式(待續)

首先要了解OSI的七層協議

第一層:物理層
第二層:數據鏈路層 802.2、802.3ATM、HDLC、FRAME RELAY 
第三層:網絡層 IP、IPX、ARP、APPLETALK、ICMP 
第四層:傳輸層 TCP、UDP、SPX 
第五層:會話層 RPC、SQL、NFS 、X WINDOWS、ASP
第六層:表示層 ASCLL、PICT、TIFF、JPEG、 MIDI、MPEG 
第七層:應用層 HTTP,FTP,SNMP,WEBSOCKET等

TCP/IP HTTP的一個形象的比喻:ip相當於通往終點的一條公路,tcp是這條公路上行駛的貨車,http是這趟火車上裝載的貨物


1.Restful

  • Restful一種架構設計風格,提供了設計原則和約束條件,而不是架構。而滿足這些約束條件和原則的應用程序或設計就是 RESTful架構或服務。
  • Restful可以看着是http協議的一種直接應用,默認基於json作爲傳輸格式,使用簡單,學習成本低效率高,但是安全性較低。
  • Restful標榜的面向資源,將網絡上各種調用當做資源,並且資源是用統一資源定位符URL來標識的,在你使用時,你只需要發送一個rest請求即可獲得這個資源爲你所用。

應用場景:適合輕量是數據傳輸,適合開放的對外平臺,springmvc,springboot的模式就是基於restful 的風格。

restful的簡單實現:

實體類

package demo;
 
import javax.xml.bind.annotation.XmlRootElement;
 
@XmlRootElement(name = "Person")
public class Person {
 
    private String name;
    private String id;
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public String getId() {
        return id;
    }
 
    public void setId(String id) {
        this.id = id;
    }
 
}

webservice接口

package demo;
 
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
 
@Path("/PersonService")
//@Produces註釋用來指定將要返回給client端的數據標識類型(MIME)。
//@Produces可以作爲class註釋,也可以作爲方法註釋,方法的@Produces註釋將會覆蓋class的註釋。
//覆蓋的意思是假如方法聲明瞭自己的Produce,那麼以方法的爲準,class的僅供參考
@Produces({ "application/json", "application/xml" })
public class PersonService {
 
    @GET
    @Path("/getPerson/{dd}")// http://localhost:8080/PersonService/getPerson/101 方式的調用  
    @Produces(MediaType.APPLICATION_XML)
    public Person getPerson(@PathParam("dd") String id) {
        if (id != null && id.length() > 0) {
            for (Person pp : Config.persons) {
                if(id.equals(pp.getId())){
                    return pp;
                }
            }
            Person result = new Person();
            result.setId(id);
            return result;
        } else {
            return new Person();
        }
    }
 
    @POST
    @Path("/regPerson")// http://localhost:8080/PersonService/regPerson 方式的調用 
    //@Consumes與@Produces相反,用來指定可以接受client發送過來的MIME類型,同樣可以用於class或者method,也可以指定多個MIME類型,一般用於@PUT,@POST。
    @Consumes({ "application/json", "application/xml" })
    public Response regPerson(Person person) {
        if (Config.persons.contains(person)) {
            return Response.status(Status.BAD_REQUEST).build();
        } else {
            Config.persons.add(person);
            return Response.ok(person).build();
        }
    }
 
    @DELETE
    @Path("/delPerson")// http://localhost:8080/PersonService/delPerson?id=1 方式的調用
    @Consumes({ "application/json", "application/xml" })
    public Response delPerson(@QueryParam("id") String id) {
        Person person = new Person();
        person.setId(id);
        if (Config.persons.contains(person)) {
            return Response.status(Status.BAD_REQUEST).build();
        } else {
            Config.persons.remove(person);
            return Response.ok(person).build();
        }
    }
 
    @PUT
    @Path("/updatePerson")// http://localhost:8080/PersonService/updatePerson 方式的調用
    @Consumes({ "application/json", "application/xml" })
    public Response updatePerson(Person person) {
        if (Config.persons.contains(person)) {
            return Response.status(Status.BAD_REQUEST).build();
        } else {
            for (Person pp : Config.persons) {
                if (pp.equals(person)) {
                    pp.setName(person.getName());
                }
            }
            return Response.ok(person).build();
        }
    }
 
}

cxf的xml配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jaxrs="http://cxf.apache.org/jaxrs"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
                            http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
                            http://cxf.apache.org/jaxrs
                            http://cxf.apache.org/schemas/jaxrs.xsd">
 
    <import resource="classpath:META-INF/cxf/cxf.xml" />
    <import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
 
    <bean id="personService" class="demo.PersonService" />
    <!-- 發佈webservice -->
    <jaxrs:server id="rs_server" address="/rs">
        <jaxrs:serviceBeans>
            <ref bean="personService"/>
        </jaxrs:serviceBeans>
    </jaxrs:server>
</beans>

2.soap

  • soap是封裝的http協議,比較重
  • soap基於xml的傳輸,生成wsdl描述文檔,可以跨語言,跨平臺的交互
  • soap在安全方面是通過使用XML-Security和XML-Signature兩個規範組成了WS-Security來實現安全控制的

應用場景:傳統行業,內部系統交互,安全性要求較高的系統

soap的簡單實現

服務端接口

package test;

import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebService;

@WebService
public interface SoapService {
 
	@WebMethod(operationName = "sayHi")
    String sayHi(@WebParam(name = "text") String text);
}

服務端實現類

package test;

import javax.jws.WebParam;
import javax.jws.WebService;

@WebService
public class SoapServiceImpl implements SoapService{
	@Override
    public String sayHi(@WebParam(name = "text") String text) {
        return "hi," + text;
    }
}

服務發佈

package test;

import javax.xml.ws.Endpoint;

public class WebPublish {

	
	public static void main(String[] args) {
		Endpoint.publish("http://localhost:8088/soap/SayHi", new SoapServiceImpl());
	}
}

使用URL地址http://localhost:8088/soap/SayHi?wsdl輸入地址欄,說明發布成功

<definitions xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:wsp="http://www.w3.org/ns/ws-policy" xmlns:wsp1_2="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://test/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.xmlsoap.org/wsdl/" targetNamespace="http://test/" name="SoapServiceImplService">
<types>
<xsd:schema>
<xsd:import namespace="http://test/" schemaLocation="http://localhost:8088/soap/SayHi?xsd=1"/>
</xsd:schema>
</types>
<message name="sayHi">
<part name="parameters" element="tns:sayHi"/>
</message>
<message name="sayHiResponse">
<part name="parameters" element="tns:sayHiResponse"/>
</message>
<portType name="SoapServiceImpl">
<operation name="sayHi">
<input wsam:Action="http://test/SoapServiceImpl/sayHiRequest" message="tns:sayHi"/>
<output wsam:Action="http://test/SoapServiceImpl/sayHiResponse" message="tns:sayHiResponse"/>
</operation>
</portType>
<binding name="SoapServiceImplPortBinding" type="tns:SoapServiceImpl">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>
<operation name="sayHi">
<soap:operation soapAction=""/>
<input>
<soap:body use="literal"/>
</input>
<output>
<soap:body use="literal"/>
</output>
</operation>
</binding>
<service name="SoapServiceImplService">
<port name="SoapServiceImplPort" binding="tns:SoapServiceImplPortBinding">
<soap:address location="http://localhost:8088/soap/SayHi"/>
</port>
</service>
</definitions>

3.RPC

  • rpc是遠程過程調用,概念上與webservice的調用沒有什麼區別。簡單的說,RPC就是從一臺機器(客戶端)上通過參數傳遞的方式調用另一臺機器(服務器)上的一個函數或方法(可以統稱爲服務)並得到返回的結果
  • rpc可以使用tcp/udp協議,也可以使用http協議。並且會隱藏底層的通訊細節。阿里的hsf,dubbo使用的tcp協議。
  • rpc在使用形式上像調用本地函數(或方法)一樣去調用遠程的函數(或方法)。最重要的!
  • rpc的調用分同步和異步兩重方式。同步調用:客戶方等待調用執行完成並返回結果;異步調用:客戶方調用後不用等待執行結果返回,但依然可以通過回調通知,回調函數等方式獲取返回結果。
  • 在rpc中,當一個請求到達rpc服務器時,這個請求就包含了一個參數集和一個文本值,方法等,通常形成“classname.methodname”的形式。
  • rpc通常會使用框架治理,rpc 的主要功能目標是讓構建分佈式計算(應用)更容易
  • 通常所說的rpc的效率高於http,實際是rpc使用了tcp協議,http的header比較重,導致傳遞時解析工作量大且重複工作多,效率不及tcp
應用場景:內部系統,需要高效率,高性能的數據傳輸

rpc的簡單實現(原作者爲樑飛,dubbo的核心成員

服務的導出和引用

package com.alibaba.study.rpc.framework;
 
 import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.Method;
 import java.lang.reflect.Proxy;
 import java.net.ServerSocket;
 import java.net.Socket;
 
 /**
  * RpcFramework
  * 
  * @author william.liangf
  */
 public class RpcFramework {
 
     /**
      * 暴露服務
      * 
      * @param service 服務實現
      * @param port 服務端口
      * @throws Exception
      */
     public static void export(final Object service, int port) throws Exception {
         if (service == null)
             throw new IllegalArgumentException("service instance == null");
         if (port <= 0 || port > 65535)
             throw new IllegalArgumentException("Invalid port " + port);
         System.out.println("Export service " + service.getClass().getName() + " on port " + port);
         ServerSocket server = new ServerSocket(port);
         for(;;) {
             try {
                 final Socket socket = server.accept();
                 new Thread(new Runnable() {
                     @Override
                     public void run() {
                         try {
                           try {
                                 ObjectInputStream input = new ObjectInputStream(socket.getInputStream());
                                 try {
                                     String methodName = input.readUTF();
                                     Class<?>[] parameterTypes = (Class<?>[])input.readObject();
                                     Object[] arguments = (Object[])input.readObject();
                                     ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());
                                     try {
                                         Method method = service.getClass().getMethod(methodName, parameterTypes);
                                         Object result = method.invoke(service, arguments);
                                         output.writeObject(result);
                                     } catch (Throwable t) {
                                         output.writeObject(t);
                                     } finally {
                                         output.close();
                                     }
                                 } finally {
                                     input.close();
                                 }
                             } finally {
                                 socket.close();
                             }
                         } catch (Exception e) {
                             e.printStackTrace();
                         }
                     }
                 }).start();
             } catch (Exception e) {
                 e.printStackTrace();
             }
         }
     }
 
     /**
      * 引用服務
      * 
      * @param <T> 接口泛型
      * @param interfaceClass 接口類型
      * @param host 服務器主機名
      * @param port 服務器端口
      * @return 遠程服務
      * @throws Exception
      */
     @SuppressWarnings("unchecked")
     public static <T> T refer(final Class<T> interfaceClass, final String host, final int port) throws Exception {
         if (interfaceClass == null)
             throw new IllegalArgumentException("Interface class == null");
        if (! interfaceClass.isInterface())
             throw new IllegalArgumentException("The " + interfaceClass.getName() + " must be interface class!");
         if (host == null || host.length() == 0)
             throw new IllegalArgumentException("Host == null!");
         if (port <= 0 || port > 65535)
             throw new IllegalArgumentException("Invalid port " + port);
         System.out.println("Get remote service " + interfaceClass.getName() + " from server " + host + ":" + port);
         return (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class<?>[] {interfaceClass}, new InvocationHandler() {
             public Object invoke(Object proxy, Method method, Object[] arguments) throws Throwable {
                 Socket socket = new Socket(host, port);
                 try {
                     ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());
                     try {
                         output.writeUTF(method.getName());
                         output.writeObject(method.getParameterTypes());
                         output.writeObject(arguments);
                         ObjectInputStream input = new ObjectInputStream(socket.getInputStream());
                         try {
                             Object result = input.readObject();
                             if (result instanceof Throwable) {
                                 throw (Throwable) result;
                             }
                             return result;
                         } finally {
                             input.close();
                         }
                     } finally {
                         output.close();
                     }
                 } finally {
                     socket.close();
                 }
             }
         });
     }
 
 }

定義服務接口

package com.alibaba.study.rpc.test;  
   
 /** 
  * HelloService 
  *  
  * @author william.liangf 
  */  
 public interface HelloService {  
  
     String hello(String name);  
   
 }

實現服務

package com.alibaba.study.rpc.test;  
   
 /** 
  * HelloServiceImpl 
  *  
  * @author william.liangf 
  */  
 public class HelloServiceImpl implements HelloService {  
   
     public String hello(String name) {  
         return "Hello " + name;  
     }  
   
 }

服務提供者

package com.alibaba.study.rpc.test;
 
 import com.alibaba.study.rpc.framework.RpcFramework;
 
 /**
  * RpcProvider
  * 
  * @author william.liangf
  */
 public class RpcProvider {
 
     public static void main(String[] args) throws Exception {
         HelloService service = new HelloServiceImpl();
         RpcFramework.export(service, 1234);
     }
 
 }

服務消費者

package com.alibaba.study.rpc.test;
 
 import com.alibaba.study.rpc.framework.RpcFramework;
 
 /**
  * RpcConsumer
  * 
  * @author william.liangf
  */
 public class RpcConsumer {
     
     public static void main(String[] args) throws Exception {
         HelloService service = RpcFramework.refer(HelloService.class, "127.0.0.1", 1234);
         for (int i = 0; i < Integer.MAX_VALUE; i ++) {
             String hello = service.hello("World" + i);
             System.out.println(hello);
             Thread.sleep(1000);
         }
     }
     
 }

4.Socket

  • Socket通常也稱作"套接字",用於描述IP地址和端口,是一個通信鏈的句柄。網絡上的兩個程序通過一個雙向的通訊連接實現數據的交換,這個雙向鏈路的一端稱爲一個Socket,一個Socket由一個IP地址和一個端口號唯一確定
  • Socket通訊過程:服務端監聽某個端口是否有連接請求,客戶端向服務端發送連接請求,服務端收到連接請求向客戶端發出接收消息,這樣一個連接就建立起來了。客戶端和服務端都可以相互發送消息與對方進行通訊
  • 在Java環境下,Socket編程主要是指基於TCP/IP協議的網絡編程。但Socket所支持的協議種類也不光TCP/IP一種,因此兩者之間是沒有必然聯繫的
  • Socket是應用層與TCP/IP協議族通信的中間軟件抽象層,它是一組接口,因此Socket不是協議,是封裝了tcp/ip的一組api接口

應用場景:即時通訊MSN,QQ,實時交互遊戲等,MSN使用的tcp協議,QQ使用的是UDP協議,因此QQ的響應速度,傳輸速度要快於MSN

Socket簡單實現

服務端

import java.io.BufferedReader;  
import java.io.IOException;  
import java.io.InputStreamReader;  
import java.io.PrintWriter;  
import java.net.ServerSocket;  
import java.net.Socket;  
  
public class SocketServerDemo {  
    private ServerSocket serverSocket;  
    public SocketServerDemo(){  
        try{  
            //設置你服務器監聽的端口爲10000,用戶能使用的端口爲1025-65535  
            serverSocket = new ServerSocket(10000);  
        }catch(IOException e){  
            //捕獲異常,不懂的話好好看java,  
            e.printStackTrace();  
        }  
        //創建新的監聽主線程,這個線程創建ServerSocket監聽  
        new Thread(new Runnable(){  
            public void run(){  
                while(true){  
                    Socket socket = null;  
                    try{  
                        socket = serverSocket.accept();  
                        //當監聽到了客戶端連接後,創建新線程傳輸數據,這樣可以實現多個客戶端同時訪問  
                        new Thread(new SocketHandler(socket)).start();  
                    }catch(Exception e){  
                        e.printStackTrace();  
                    }  
                }  
            }  
        }).start();  
    }  
      
    class SocketHandler implements Runnable{  
        private Socket socket;  
        private BufferedReader reader;  
        private PrintWriter writer;  
        SocketHandler(Socket socket){  
            try{  
                this.socket = socket;  
                reader = new BufferedReader(new InputStreamReader(this.socket.getInputStream(),"GB2312"));  
                writer = new PrintWriter(socket.getOutputStream(), true);  
                writer.println("-------welcome---------");  
                writer.println("-------welcome---------");  
                writer.println("-------welcome---------");  
            }catch(IOException e){  
                e.printStackTrace();  
            }  
              
        }  
        //這裏是覆蓋實現接口Runnable裏的run()  
        public void run(){  
            try{  
                //讀取數據,這裏只能讀取一行String  
                String line = reader.readLine();  
                System.out.println(line);  
            }catch(IOException e){e.printStackTrace();}finally{  
                //最後要關閉Socket  
                try{  
                    if(socket!=null)socket.close();  
                    if(reader!=null)reader.close();  
                    if(writer!=null)writer.close();  
                }catch(IOException e){  
                    e.printStackTrace();  
                }  
            }  
        }  
    }  
      
    public static void main(String[] args){  
        new SocketServerDemo();  
    }  
}  

客戶端

import java.io.BufferedReader;  
import java.io.IOException;  
import java.io.InputStreamReader;  
import java.io.PrintWriter;  
import java.net.Socket;  
  
public class SocketClientDemo implements Runnable{  
    private Socket socket;  
    BufferedReader reader;  
    private PrintWriter writer;  
    public SocketClientDemo(){  
        try{  
            //127.0.0.1表示本機IP,10000爲服務器Socket設置的端口  
            socket = new Socket("127.0.0.1", 10000);  
            reader = new BufferedReader(new InputStreamReader(socket.getInputStream(), "GB2312"));  
            writer = new PrintWriter(socket.getOutputStream(), true);  
            writer.println("working.............");  
        }catch(IOException e){  
            e.printStackTrace();  
        }  
    }  
    public void run(){  
        try{  
            //這裏就可以讀取所有行String  
            String line, buffer="";  
            while(!((line = reader.readLine())==null))  
                buffer+=line;  
                System.out.println(buffer);  
        }catch(IOException e){    
            e.printStackTrace();  
            System.out.println("problem");  
        }finally{  
            //最後關閉Socket  
            try{  
                if(socket!=null)socket.close();  
                if(reader!=null)reader.close();  
                if(writer!=null)writer.close();  
            }catch(IOException e){  
                e.printStackTrace();  
            }  
              
        }  
          
    }  
    public static void main(String[] args){  
        new Thread(new SocketClientDemo()).start();  
          
          
    }  
  
}  

5.WebSocket

  • WebSocket協議是基於TCP的一種新的網絡協議。它實現了瀏覽器與服務器全雙工(full-duplex)通信——允許服務器主動發送信息給客戶端。
  • Websocket是一個新協議,跟HTTP協議基本沒有關係,有交集,但是並不是全部
  • Websocket是應用層第七層上的一個應用層協議,它必須依賴 HTTP 協議進行一次握手 ,握手成功後,數據就直接從 TCP 通道傳輸,與 HTTP 無關了
  • WebSocket發起的請求頭
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Origin: http://example.com
  • 服務器返回
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat

應用場景:社交聊天、社交訂閱,彈幕、多玩家遊戲、協同編輯、股票基金實時報價、體育實況更新、視頻會議/聊天

websocket簡單實現

引用:https://blog.csdn.net/guoquanyou/article/details/51683165

前端網頁

<!DOCTYPE html>  
<html>  
<head>  
<title>HTML5 WebSocket測試</title>  
</head>  
<body>  
    <div>  
        <input type="button" value="Start" onclick="start()" />  
    </div>  
    <div id="messages"></div>  
    <script type="text/javascript">  
        var webSocket = new WebSocket('ws://localhost:8080/WebSocket/websocket');  
        webSocket.onerror = function(event) {  
            alert(event.data);  
        };  
        //與WebSocket建立連接  
        webSocket.onopen = function(event) {  
            document.getElementById('messages').innerHTML = '與服務器端建立連接';  
        };  
        //處理服務器返回的信息  
        webSocket.onmessage = function(event) {  
            document.getElementById('messages').innerHTML += '<br />'+ event.data;  
        };  
        function start() {  
            //向服務器發送請求  
            webSocket.send('我是jCuckoo');  
        }  
    </script>  
</body>  
</html>
後端實現
package com.jCuckoo.server;  
  
import java.io.IOException;  
  
import javax.websocket.OnClose;  
import javax.websocket.OnMessage;  
import javax.websocket.OnOpen;  
import javax.websocket.Session;  
import javax.websocket.server.ServerEndpoint;  
/** 
 * 在tomcat7中存在WebSocketServlet類(但已經過時),在tomcat8中徹底刪除 
 * 此處使用@ServerEndpoint註解,主要是將目前的類定義成一個websocket服務器端 
 * 註解的值將被用於監聽用戶連接的終端訪問URL地址 
 */  
@ServerEndpoint("/websocket")  
public class WebSocketTest {  
    /** 
     * 當服務器接收到客戶端發送的消息時所調用的方法 
     * 該方法可能包含一個javax.websocket.Session可選參數 
     * 如果有這個參數,容器將會把當前發送消息客戶端的連接Session注入進去 
     */  
    @OnMessage  
    public void onMessage(String message,Session session) throws IOException, InterruptedException {  
        // 打印從客戶端獲取到的信息  
        System.out.println("從客戶端接收到的信息: " + message);  
        //向客戶端第一次發送信息  
        session.getBasicRemote().sendText("=======向客戶端第一次發送信息=======");  
        //每秒向客戶端發送一次信息,連續發送3次  
        int sentMessages = 0;  
        while (sentMessages < 3) {  
            Thread.sleep(1000);  
            session.getBasicRemote().sendText("即時發送信息,當前是第 " + sentMessages+"次...");  
            sentMessages++;  
        }  
        // 向客戶端發送最後一次信息  
        session.getBasicRemote().sendText("=======向客戶端發送最後一次信息=======");  
    }  
    /** 
     * 當一個新用戶連接時所調用的方法 
     * 該方法可能包含一個javax.websocket.Session可選參數 
     * 如果有這個參數,容器將會把當前發送消息客戶端的連接Session注入進去 
     */  
    @OnOpen  
    public void onOpen(Session session) {  
        System.out.println("客戶端連接成功");  
    }  
    /** 當一個用戶斷開連接時所調用的方法*/  
    @OnClose  
    public void onClose() {  
        System.out.println("客戶端關閉");  
    }  
}  


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章