Socket请求和Http请求区别和场景

我们在开发过程中遇到http请求和socket请求。大部分前后交互都是通过http请求的方式,那socket请求怎么使用,什么情况下使用呢?

基本概念
http请求:基于http协议的soap协议,常见的http数据请求方式有get和post,web服务。
socket请求:实现服务器与客户端之间的物理连接,并进行数据传输。主要有TCP/UDP两个协议。Socket处于网络协议的传输层。
(1)、TCP:传输控制协议,面向连接的的协议,稳定可靠。在客户端和服务器彼此交换数据前,必须先在双方之间建立一个TCP连接,之后才能传输数据。
(2)、UDP:广播式数据传输,UDP不提供可靠性,它只是把应用程序传给IP层的数据报发送出去,但是并不能保证它们能到达目的地。由于UDP在传输数据报前不用在客户和服务器之间建立一个连接,且没有超时重发等机制,故而传输速度很快。
Socket请求和Http请求区别和场景_第1张图片
网络传输架构层面一般分为上图的7层,http协议在TCP/IP分层模型上处于应用层,需要从上向下依次经历所有的层面发送请求,在由下向上依次传输返回请求处理后的数据。socket请求是位于传输层面,所以在向下发送请求和返回数据都要相对http请求更短。应用层面大多都是http请求,大多是用户进行业务操作发起,一般使用B/S架构。传输层面一般非用户层面的一些业务处理比较多,C/S架构相对比较合适。

socket请求的优缺点:
优点:
传输数据为字节级,传输数据可自定义,数据量小。相应的移动端开发,手机费用低;
传输数据时间短,性能高;
适合C/S之间信息实时交互;
可以加密,数据安全性高;
缺点:
需要对传输的数据进行解析,转化为应用级的数据;
对开发人员的开发水平要求高;
相对于Http协议传输,增加了开发量;

http请求的优缺点:
优点:
基于应用级的接口使用方便;
要求的开发水平不高,容错性强;
缺点:
传输速度慢,数据包大。
如实现实时交互,服务器性能压力大;
数据传输安全性差;

注意:
HTTP协议:简单对象访问协议,对应于应用层 ,HTTP协议是基于TCP连接的。
tcp协议: 对应于传输层;
ip协议: 对应于网络层;
TCP/IP是传输层协议,主要解决数据如何在网络中传输;而HTTP是应用层协议,主要解决如何包装数据。
Socket是对TCP/IP协议的封装,Socket本身并不是协议,而是一个调用接口(API),通过Socket,我们才能使用TCP/IP协议。

两者的区别:
http连接:http连接就是所谓的短连接,即客户端向服务器端发送一次请求,服务器端响应后连接即会断掉;
socket连接:socket连接就是所谓的长连接,理论上客户端和服务器端一旦建立起连接将不会主动断掉;但是由于各种环境因素可能会使连接断开,比如说:服务器端或客户端主机down了,网络故障,或者两者之间长时间没有数据传输,网络防火墙可能会断开该连接以释放网络资源。所以当一个socket连接中没有数据的传输,那么为了维持连接需要发送心跳消息~~具体心跳消息格式是开发者自己定义的。

长连接
指在一个连接上可以连续发送多个数据包,在连接保持期间,如果没有数据包发送,需要双方发链路检测包。整个通讯过程,客户端和服务端只用一个Socket对象,长期保持Socket的连接。
短连接
短连接服务是每次请求都建立链接,交互完之后关闭链接
长连接与短连接的优势
长连接多用于操作频繁,点对点的通讯,而且连接数不能太多情况。每个TCP连接都需要三步握手,这需要时间,如果每个操作都是短连接,再操作的话那么处理速度会降低很多,所以每个操作完后都不断开,下次处理时直接发送数据包就OK了,不用建立TCP连接。例如:数据库的连接用长连接,如果用短连接频繁的通信会造成socket错误,而且频繁的socket 创建也是对资源的浪费。

适用场景:
Socket适用场景:网络游戏,银行交互,支付。
http适用场景:公司OA服务,互联网服务。

socket代码示例:
1、简单示例
服务端:

package socket.socket1.socket;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerSocketTest {
public static void main(String[] args) {
try {
// 初始化服务端socket并且绑定9999端口
            ServerSocket serverSocket  =new ServerSocket(9999);
            //等待客户端的连接
            Socket socket = serverSocket.accept();
            //获取输入流
            BufferedReader bufferedReader =new BufferedReader(new InputStreamReader(socket.getInputStream()));
           //读取一行数据
           String str = bufferedReader.readLine();
            //输出打印
            System.out.println(str);
        }catch (IOException e) {
e.printStackTrace();
        }

}

}


客户端:

package socket.socket1.socket;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.Socket;

public class ClientSocket {
public static void main(String[] args) {
try {
Socket socket =new Socket("127.0.0.1",9999);
            BufferedWriter bufferedWriter =new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            String str="你好,这是我的第一个socket";
            bufferedWriter.write(str);
        }catch (IOException e) {
e.printStackTrace();
        }

}

}

