欢迎你们搜索“小猴子的技术笔记”关注个人公众号,有问题能够及时和我交流。编程
你知道插座吗?你知道网络编程中的插座吗?也许你会有点迷惑,什么是插座!可是我若是说出“套接字”、“socket”这样的关键字你就会恍然大悟。tomcat
所谓的“插座”叫作套接字又叫作socket,用来表示一个端点,能够与网络中其余的socket进行链接,而后进行数据的传输。安全
咱们都知道在网络上中能够经过IP地址肯定惟一的一台主机,而后主机和主机之间进行通信。可是准确来讲:网络通信中的双方并非主机,而是主机中的进程。这就须要肯定主机中那个进程进行的网络通信,所以还须要一个端口号来肯定主机中的惟一进程。微信
若是从操做系统的角度来讲:套接字是使用标准的Unix文件描述符来与其余计算机进行通信的一种方式。网络
IP+PORT的组合就构成了网络中惟一标识符“套接字”。端口号的范围是0-65535,可是低于256的端口号进行了保留,做为系统的标准的应用程序端口。好比HTTP的默认的80,POP3的110,Telent的23,FTP的21等。app
套接字容许两个进程进行通信,这两个进程可能运行在同一个机器上,也可能运行在不一样机器上。
套接字主要有两类:流式套接字和数据报套接字。框架
流式套接字提供了面向链接、可靠的数据传输服务,能够很是准确的实现按照顺序接收数据。若是你经过流式套接字发送"h","e","l","l","o"五个字符,它到达另外一端的顺序也将会是"h","e","l","l","o"。缘由就在于流式套接字使用的是TCP进行数据传输,可以保证数据的安全性。流式套接字是最常使用的,一些众所周知的协议使用的都是它,如HTTP,TCP,SMTP,POP3等。
数据报套接字:提供无链接的服务,你不须要像流式套接字那样创建一个链接,而只须要将地址信息一同打包而后发出去。该服务使用的是UDP进行传输,延迟小,效率高可是不能保证数据传输的准确性。socket
若是咱们建立一个ServerSocket须要经历如下几个步骤:bind-->listen-->accpet-->recv-->write-->close。这也是底层Linux/或者Unix对一个端口监听经历的步骤。this
bind:绑定一个地址和端口,确认端点的信息。编码
ServerSocket serverSocket = new ServerSocket(8888);
这里也许你看到了并无指明IP,那么“ServerSocket”会自动获取本机的ip地址做为填充,源码以下:
public ServerSocket(int port) throws IOException { this(port, 50, null); }
public ServerSocket(int port, int backlog, InetAddress bindAddr) throws IOException { setImpl(); if (port < 0 || port > 0xFFFF) throw new IllegalArgumentException("Port value out of range: " + port); if (backlog < 1) backlog = 50; try { bind(new InetSocketAddress(bindAddr, port), backlog); } catch(SecurityException e) { close(); throw e; } catch(IOException e) { close(); throw e; } }
public InetSocketAddress(InetAddress addr, int port) { holder = new InetSocketAddressHolder( null, addr == null ? InetAddress.anyLocalAddress() : addr, checkPort(port)); }
会对“InetAddress”进行判断,若是为空了就获取本地的地址。
listen:当端口和ip绑定以后就会进行到监听状态,这个时候会监听是否有链接请求上来。
accept:若是客户端的ip和端口符合服务端,也就是插头可以对应上插座了,就接收这个客户端的链接。这里“ServerSocket.accept()”将“listen”和“accept”进行了整合,监听而且接受一个链接。
Socket socket = serverSocket.accept();
send:发送数据,须要先得到socket的的输出流,而后经过输出流进行数据的发送。
OutputStream out = socket.getOutputStream();
拿到的是输出流,所以发送的是字节,须要将字符串进行字节的转换以后,在进行编码以后发送。若是没有进行编码的话,默认采用的是系统的默认编码。
out.write("hello".getBytes(StandardCharsets.UTF_8));
recv: 接受数据,也须要先获取到socket的输入流。
InputStream inputStream = socket.getInputStream();
获取输入流以后按照字节进行读取数据。若是数据发送完毕了,也就是调用了“socket.close()”就会读取到“-1”,表示没有数据能够读到了。
InputStream in = socket.getInputStream(); StringBuffer sb = new StringBuffer(); int len; while ((len = in.read(bytes)) != -1) { sb.append(new String(bytes, 0, len)); } System.out.println("接收到消息:" + sb.toString());
close:关闭(这个不是必须的,若是是长连接,那么将不会关闭socket,除非服务发生异常)
serverSocket.close();
上述的客户端是阻塞式的,一次只能接受一个socket客户端进行链接。
客户端要经历:connect-->recv/send-->close
Socket socket = new Socket("localhost", 8888);
recv:获取到输入流,读取字节数字。
byte[] bytes = new byte[1024]; InputStream in = socket.getInputStream(); StringBuffer sb = new StringBuffer(); int len; while ((len = in.read(bytes)) != -1) { sb.append(new String(bytes, 0, len)); } System.out.println("接收到消息:" + sb.toString());
write:获取到输出流,将数据按照字节输出给服务端。
OutputStream out = socket.getOutputStream(); out.write("hello".getBytes(StandardCharsets.UTF_8));
close:关闭资源
in.close(); out.close(); socket.close(); System.out.println("数据接收完毕");
至此基于一个简单的socket网络编程就完成了。咱们须要重点了解,socket是基于IP+Port组合的,用于网络中的通讯之间的创建。网络编程会在咱们的平常开发中起到很重要的做用,不少框架都是基于socket演变而来的,好比tomcat、netty等。 最后欢迎你们添加个人微信公众号号"小猴子的技术笔记",若是有问题能够及时和我交流。