但使龙城飞将在,只缘身在此山中

之前在 BIO、NIO 入门(Netty 先导) 一文中聊了 socket ,本文想把视野拉大,从计算机网络的纬度来聊聊 socket

温故而知新,聊聊网络模型

上图对比给出了 ISO(International Standardization Organization 国际标准化组织)的 OSI (Open System Interconnect Reference Model 开放式系统互联参考模型)七层网络参考模型,这个模型是标准,是参考,而现在使用最广的实现是 TCP/IP 的四层模型。

Tips:这里提一下,网上有说 TCP/IP 四层模型的,也有说五层的,区别在于 四层是把 数据链路层 和 物理层 合并为一个 网络接口层,这两种说法都不为错。

聊点真实的

感觉上图说的还是有点虚,懂了但是好像没完全懂。那这里我们聊点真实的,说说我们真正在用的 TCP/IP 网络模型,以 五层分层为例,这样更清楚一些。

总的来说,我们日常编程玩的大部分都在 应用层 和 传输层 这两块。OK 这里提到 Socket 编程,那么 Socket 在哪?用来干嘛?继续来点真实的

说说 Socket

所谓 Socket 编程,其实就是在串联 应用层传输层以达到我们的需求目的。主要就是在操作 传输层的协议,而我们主流的传输层协议就是 TCP 和 UDP 所以重点关注这两个协议即可。

不得不说的 TCP 和 UDP

时常被问到的一个问题:TCP 和 UDP 什么区别? 通常答案是这样的

TCP 是面向连接的;UDP 面向无连接。

TCP 是可靠的,无差错、不丢失、保证顺序;UDP 不可靠,不保证

TCP 面向字节流,发送时是一个流;UDP 面向数据报 ,一个一个发送

TCP 可以提供 流量控制,拥塞控制;UDP 没有

以上大概是常用的回答了,但是对于自己理解来说,似乎总有些说不清道不明的迷糊在里面。比如

连接到底是什么?真的是在网线中维系了这样一种抽象的链路吗?

不是的,”连接“ 意思上感觉是通路,其实其只存在于两端,即需要通信的两端。

建立连接其实就是在通信两端建立一种数据结构,然后维持这种数据结构,以保证通信双方可以互相识别对方发送过来的信息,并用这样的数据结构保证面向连接的特性。

来点真实的:连接在两端,而非通路。可以理解为两端数据结构的协调,这个数据结构类比 Java 可以理解为 Class

两边数据结构状态一致,符合 TCP 的规则,那就认为连接存在,如果对不上就是连接断了。

而所谓的可靠、顺序、流传输,这些也都是通过两端的数据结构来保证。可靠是数据结构在点名,顺序是数据结构进行了排序、流传输是数据结构对单个包进行了归并统一发送。

生活的例子:

我觉得很形象的就是我们买东西时 收发快递 ,你与商家就是两端,网络层就相当于你们彼此知道地址,而发送包裹交给物流,具体发生什么事你们根本不知道,你们只关心结果,而 ”数据结构“ 就是商家和买家各自对于包裹异常所作出的行为补正,用于保证快递正确到达且完好无损。

回到 Socket 编程

经过上面的铺垫,可以知道,Socket 编程其实就是端对端的编程,其控制的是端上的逻辑,有时候我们又会听到一起其他的相关名词。如: Socket 文件、Socket 句柄。 这个其实是在我们最常用的 Linux 服务器上,Socket 就是以一种文件的形式存在的,所以收到内存的大小的限制,Socket 连接数同样也是有限制的,不然迟早要冲垮你的服务器。

下图是 Socket 针对 TCP 协议 Java 版的伪代码。其他语言逻辑类似。

这里 服务端 绑定好端口,调用 accept 方法,等待一个连接请求,完成 TCP 的三次握手,成功后会返回这个链接的 Socket 对象,那么如果需要处理多个连接就需要多次调用 accept 方法,所以经常可以看到 accept 方法是套在循环里的。

可以发现客户端在创建 Socket 连接的时候似乎不需要绑定端口,这是因为系统会为其随机分配一个端口进行连接,因为客户端只有我们自己在用,所以并不关心客户端端口是多少。

用 Socket 实现 RPC 远程服务调用

