当前位置: 首页 > news >正文

想学做宝宝食谱上什么网站在家帮别人做网站赚钱吗

想学做宝宝食谱上什么网站,在家帮别人做网站赚钱吗,兰州网站开发公司,做微信的网站秀客Socket是Java网络编程的基础#xff0c;了解还是有好处的#xff0c;这篇文章主要讲解Socket的基础编程。Socket用在哪呢#xff0c;主要用在进程间#xff0c;网络间通信。本篇比较长#xff0c;特别做了个目录#xff1a;一、Socket通信基本示例这种模式是基础#xf…Socket是Java网络编程的基础了解还是有好处的这篇文章主要讲解Socket的基础编程。Socket用在哪呢主要用在进程间网络间通信。本篇比较长特别做了个目录一、Socket通信基本示例这种模式是基础必须掌握后期对Socket的优化都是在这个基础上的也是为以后学习NIO做铺垫。package yiwangzhibujian.onlysend;import java.io.InputStream;import java.net.ServerSocket;import java.net.Socket;public class SocketServer {public static void main(String[] args) throws Exception {// 监听指定的端口int port 55533;ServerSocket server new ServerSocket(port);// server将一直等待连接的到来System.out.println(server将一直等待连接的到来);Socket socket server.accept();// 建立好连接后从socket中获取输入流并建立缓冲区进行读取InputStream inputStream socket.getInputStream();byte[] bytes new byte[1024];int len;StringBuilder sb new StringBuilder();while ((len inputStream.read(bytes)) ! -1) {//注意指定编码格式发送方和接收方一定要统一建议使用UTF-8sb.append(new String(bytes, 0, len,UTF-8));}System.out.println(get message from client: sb);inputStream.close();socket.close();server.close();}}服务端监听一个端口等待连接的到来package yiwangzhibujian.onlysend;import java.io.OutputStream;import java.net.Socket;public class SocketClient {public static void main(String args[]) throws Exception {// 要连接的服务端IP地址和端口String host 127.0.0.1;int port 55533;// 与服务端建立连接Socket socket new Socket(host, port);// 建立连接后获得输出流OutputStream outputStream socket.getOutputStream();String message你好 yiwangzhibujian;socket.getOutputStream().write(message.getBytes(UTF-8));outputStream.close();socket.close();}}客户端通过ip和端口连接到指定的server然后通过Socket获得输出流并向其输出内容服务器会获得消息。最终服务端控制台打印如下server将一直等待连接的到来get message from client: 你好 yiwangzhibujian通过这个例子应该掌握并了解Socket服务端和客户端的基本编程传输编码统一指定防止乱码这个例子做为学习的基本例子实际开发中会有各种变形比如客户端在发送完消息后需要服务端进行处理并返回如下。二、消息通信优化2.1 双向通信发送消息并接受消息这个也是做为Socket编程的基本应该掌握例子如下package yiwangzhibujian.waitreceive;import java.io.InputStream;import java.io.OutputStream;import java.net.ServerSocket;import java.net.Socket;public class SocketServer {public static void main(String[] args) throws Exception {// 监听指定的端口int port 55533;ServerSocket server new ServerSocket(port);// server将一直等待连接的到来System.out.println(server将一直等待连接的到来);Socket socket server.accept();// 建立好连接后从socket中获取输入流并建立缓冲区进行读取InputStream inputStream socket.getInputStream();byte[] bytes new byte[1024];int len;StringBuilder sb new StringBuilder();//只有当客户端关闭它的输出流的时候服务端才能取得结尾的-1while ((len inputStream.read(bytes)) ! -1) {// 注意指定编码格式发送方和接收方一定要统一建议使用UTF-8sb.append(new String(bytes, 0, len, UTF-8));}System.out.println(get message from client: sb);OutputStream outputStream socket.getOutputStream();outputStream.write(Hello Client,I get the message..getBytes(UTF-8));inputStream.close();outputStream.close();socket.close();server.close();}}与之前server的不同在于当读取完客户端的消息后打开输出流将指定消息发送回客户端客户端程序为package yiwangzhibujian.waitreceive;import java.io.InputStream;import java.io.OutputStream;import java.net.Socket;public class SocketClient {public static void main(String args[]) throws Exception {// 要连接的服务端IP地址和端口String host 127.0.0.1;int port 55533;// 与服务端建立连接Socket socket new Socket(host, port);// 建立连接后获得输出流OutputStream outputStream socket.getOutputStream();String message 你好 yiwangzhibujian;socket.getOutputStream().write(message.getBytes(UTF-8));//通过shutdownOutput高速服务器已经发送完数据后续只能接受数据socket.shutdownOutput();InputStream inputStream socket.getInputStream();byte[] bytes new byte[1024];int len;StringBuilder sb new StringBuilder();while ((len inputStream.read(bytes)) ! -1) {//注意指定编码格式发送方和接收方一定要统一建议使用UTF-8sb.append(new String(bytes, 0, len,UTF-8));}System.out.println(get message from server: sb);inputStream.close();outputStream.close();socket.close();}}客户端也有相应的变化在发送完消息时调用关闭输出流方法然后打开输出流等候服务端的消息。2.2 使用场景这个模式的使用场景一般用在客户端发送命令给服务器然后服务器相应指定的命令如果只是客户端发送消息给服务器然后让服务器返回收到消息的消息这就有点过分了这就是完全不相信Socket的传输安全性要知道它的底层可是TCP如果没有发送到服务器端是会抛异常的这点完全不用担心。2.3 如何告知对方已发送完命令其实这个问题还是比较重要的正常来说客户端打开一个输出流如果不做约定也不关闭它那么服务端永远不知道客户端是否发送完消息那么服务端会一直等待下去直到读取超时。所以怎么告知服务端已经发送完消息就显得特别重要。2.3.1 通过Socket关闭这个是第一章介绍的方式当Socket关闭的时候服务端就会收到响应的关闭信号那么服务端也就知道流已经关闭了这个时候读取操作完成就可以继续后续工作。但是这种方式有一些缺点客户端Socket关闭后将不能接受服务端发送的消息也不能再次发送消息如果客户端想再次发送消息需要重现创建Socket连接2.3.2 通过Socket关闭输出流的方式这种方式调用的方法是socket.shutdownOutput();而不是(outputStream为发送消息到服务端打开的输出流)outputStream.close();如果关闭了输出流那么相应的Socket也将关闭和直接关闭Socket一个性质。调用Socket的shutdownOutput()方法底层会告知服务端我这边已经写完了那么服务端收到消息后就能知道已经读取完消息如果服务端有要返回给客户的消息那么就可以通过服务端的输出流发送给客户端如果没有直接关闭Socket。这种方式通过关闭客户端的输出流告知服务端已经写完了虽然可以读到服务端发送的消息但是还是有一点点缺点不能再次发送消息给服务端如果再次发送需要重新建立Socket连接这个缺点在访问频率比较高的情况下将是一个需要优化的地方。2.3.3 通过约定符号这种方式的用法就是双方约定一个字符或者一个短语来当做消息发送完成的标识通常这么做就需要改造读取方法。假如约定单端的一行为end代表发送完成例如下面的消息end则代表消息发送完成hello yiwangzhibujianend那么服务端响应的读取操作需要进行如下改造Socket socket server.accept();// 建立好连接后从socket中获取输入流并建立缓冲区进行读取BufferedReader readnew BufferedReader(new InputStreamReader(socket.getInputStream(),UTF-8));String line;StringBuilder sb new StringBuilder();while ((line read.readLine()) ! null end.equals(line)) {//注意指定编码格式发送方和接收方一定要统一建议使用UTF-8sb.append(line);}可以看见服务端不仅判断是否读到了流的末尾还判断了是否读到了约定的末尾。这么做的优缺点如下优点不需要关闭流当发送完一条命令(消息)后可以再次发送新的命令(消息)缺点需要额外的约定结束标志太简单的容易出现在要发送的消息中误被结束太复杂的不好处理还占带宽经过了这么多的优化还是有缺点难道就没有完美的解决方案吗答案是有的看接下来的内容。2.3.4 通过指定长度如果你了解一点class文件的结构(后续会写敬请期待)那么你就会佩服这么设计方式也就是说我们可以在此找灵感就是我们可以先指定后续命令的长度然后读取指定长度的内容做为客户端发送的消息。现在首要的问题就是用几个字节指定长度呢我们可以算一算1个字节最大256表示256B2个字节最大65536表示64K3个字节最大16777216表示16M4个字节最大4294967296表示4G依次类推这个时候是不是很纠结最大的当然是最保险的但是真的有必要选择最大的吗其实如果你稍微了解一点UTF-8的编码方式(字符编码后续会写敬请期待)那么你就应该能想到为什么一定要固定表示长度字节的长度呢我们可以使用变长方式来表示长度的表示比如第一个字节首位为0即0XXXXXXX表示长度就一个字节最大128表示128B第一个字节首位为110那么附带后面一个字节表示长度即110XXXXX 10XXXXXX最大2048表示2K第一个字节首位为1110那么附带后面二个字节表示长度即110XXXXX 10XXXXXX 10XXXXXX最大131072表示128K依次类推上面提到的这种用法适合高富帅的程序员使用一般呢如果用作命名发送两个字节就够了如果还不放心4个字节基本就能满足你的所有要求下面的例子我们将采用2个字节表示长度目的只是给你一种思路让你知道有这种方式来获取消息的结尾服务端程序package yiwangzhibujian.waitreceive2;import java.io.InputStream;import java.net.ServerSocket;import java.net.Socket;public class SocketServer {public static void main(String[] args) throws Exception {// 监听指定的端口int port 55533;ServerSocket server new ServerSocket(port);// server将一直等待连接的到来System.out.println(server将一直等待连接的到来);Socket socket server.accept();// 建立好连接后从socket中获取输入流并建立缓冲区进行读取InputStream inputStream socket.getInputStream();byte[] bytes;// 因为可以复用Socket且能判断长度所以可以一个Socket用到底while (true) {// 首先读取两个字节表示的长度int first inputStream.read();//如果读取的值为-1 说明到了流的末尾Socket已经被关闭了此时将不能再去读取if(first-1){break;}int second inputStream.read();int length (first 8) second;// 然后构造一个指定长的byte数组bytes new byte[length];// 然后读取指定长度的消息即可inputStream.read(bytes);System.out.println(get message from client: new String(bytes, UTF-8));}inputStream.close();socket.close();server.close();}}此处的读取步骤为先读取两个字节的长度然后读取消息客户端为package yiwangzhibujian.waitreceive2;import java.io.OutputStream;import java.net.Socket;public class SocketClient {public static void main(String args[]) throws Exception {// 要连接的服务端IP地址和端口String host 127.0.0.1;int port 55533;// 与服务端建立连接Socket socket new Socket(host, port);// 建立连接后获得输出流OutputStream outputStream socket.getOutputStream();String message 你好 yiwangzhibujian;//首先需要计算得知消息的长度byte[] sendBytes message.getBytes(UTF-8);//然后将消息的长度优先发送出去outputStream.write(sendBytes.length 8);outputStream.write(sendBytes.length);//然后将消息再次发送出去outputStream.write(sendBytes);outputStream.flush();//此处重复发送一次实际项目中为多个命名此处只为展示用法message 第二条消息;sendBytes message.getBytes(UTF-8);outputStream.write(sendBytes.length 8);outputStream.write(sendBytes.length);outputStream.write(sendBytes);outputStream.flush();//此处重复发送一次实际项目中为多个命名此处只为展示用法message the third message!;sendBytes message.getBytes(UTF-8);outputStream.write(sendBytes.length 8);outputStream.write(sendBytes.length);outputStream.write(sendBytes);outputStream.close();socket.close();}}客户端要多做的是在发送消息之前先把消息的长度发送过去。这种事先约定好长度的做法解决了之前提到的种种问题Redis的Java客户端Jedis就是用这种方式实现的这种方式的缺点暂时还没发现当然如果是需要服务器返回结果那么也依然使用这种方式服务端也是先发送结果的长度然后客户端进行读取。当然现在流行的就是长度类型数据模式的传输方式。三、服务端优化3.1 服务端并发处理能力在上面的例子中服务端仅仅只是接受了一个Socket请求并处理了它然后就结束了但是在实际开发中一个Socket服务往往需要服务大量的Socket请求那么就不能再服务完一个Socket的时候就关闭了这时候可以采用循环接受请求并处理的逻辑package yiwangzhibujian.multiserver;import java.io.IOException;import java.io.InputStream;import java.net.ServerSocket;import java.net.Socket;public class SocketServer {public static void main(String args[]) throws IOException {// 监听指定的端口int port 55533;ServerSocket server new ServerSocket(port);// server将一直等待连接的到来System.out.println(server将一直等待连接的到来);while(true){Socket socket server.accept();// 建立好连接后从socket中获取输入流并建立缓冲区进行读取InputStream inputStream socket.getInputStream();byte[] bytes new byte[1024];int len;StringBuilder sb new StringBuilder();while ((len inputStream.read(bytes)) ! -1) {// 注意指定编码格式发送方和接收方一定要统一建议使用UTF-8sb.append(new String(bytes, 0, len, UTF-8));}System.out.println(get message from client: sb);inputStream.close();socket.close();}}}这种一般也是新手写法但是能够循环处理多个Socket请求不过当一个请求的处理比较耗时的时候后面的请求将被阻塞所以一般都是用多线程的方式来处理Socket即每有一个Socket请求的时候就创建一个线程来处理它。不过在实际生产中创建的线程会交给线程池来处理为了线程复用创建线程耗时回收线程慢防止短时间内高并发指定线程池大小超过数量将等待方式短时间创建大量线程导致资源耗尽服务挂掉package yiwangzhibujian.threadserver;import java.io.InputStream;import java.net.ServerSocket;import java.net.Socket;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;public class SocketServer {public static void main(String args[]) throws Exception {// 监听指定的端口int port 55533;ServerSocket server new ServerSocket(port);// server将一直等待连接的到来System.out.println(server将一直等待连接的到来);//如果使用多线程那就需要线程池防止并发过高时创建过多线程耗尽资源ExecutorService threadPool Executors.newFixedThreadPool(100);while (true) {Socket socket server.accept();Runnable runnable()-{try {// 建立好连接后从socket中获取输入流并建立缓冲区进行读取InputStream inputStream socket.getInputStream();byte[] bytes new byte[1024];int len;StringBuilder sb new StringBuilder();while ((len inputStream.read(bytes)) ! -1) {// 注意指定编码格式发送方和接收方一定要统一建议使用UTF-8sb.append(new String(bytes, 0, len, UTF-8));}System.out.println(get message from client: sb);inputStream.close();socket.close();} catch (Exception e) {e.printStackTrace();}};threadPool.submit(runnable);}}}使用线程池的方式算是一种成熟的方式。可以应用在生产中。3.2 服务端其他属性ServerSocket有以下3个属性。SO_TIMEOUT表示等待客户连接的超时时间。一般不设置会持续等待。SO_REUSEADDR表示是否允许重用服务器所绑定的地址。一般不设置经我的测试没必要下面会进行详解。SO_RCVBUF表示接收数据的缓冲区的大小。一般不设置用系统默认就可以了。具体详细的解释可以参照下面。3.3 性能再次提升当现在的性能还不能满足需求的时候就需要考虑使用NIO这不是本篇的内容后续会贴出。四、Socket的其它知识其实如果经常看有关网络编程的源码的话就会发现Socket还是有很多设置的可以学着用但是还是要有一些基本的了解比较好。下面就对Socket的Java API中涉及到的进行简单讲解。首先呢Socket有哪些可以设置的选项其实在SocketOptions接口中已经都列出来了int TCP_NODELAY 0x0001对此连接禁用 Nagle 算法。int SO_BINDADDR 0x000F此选项为 TCP 或 UDP 套接字在 IP 地址头中设置服务类型或流量类字段。int SO_REUSEADDR 0x04设置套接字的 SO_REUSEADDR。int SO_BROADCAST 0x0020此选项启用和禁用发送广播消息的处理能力。int IP_MULTICAST_IF 0x10设置用于发送多播包的传出接口。int IP_MULTICAST_IF2 0x1f设置用于发送多播包的传出接口。int IP_MULTICAST_LOOP 0x12此选项启用或禁用多播数据报的本地回送。int IP_TOS 0x3此选项为 TCP 或 UDP 套接字在 IP 地址头中设置服务类型或流量类字段。int SO_LINGER 0x0080指定关闭时逗留的超时值。int SO_TIMEOUT 0x1006设置阻塞 Socket 操作的超时值 ServerSocket.accept(); SocketInputStream.read(); DatagramSocket.receive(); 选项必须在进入阻塞操作前设置才能生效。int SO_SNDBUF 0x1001设置传出网络 I/O 的平台所使用的基础缓冲区大小的提示。int SO_RCVBUF 0x1002设置传入网络 I/O 的平台所使用基础缓冲区的大小的提示。int SO_KEEPALIVE 0x0008为 TCP 套接字设置 keepalive 选项时int SO_OOBINLINE 0x1003置 OOBINLINE 选项时在套接字上接收的所有 TCP 紧急数据都将通过套接字输入流接收。上面只是简单介绍了下(来源Java API)下面有对其中的某些的详细讲解没讲到的后续如果用到会补上。4.1 客户端绑定端口服务端绑定端口是可以理解的因为要监听指定的端口但是客户端为什么要绑定端口说实话我觉得这么做的人有点2或许有的网络安全策略配置了端口访出使用户只能使用指定的端口那么这样的配置也是挺2的直接说就可以不要留面子。当然首先要理解的是如果没有指定端口的话Socket会自动选取一个可以用的端口不用瞎操心的。但是你非得指定一个端口也是可以的做法如下这时候就不能用Socket的构造方法了要一步一步来// 要连接的服务端IP地址和端口String host localhost;int port 55533;// 与服务端建立连接Socket socket new Socket();socket.bind(new InetSocketAddress(55534));socket.connect(new InetSocketAddress(host, port));这样做就可以了但是当这个程序执行完成以后再次执行就会报端口占用异常java.net.BindException: Address already in use: connect明明上一个Socket已经关闭了为什么再次使用还会说已经被占用了呢如果你是用netstat 命令来查看端口的使用情况netstat -n|findstr 55533TCP 127.0.0.1:55534 127.0.0.1:55533 TIME_WAIT就会发现端口的使用状态为TIME_WAIT说到这你需要有一点TCP连接的基本常识建议看《》这是其中的一点摘抄笔记或许对理解有一些帮助。简单来说当连接主动关闭后端口状态变为TIME_WAIT其他程序依然不能使用这个端口防止服务端因为超时重新发送的确认连接断开对新连接的程序造成影响。TIME_WAIT的时间一般有底层决定一般是2分钟还有1分钟和30秒的。所以客户端不要绑定端口不要绑定端口不要绑定端口。4.2 读超时SO_TIMEOUT读超时这个属性还是比较重要的当Socket优化到最后的时候往往一个Socket连接会一直用下去那么当一端因为异常导致连接没有关闭另一方是不应该持续等下去的所以应该设置一个读取的超时时间当超过指定的时间后还没有读到数据就假定这个连接无用然后抛异常捕获异常后关闭连接就可以了调用方法为public void setSoTimeout(int timeout)throws SocketExceptiontimeout - 指定的以毫秒为单位的超时值。设置0为持续等待下去。建议根据网络环境和实际生产环境选择。这个选项设置的值将对以下操作有影响ServerSocket.accept()SocketInputStream.read()DatagramSocket.receive()4.3 设置连接超时这个连接超时和上面说的读超时不一样读超时是在建立连接以后读数据时使用的而连接超时是在进行连接的时候等待的时间。4.4 判断Socket是否可用当需要判断一个Socket是否可用的时候不能简简单单判断是否为null是否关闭下面给出一个比较全面的判断Socket是否可用的表达式这是根据Socket自身的一些状态进行判断的它的状态有bound是否绑定closed是否关闭connected是否连接shutIn是否关闭输入流shutOut是否关闭输出流socket ! null socket.isBound() !socket.isClosed() socket.isConnected() !socket.isInputShutdown() !socket.isOutputShutdown()建议如此使用但这只是第一步保证Socket自身的状态是可用的但是当连接正常创建后上面的属性如果不调用本方相应的方法是不会改变的也就是说如果网络断开、服务器主动断开Java底层是不会检测到连接断开并改变Socket的状态所以真实的检测连接状态还是得通过额外的手段有两种方式。4.4.1 自定义心跳包双方需要约定什么样的消息属于心跳包什么样的消息属于正常消息假设你看了上面的章节现在说就容易理解了我们定义前两个字节为消息的长度那么我们就可以定义第3个字节为消息的属性可以指定一位为消息的类型1为心跳0为正常消息。那么要做的有如下客户端发送心跳包服务端获取消息判断是否是心跳包若是丢弃当客户端发送心跳包失败时就可以断定连接不可用具体的编码不再贴出自己实现即可。4.4.2 通过发送紧急数据Socket自带一种模式那就是发送紧急数据这有一个前提那就是服务端的OOBINLINE不能设置为true它的默认值是false。OOBINLINE的true和false影响了什么对客户端没有影响对服务端如果设置为true那么服务端将会捕获紧急数据这会对接收数据造成混淆需要额外判断发送紧急数据通过调用Socket的方法socket.sendUrgentData(0);发送数据任意即可因为OOBINLINE为false的时候服务端会丢弃掉紧急数据。当发送紧急数据报错以后我们就会知道连接不通了。4.4.3 真的需要判断连接断开吗通过上面的两种方式已经可以判断出连接是否可用然后我们就可以进行后续操作可是请大家认真考虑下面的问题发送心跳成功时确认连接可用当再次发送消息时能保证连接还可用吗即便中间的间隔很短如果连接不可用了你会怎么做重新建立连接再次发送数据还是说单单只是记录日志如果你打算重新建立连接那么发送心跳包的意义何在为何不在发送异常时再新建连接如果你认真考虑了上面的问题那么你就会觉得发送心跳包完全是没有必要的操作通过发送心跳包来判断连接是否可用是通过捕获异常来判断的。那么我们完全可以在发送消息报出IO异常的时候在异常中重新发送一次即可这两种方式的编码有什么不同呢下面写一写伪代码。提前检测连接是否可用//有一个连接中的socketSocket socket...//要发送的数据String data;try{//发送心跳包或者紧急数据来检测连接的可用性}catch (Excetption e){//打印日志并重连Socketsocketnew Socket(host,port);}socket.write(data);直接发送数据出异常后重新连接再次发送//有一个连接中的socketSocket socket...//要发送的数据String data;try{socket.write(data);}catch (Excetption e){//打印日志并重连Socketsocketnew Socket(host,port);socket.write(data);}通过比较可以发现两种方式的特点现在简单介绍下两种方式均可实现连接断开重新连接并发送提前检测再每次发送消息的时候都要检测影响效率占用带宽希望大家认真考虑做出自己的选择。4.5 设置端口重用SO_REUSEADDR首先创建Socket时默认是禁止的设置true有什么作用呢Java API中是这么介绍的关闭 TCP 连接时该连接可能在关闭后的一段时间内保持超时状态(通常称为 TIME_WAIT 状态或 2MSL 等待状态)。对于使用已知套接字地址或端口的应用程序而言如果存在处于超时状态的连接(包括地址和端口)可能不能将套接字绑定到所需的 SocketAddress 上。使用 bind(SocketAddress) 绑定套接字前启用 SO_REUSEADDR 允许在上一个连接处于超时状态时绑定套接字。一般是用在绑定端口的时候使用但是经过我的测试建议如下服务端绑定端口后关闭服务端重新启动后不会提示端口占用客户端绑定端口后关闭即便设置ReuseAddress为true即便能绑定端口连接的时候还是会报端口占用异常综上所述不建议绑定端口也没必要设置ReuseAddress当然ReuseAddress的底层还是和硬件有关系的或许在你的机器上测试结果和我不一样若是如此和平台相关性差异这么大配置更是不建议使用了。4.6 设置关闭等待SO_LINGERJava API的介绍是启用/禁用具有指定逗留时间(以秒为单位)的 SO_LINGER。最大超时值是特定于平台的。 该设置仅影响套接字关闭。大家都是这么说的当调用Socket的close方法后没有发送的数据将不再发送设置这个值的话Socket会等待指定的时间发送完数据包。说实话经过我简单的测试对于一般数据量来说几十K左右即便直接关闭Socket的连接服务端也是可以收到数据的。所以对于一般应用没必要设置这个值当数据量发送过大抛出异常时再来设置这个值也不晚。那么到达逗留超时值时套接字将通过 TCP RST 强制性 关闭。启用超时值为零的选项将立即强制关闭。如果指定的超时值大于 65,535则其将被减少到 65,535。4.7 设置发送延迟策略TCP_NODELAY一般来说当客户端想服务器发送数据的时候会根据当前数据量来决定是否发送如果数据量过小那么系统将会根据Nagle 算法(暂时还没研究)来决定发送包的合并也就是说发送会有延迟这在有时候是致命的比如说对实时性要求很高的消息发送在线对战游戏等即便数据量很小也要求立即发送如果稍有延迟就会感觉到卡顿默认情况下Nagle 算法是开启的所以如果不打算有延迟最好关闭它。这样一旦有数据将会立即发送而不会写入缓冲区。但是对延迟要求不是特别高下还是可以使用的还是可以提升网络传输效率的。4.8 设置输出输出缓冲区大小SO_RCVBUF/SO_SNDBUFSO_SNDBUF发送缓冲SO_RCVBUF接收缓冲默认都是8K如果有需要可以修改通过相应的set方法。不建议修改的太小设置太小数据传输将过于频繁。太大了将会造成消息停留。不过我对这个经过测试后有以下结论当数据填满缓冲区时一定会发送当数据没有填满缓冲区时也会发送这个算法还是上面说的Nagle 算法4.9 设置保持连接存活SO_KEEPALIVE虽然说当设置连接连接的读超时为0即无限等待时Socket不会被主动关闭但是总会有莫名其妙的软件来检测你的连接是否有数据发送长时间没有数据传输的连接会被它们关闭掉。因此通过设置这个选项为true可以有如下效果当2个小时(具体的实现而不同)内在任意方向上都没有跨越套接字交换数据则 TCP 会自动发送一个保持存活的消息到对面。将会有以下三种响应返回期望的ACK。那么不通知应用程序(因为一切正常)2 小时的不活动时间过后TCP 将发送另一个探头。对面返回RST表明对面挂了但是又好了Socket依然要关闭没有响应说明对面挂了这时候关闭Socket所以对于构建长时间连接的Socket还是配置上SO_KEEPALIVE比较好。4.10 异常java.net.SocketException: Connection reset by peer这个异常的含义是我正在写数据的时候你把连接给关闭了。这个异常在一般正常的编码是不会出现这个异常的因为用户通常会判断是否读到流的末尾了读到末尾才会进行关闭操作如果出现这个异常那就检查一下判断是否读到流的末尾逻辑是否正确。五、关于Socket的理解5.1 Socket和TCP/IP最近在看《TCP/IP详解 卷1协议》关于TCP/IP我觉得讲解的非常详细我做了点摘抄可以大致看看非常建议大家阅读下这本书。通常TCP/IP分为四层也就是说Socket实际上是归属于应用层使用的事运输层的TCP使用SocketServer监听的端口也是可以被Telnet连接的。可以看下面两行代码ServerSocket server new ServerSocket(port);Socket socket server.accept();在什么情况获取到这个Socket呢通过理论加测试结论是在三次握手操作后系统才会将这个连接交给应用层ServerSocket 才知道有一个连接过来了。那么系统当接收到一个TCP连接请求后如果上层还没有接受它(假如SocketServer循环处理Socket一次一个)那么系统将缓存这个连接请求既然是缓存那么就是有限度的书上介绍的是缓存3个但是经过我的本机测试是50个也就是说系统将会为应用层的Socket缓存50和TCP连接(这是和系统底层有关系的)当超过指定数量后系统将会拒绝连接。假如缓存的TCP连接请求发送来数据那么系统也会缓存这些数据等待SocketServer获得这个连接的时候一并交给它这个会在后期学习NIO进行详解。换句话说系统接收TCP连接请求放入缓存队列而SocketServer从缓存队列获取Socket。而上面例子中的为了让服务端知道发送完消息的关闭输出流的操作socket.shutdownOutput();其实是对应着四次挥手的第一次也就是上面说的主动关闭FIN_WAIT_1这样服务端就能得知客户端发送完消息此时服务端可以选择关闭连接也可以选择发送数据后关闭连接这就是TCP所说的半关闭。其实很多知识都是想通的多学点基础知识还是有必要的。5.2 Socket和RMIRMI基础知识就不多介绍了(后续会写敬请期待)现在假定你对RMI有所了解那么一般就会对这两种技术有所比较。或者说在应用的时候就会想用那种技术比较好。RMI全称Remote Method Invocation远程方法调用通过名字其实就能对这种技术有个初步的了解。现在我就简单说说我对这两种技术的想法。这个待写等我写完RMI博客的时候补上那时候会更细致的了解下。5.3 DatagramSocket与Socket这一段涉及到UDP依然和上面一样后续会补上。5.4 拆包和黏包使用Socket通信的时候或多或少都听过拆包和黏包如果没听过而去贸然编程那么偶尔就会碰到一些莫名其妙的问题所有有这方面的知识还是比较重要的至少知道怎么发生怎么防范。现在先简单说明下拆包和黏包的原因拆包当一次发送(Socket)的数据量过大而底层(TCP/IP)不支持一次发送那么大的数据量则会发生拆包现象。黏包当在短时间内发送(Socket)很多数据量小的包时底层(TCP/IP)会根据一定的算法(指Nagle)把一些包合作为一个包发送。首先可以明确的是大部分情况下我们是不希望发生拆包和黏包的(如果希望发生什么都去做即可)那么怎么去避免呢下面进行详解5.4.1 黏包首先我们应该正确看待黏包黏包实际上是对网络通信的一种优化假如说上层只发送一个字节数据而底层却发送了41个字节其中20字节的I P首部、 20字节的T C P首部和1个字节的数据而且发送完后还需要确认这么做浪费了带宽量大时还会造成网络拥堵。当然它还是有一定的缺点的就是因为它会合并一些包会导致数据不能立即发送出去会造成延迟如果能接受(一般延迟为200ms)那么还是不建议关闭这种优化如果因为黏包会造成业务上的错误那么请改正你的服务端读取算法(协议)因为即便不发生黏包在服务端缓存区也可能会合并起来一起提交给上层推荐使用长度类型数据模式。如果不希望发生黏包那么通过禁用TCP_NODELAY即可Socket中也有相应的方法void setTcpNoDelay(boolean on)通过设置为true即可防止在发送的时候黏包但是当发送的速率大于读取的速率时在服务端也会发生黏包即因服务端读取过慢导致它一次可能读取多个包。5.4.2 拆包这个问题应该引起重视在TCP/IP详解中说过最大报文段长度(MSS)表示TCP传往另一端的最大块数据的长度。当一个连接建立时连接的双方都要通告各自的 MSS。客户端会尽量满足服务端的要求且不能大于服务端的MSS值当没有协商时会使用值536字节。虽然看起来MSS值越大越好但是考虑到一些其他情况这个值还是不太好确定具体详见《TCP/IP详解 卷1协议》。如何应对拆包其实在上面2.3节已经介绍过了那就是如何表明发送完一条消息了对于已知数据长度的模式可以构造相同大小的数组循环读取示例代码如下int length1024;//这个是读取的到数据长度现假定1024byte[] datanew byte[1024];int readLength0;while(readLengthint read inputStream.read(data, readLength, length-readLength);readLengthread;}这样当循环结束后就能读取到完整的一条数据而不需要考虑拆包了。
http://www.pierceye.com/news/23527/

