首先要了解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("客戶端關閉");
}
}