ok ,基于理论,这里我用 Java 简单实现了一个 RPC 接口,给大家提供一个实际的参考。

服务端统一接口

public interface RpcServer {void stop();void start() throws IOException;void register(Class<?> serviceInterface, Class<?> impl);boolean isRunning();int getPort();}

服务端默认实现

public class DefaultServer implements RpcServer {private static ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());private static final HashMap<String, Class> serviceRegistry = new HashMap<String, Class>();private static boolean isRunning = false;private int port;public DefaultServer(int port) {this.port = port;}@Overridepublic void stop() {isRunning = false;executor.shutdown();}@Overridepublic void start() throws IOException {System.out.println("start server");ServerSocket serverSocket = new ServerSocket();serverSocket.bind(new InetSocketAddress(port));System.out.println("====已注册服务====");for (Map.Entry<String, Class> entry : serviceRegistry.entrySet()) {System.out.println("注册服务名:"+entry.getKey()+" 实现:"+entry.getValue());}System.out.println("==========");try {while (true) {executor.execute(new ServerTask(serverSocket.accept()));}} finally {serverSocket.close();}}@Overridepublic void register(Class<?> serviceInterface, Class<?> impl) {serviceRegistry.put(serviceInterface.getName(), impl);}@Overridepublic boolean isRunning() {return isRunning;}@Overridepublic int getPort() {return port;}private class ServerTask implements Runnable {private Socket socket;public ServerTask(Socket socket) {this.socket = socket;}@Overridepublic void run() {ObjectInputStream inputStream = null;ObjectOutputStream outputStream = null;try {inputStream = new ObjectInputStream(socket.getInputStream());String serviceName = inputStream.readUTF();String methodName = inputStream.readUTF();Class<?>[] parameterTypes = (Class<?>[]) inputStream.readObject();Object[] arguments = (Object[]) inputStream.readObject();Class<?> serviceClass = serviceRegistry.get(serviceName);if (serviceClass == null) {throw new ClassNotFoundException(serviceName + " not found");}Method method = serviceClass.getMethod(methodName, parameterTypes);Object result = method.invoke(serviceClass.newInstance(), arguments);outputStream = new ObjectOutputStream(socket.getOutputStream());outputStream.writeObject(result);} catch (Exception e) {e.printStackTrace();} finally {if (outputStream != null) {try {outputStream.close();} catch (IOException e) {e.printStackTrace();}}if (inputStream != null) {try {inputStream.close();} catch (IOException e) {e.printStackTrace();}}if (socket != null) {try {socket.close();} catch (IOException e) {e.printStackTrace();}}}}}
}

客户端实现

public class RPCClient<T> {public static  <T> T getRemoteProxyObj(final Class<?> serviceInterface, final InetSocketAddress addr) {return (T) Proxy.newProxyInstance(serviceInterface.getClassLoader(), new Class[]{serviceInterface},new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Socket socket = null;ObjectOutputStream output = null;ObjectInputStream input = null;try {socket = new Socket();socket.connect(addr);output = new ObjectOutputStream(socket.getOutputStream());output.writeUTF(serviceInterface.getName());output.writeUTF(method.getName());output.writeObject(method.getParameterTypes());output.writeObject(args);// 4.同步阻塞等待服务器返回应答,获取应答后返回input = new ObjectInputStream(socket.getInputStream());return input.readObject();} catch (Exception e) {e.printStackTrace();} finally {if (socket != null) socket.close();if (output != null) output.close();if (input != null) input.close();}return null;}});}}

demo 类

public interface Demo {String getName(String name);
}
public class DemoAImpl implements Demo{@Overridepublic String getName(String name) {return "AAAAA "+name;}
}

提供服务

public class ProviderTest {public static void main(String[] args) throws IOException {RpcServer rpcServer = new DefaultServer(8088);rpcServer.register(Demo.class, DemoAImpl.class);rpcServer.start();}
}

客户端调用

public class RpcTest {public static void main(String[] args) {Demo demo = RPCClient.getRemoteProxyObj(Demo.class, new InetSocketAddress("localhost", 8088));System.out.println(demo.getName("nice"));}
}

说明一下

这种编程方式其实是非常原始的,有极大的改进空间,比如用 NIO 或者 Netty 都可以进行优化 BIO、NIO 入门(Netty 先导),还有数据传输的方式,篇幅原因,这里埋个坑,我们下期继续聊。

千回百转,这里是 dying 搁浅。

网络编程之 Socket 编程 一文看懂相关推荐

  1. 网络编程之 socket编程

    socket编程(基于linux下的网络编程) 提起网络编程那么我们就不得不说一下socket编程了(本博客主要是围绕下面这本书展开的). 感谢bingo大佬提供的书籍 链接: https://pan ...

  2. java网络编程之Socket编程

    概念 网络编程分为BIO(传统IO).NIO.AIO.Socket编程属于BIO这种传统IO. InetAddress java.net.InetAddress是JAVA中管理IP地址的类,常用 pu ...

  3. Python网络编程之socket编程

    什么是Socket? Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口.在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面 ...

  4. linux网络编程之socket编程(六)

    经过一个国庆长假,又有一段时间没有写博文了,今天继续对linux网络编程进行学习,如今的北京又全面进入雾霾天气了,让我突然想到了一句名句:"真爱生活,珍惜生命",好了,言归正传. ...

  5. linux网络编程之Socket编程

    (1)socket套接字 1)在linux环境下,socket用于表示进程间网络通信的特殊文件类型,其本质是内核借助缓冲区形成的伪文件(不占磁盘空间,除此之外还有二进制文件,管道,字符文件). 2)伪 ...