相关文章:

  • 建设童装网站的意义seo技术教程
  • 网站推广官方平台学网站建设需要多长时间
  • 怎么用vs2017做网站做网站有什么专业术语
  • 怎样在工商局网站上做网登wordpress官方安装主题
  • 软件网站开发平台南京制作手机网站
  • 河北住房和城乡建设厅网站驱动工商年报网上申报系统官网
  • 一 建设茶叶网站前的市场分析杭州seo俱乐部
  • 山东mip网站建设怎么把网站做成自适应
  • 如何零基础做网站wordpress 手机模板调用pc内容
  • 如何做电影网站 去哪里找片源建设电子商务网站流程图
  • 关于 建设 旅游网站 建议wordpress 当前页面登录
  • 企业网站包含哪些页面杭州市规划建设网站
  • 学院网站建设流程十大货源网站大全
  • 苍梧网站建设石家庄红酒公司 网站建设
  • 旅游网站介绍怎么写个人博客网站模板源码
  • 浙江省住房与城乡建设部网站做网站多少钱西宁君博领先
  • 简繁网站怎么做理财网站开发文档
  • 展示产品的网站 个人备案还是企业有哪些教育网站做的比较好
  • 烟台h5网站开发创意设计椅子
  • 怎么在自己电脑上搭建网站自己怎么建设手机网站首页
  • 东莞外贸网站制作深圳企业seo
  • 营销的网站建设公司眉山做网站
  • 视频网站闪图怎么做网站建设和备案的顺序
  • 仿牌网站 域名注册美食网站建设服务策划书
  • 网站开发制作合同范本专业商城网站建设报价
  • 建网站一般要多少钱小程序代码
  • 购物网站源码网站建设一条龙
  • 选择做网站销售的优势国家商标注册官网入口
  • 网站制作合作协议dede网站婚纱模板
  • 江西网站开发的公司上海做企业网站的公司