理解套接字

Posted by Kanon on December 10, 2017

什么是套接字(Socket)

套接字位于应用层与传输层之间,是应用程序与网络之间进行数据传输的门户,每一个套接字都与一个进程相关联。

那要如何区分不同的套接字呢,这里就要提到端口号的概念。

端口号

端口号是一个16比特的数,其大小在0-65535之间。0-1023范围的端口号称为周知端口号,是受限制的,这是指它们保留给诸如HTTP(80)和FTP(21)之类的周知应用层协议来使用。

UDP套接字

一个UDP套接字由一个二元组来标识,该二元组包含一个目的地址和一个目的端口号。

如果两个UDP报文段有不同的源IP地址或不同的源端口号,但具有相同的目的IP地址和目的端口号,那么这两个报文段将被定向到相同的目的套接字。

TCP套接字

一个TCP套接字是由一个四元组来标识,包括源IP地址、源端口号、目的IP地址、目的端口号。

当一个TCP报文段从网络到达一台主机时,该主机使用全部四个值来将报文段定向到相应的TCP套接字。

与UDP套接字不同,具有不同源IP地址和端口号的TCP报文段将被定向到不同的套接字。

Java实现简单的服务器与客户端

MultiThreadEchoServer.java

package 并发;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class MultiThreadEchoServer {
    class HandleMsg implements Runnable{
        Socket clientSocket;
		
        public HandleMsg(Socket clientSocket) {
            this.clientSocket = clientSocket;
        }
		
        public void run() {
            BufferedReader is = null;
            PrintWriter os = null;
            try {
                is = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
                os = new PrintWriter(clientSocket.getOutputStream(), true);
				
                //从inputStream中读取客户端发送的数据
                String inputLine = null;
                long b = System.currentTimeMillis();
                while ((inputLine = is.readLine()) != null) {
		
                    os.println(inputLine);
                }
                long e = System.currentTimeMillis();
                //输出处理一次客户端请求所花费的时间(包括读数据和回写数据的时间)
                System.out.println("spend:"+(e-b)+"ms");
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                //关闭输入输出流以及套接字
                try {
                    //确保is不为null,is不等于null说明输入流还没关,如果在等于null的情况下去close就会抛异常
                    if(is != null)
		    
                        is.close();
                    if(os != null)
		    
                        os.close();
                    clientSocket.close();
                }catch (IOException e){
                    e.printStackTrace();
                }
            }
        }
    }
	
    public static void main(String[] args) {
        ExecutorService pool = Executors.newCachedThreadPool();
        //欢迎套接字,所有要与服务器通信的客户的起始接触点,三次握手在这个套接字里完成
        ServerSocket echoServer = null;
        //连接套接字,随后为了与每个客户端通信而生成的套接字
        Socket clientSocket = null;
        try {
            echoServer = new ServerSocket(8000);
        }catch(IOException e) {
            System.out.println(e);
        }
		
        while(true) {
            try {
                clientSocket = echoServer.accept();
                System.out.println(clientSocket.getRemoteSocketAddress()+" Connect!");
                //非静态内部类的实例化需要通过外部类的对象
                pool.execute(new MultiThreadEchoServer().new HandleMsg(clientSocket));
            }catch(IOException e) {
                System.out.println(e);
            }
        }
    }
}

Client1.java

package 并发;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetSocketAddress;
import java.net.Socket;

public class Client1 {
    public static void main(String[] args) {
        Socket client = null;
        PrintWriter writer = null;
        BufferedReader reader = null;
        try {
            client = new Socket();
            client.connect(new InetSocketAddress("localhost", 8000));
            writer = new PrintWriter(client.getOutputStream(), true);
            writer.println("Hello!");
            //表示强制将缓冲区中的数据发送出去,不必等到缓冲区满.
            writer.flush();
			
            reader = new BufferedReader(new InputStreamReader(client.getInputStream()));
            System.out.println("from server:"+reader.readLine());
        }catch (IOException e) {
            e.printStackTrace();
        } finally {
            //关闭输入输出流以及套接字
            try {
                if(writer != null)
		
                    writer.close();
                if(reader != null)
		
                    reader.close();
                if(client != null)
		
                    client.close();
            }catch (IOException e){
                e.printStackTrace();
            }
        }
    }
}

欢迎套接字与连接套接字

服务器为了能够处理客户端的请求,需要先后建立两个套接字

  • 欢迎套接字(ServerSocket):所有来自客户端连接请求的起始接触点,三次握手在这个套接字里完成
  • 连接套接字:随后进行通信用的套接字

参考

《计算机网络自顶向下方法》

碎碎念

周日,天气晴,成都冬日里每个出太阳的日子都是节日。周末的沙河俨然成为成华区附属公园,穿着花花绿绿的大妈争相在银杏树下拍照,一对对情侣,嬉戏的孩童。望着那些快秃头的银杏,是不是也该来些霸王。一个人听着歌穿过人群,我也不知道我的脚步匆匆着什么。