  6. 网络编程+go+java_GO语言的进阶之路-网络编程之socket

    GO语言的进阶之路-网络编程之socket 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.什么是socket; 在说socket之前,我们要对两个概念要有所了解,就是IP和端口 ...

  7. 网络编程之socket

    网络编程之socket 看到本篇文章的题目是不是很疑惑,what is this?,不要着急,但是记住一说网络编程,你就想socket,socket是实现网络编程的工具,那么什么是socket,什么是 ...

  8. GO语言的进阶之路-网络编程之socket

    GO语言的进阶之路-网络编程之socket 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.什么是socket; 在说socket之前,我们要对两个概念要有所了解,就是IP和端口 ...

  9. 浅谈Java网络编程之Socket (2)

    <浅谈Java网络编程之Socket (1)>中我们已经和大家说到客户端的网络编程,下面和大家分享的是服务器的实现代码. import java.net.*; import java.io ...

最新文章

  1. oracle 存储过程的基本语法
  2. tensorflow padd
  3. 数组的partition调整
  4. 逆向工程核心原理学习笔记(十四):栈帧1
  5. Windows下VC++显示UTF-8编码中文
  6. 微信小程序云开发教程-云函数入门(2)-接收参数、前端调用
  7. 巴克码信号处理的计算机仿真,单码道绝对编码信号处理建模与仿真
  8. 详解C3P0(数据库连接池)
  9. 突然发现浏览器广告拦截插件原理
  10. 高通9008工具 qpst 安装时报错 qpst server returned unexpected error attempting 解决办法
  11. 《失业的程序员》(十二):潜意识的智商 .
  12. 深度xp系统安装教程
  13. 《人类简史》《未来简史》读后感作文5000字
  14. 【论文笔记】基于深度学习的视觉检测及抓取方法
  15. Opengl+VS2019安装+(简单例子)+Opengl教程
  16. 5.5 listen() --- 如果有“人”,请叫我?
  17. TikTok涨粉?参考抖音?账号增粉解析!
  18. spring实战学习(六)事务管理
  19. KNN实现小麦种子分类问题
  20. 2020年全国水资源总量、总供水量、总用水量及人均综合用水量分析[图]

热门文章

  1. 文字填充图形效果教程
  2. 跑步,读书,成为更好的自己,结识志同道合的小伙伴
  3. 初学者如何学习FPGA?一文为你讲解清楚
  4. 如何使用EasyExcel
  5. Linux目录结构详细介绍
  6. homepod怎么设置为中文_苹果HomePod增加支持更多新功能与Siri语言!
  7. Mac 下IDEA 在同一窗口下打开多个项目
  8. Idea 一键启动多个项目
  9. vue学习记录-05 事件监听
  10. uniapp 自定义启动图