启动服务端:可以看到在正常等待被连接
Socket请求和Http请求区别和场景_第2张图片
启动客户端:
Socket请求和Http请求区别和场景_第3张图片
启动客户端的同时发现服务端抛出异常
Socket请求和Http请求区别和场景_第4张图片
上诉服务断抛出异常,说明客户端请求服务端是成功的。问题的原因,首先我们需要了解。socket通信是阻塞的,他会在以下几个地方进行阻塞。第一个是accept方法,调用这个方法后,服务端一直阻塞在那里,直到有客户端连接进来。第二个是read方法,调用read方法也会进行阻塞。上诉的问题就是read方法阻塞。解决方法是客户端发送完成请求后需要bufferedWriter.flush();一下。
改进客户端请求代码:

package socket.socket1.socket;

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.Socket;

public class ClientSocket {
public static void main(String[] args) {
try {
Socket socket =new Socket("127.0.0.1",9999);
            BufferedWriter bufferedWriter =new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            String str="你好,这是我的第一个socket";
            bufferedWriter.write(str);
            //刷新输入流
            bufferedWriter.flush();
            //关闭socket的输出流
            socket.shutdownOutput();
        }catch (IOException e) {

e.printStackTrace();

        }
}
}


此时再看服务端:可以正常接收到数据
Socket请求和Http请求区别和场景_第5张图片

由于socket通信是阻塞式的,假设我现在有A和B两个客户端同时连接到服务端上,当客户端A发送信息给服务端后,那么服务端将一直阻塞在A的客户端上(就是我们上诉说的accept阻塞),通过while循环从A客户端读取信息,此时如果B给服务端发送信息时,将进入阻塞队列,直到A客户端发送完毕,并且退出后,B才可以和服务端进行通信。简单地说,我们现在实现的功能,虽然可以让客户端不间断的和服务端进行通信,与其说是一对一的功能,因为只有当客户端A关闭后,客户端B才可以真正和服务端进行通信,这显然不是我们想要的。

2、多线程时的socket通信
上面我们说到socket的accept方法是阻塞的,我们可以通过多线程来解决
服务端:

package socket.socket1.socket;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;

public class ServerSocketTest {
public static void main(String[] args)throws IOException {
// 初始化服务端socket并且绑定9999端口
            ServerSocket serverSocket  =new ServerSocket(9999);
            while (true){
//等待客户端的连接
                Socket socket = serverSocket.accept();
                //每当有一个客户端连接进来后,就启动一个单独的线程进行处理
                new Thread(new Runnable() {
@Override
                    public void run() {
//获取输入流,并且指定统一的编码格式
                        BufferedReader bufferedReader =null;
                        try {
bufferedReader =new BufferedReader(new InputStreamReader(socket.getInputStream(),"UTF-8"));
                            //读取一行数据
                            String str;
                            //通过while循环不断读取信息,
                            while ((str = bufferedReader.readLine())!=null){
//输出打印
                                System.out.println("客户端说:"+str);
                            }
}catch (IOException e) {
e.printStackTrace();
                        }
}
}).start();
            }
}
}


客户端A和B代码相同如下:

package socket.socket1.socket;

import java.io.*;
import java.net.Socket;

public class ClientSocket {
public static void main(String[] args) {
try {
//初始化一个socket
            Socket socket =new Socket("127.0.0.1",9999);
            //通过socket获取字符流
            BufferedWriter bufferedWriter =new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            //通过标准输入流获取字符流
            BufferedReader bufferedReader =new BufferedReader(new InputStreamReader(System.in,"UTF-8"));
          while (true){
String str = bufferedReader.readLine();
              bufferedWriter.write(str);
              bufferedWriter.write("\n");
              bufferedWriter.flush();
          }
}catch (IOException e) {
e.printStackTrace();
        }
}
}


客户端A启动
Socket请求和Http请求区别和场景_第6张图片
客户端B启动
Socket请求和Http请求区别和场景_第7张图片
服务端可以看到数据
Socket请求和Http请求区别和场景_第8张图片
说明我们可以通过服务端多线程的方式解决accpet阻塞的问题。
服务端代码改良,使用线程池

package socket.socket1.socket;

import java.beans.Encoder;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ServerSocketTest {
public static void main(String[] args)throws IOException {
// 初始化服务端socket并且绑定9999端口
        ServerSocket serverSocket =new ServerSocket(9999);
        //创建一个线程池
        ExecutorService executorService = Executors.newFixedThreadPool(100);
        while (true) {
//等待客户端的连接
            Socket socket = serverSocket.accept();
           Runnable runnable = () -> {
BufferedReader bufferedReader =null;
                try {
bufferedReader =new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
                    //读取一行数据
                    String str;
                    //通过while循环不断读取信息,
                    while ((str = bufferedReader.readLine()) !=null) {
//输出打印
                        System.out.println("客户端说:" + str);
                    }
}catch (IOException e) {
e.printStackTrace();
                }
};
            executorService.submit(runnable);
        }
}
}


你可能感兴趣的:(javaWEB,http,socket)