socket的一些细节

在socket的编程时,一个发送一个接收,类似如下的代码

发送:     outputStream = sock.getOutputStream()     outputStream.write( … )  

接收:   inputStream= sock.getInputStream()     inputStream.read( … )

看似很简单,其实并不是那么简单,很多东不了解底层的东西,在写代码的时候,往往容易出问题。这一篇,来说说关于系统缓冲区的问题。

        发送与接收的细节是比较复杂的,首先说说发送方,当我们调用outputStream.write(data),其实并不没有把数据发送出去,只是把数据放到数据发送缓冲区,然后系统从数据发送缓冲区取出数据,经网卡发出去,也就是说,发数据这个动作并不是有程序来完成而是操作系统来完成的。

        接着来说说接收方,首先是操作系统从网卡读取网络数据,然后存在一个叫做数据接收缓冲区,然后程序调用inputStream.read( ),从数据接收缓冲区中读取数据。由此看来,每当我们打开一个socket连接,则系统会分配两个缓冲区,数据发送缓冲区和数据接收缓冲区。

       说完数据缓冲区的存在,我们就说说数据的发送和接收的规则。

      数据的发送规则:1、保证所有数据都会被发出去;2、保证数据的先来后到;3、不保证数据包的完整性;4、不保证马上能把数据发出去

      数据的接收规则:1、自动接收:无论程序有没有调用inputStream.read(),数据都会自动被OS接收并存储在数据接收缓冲区中;2,inputStream.read()仅仅是从数据接收缓冲区中取走数据,如果数据额接收缓冲区灭有数据,则次方法就会发生阻塞,如果缓冲区中数据较少,则是有多少读多少;3、InputStream.read() 重载了2个方法read(byte b[])、 read(byte b[], int off, int len)接收到的数据存到数组b里,而len表示试图读取的字节数,off是从哪里开始。由此可以知道,调用inputStream.read()取到的数据不保证完整性!

      为了能使数据的完整性,我们制定新的协议。1、发送方,我们可以先发送数据的长度,再讲数据发出去,byte[4]  + byte[N]  ( 变长编码 );2、接收方,先读取先四个字节得知数据的长度,然后再接收数据。

以下给出我自己改造后的接收和发送,算是抛砖引玉

发送方:

package my;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.ByteBuffer;

public class AdClientConnection
{
	private Socket socket;
	private InputStream input;
	private OutputStream out;
	private byte[] inputData = new byte[400];
	
    private String charSet = "UTF-8";
	

    /**
     * 连接
     * @param ip
     * @param port
     * @throws IOException
     */
	public void connetion(String ip , int port) throws IOException
	{
		socket = new Socket();
		socket.connect(new InetSocketAddress(ip, port));
		
		input = socket.getInputStream();
		out = socket.getOutputStream();
	}
	/**
	 * 关闭连接
	 * @throws IOException 
	 */
	public void close() throws IOException
	{
		if(socket != null)
		{
			socket.close();
			socket = null;
		}
	}
    /**
     * 完整读取数据
     * @param data  读取的数据
     * @param off   开始读取的下标
     * @param N    一共要读取的数据
     * @throws IOException 
     */
    public int readFully(byte[] data , int off , int N) throws IOException
    {
    	int count = 0;//已经读取的数据
    	while(count < N)
    	{
    		int remain = N - count;
    		int numBytes = input.read(data, off + count, remain);
    		if(numBytes < 0)
    			return -1;
    		count += numBytes;
    	}
    	return N;   	
    }
    /**
     * 发送文字
     * @param str
     * @throws IOException 
     */
    public void sendString(String str) throws IOException
    {
    	ByteBuffer buf = ByteBuffer.allocate(4);
    	//先获取字符串的大小
    	byte[] data = str.getBytes(this.charSet);
    	buf.putInt(data.length);
    	//先发送字符串的长度
    	out.write(buf.array(), 0, 4);
    	//再发送内容
    	out.write(data);   	
    }
    /**
     * 接收字符串
     * @return
     * @throws IOException 
     */
    public String recvString() throws IOException
    {
    	//首先接收字符串的长度
    	ByteBuffer buf = ByteBuffer.allocate(4);
    	int n = readFully(buf.array(), 0, 4);
    	int len = buf.getInt();
    	
    	readFully(inputData , 0 , len);
    	return new String(inputData , 0 , len , this.charSet);
    }

}

       接收方:

package my;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class MyServser
{

	public static void main(String[] args)
	{
		try
		{
			System.out.println("服务器已启动,等待连接");
			ServerSocket serverSocket = new ServerSocket(2019);
			Socket socket= serverSocket.accept();
			AdServerConnection server = new AdServerConnection(socket);
			socket.setSoTimeout(3000);
			String str = server.recvString();
			str += server.recvString();
			System.out.println("收到的信息" + str);
			server.sendString("我已收到,你也好呀");
			server.close();
		} catch (IOException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

}

 

你可能感兴趣的:(Java网络编程,socket,java学习之网络编程)