看了這篇網絡編程,就能夠和麪試官聊聊了

網絡編程

一、網絡編程的基本概念
二、IP地址及端口號
三、通訊協議
四、TCP通訊代碼實踐
    4.1消息傳遞
    4.2文件上傳
五、UDP通訊代碼
    5.1 UDP實現消息發送
    5.2 使用UDP循環發送和接收消息
    5.3使用UDP實現聊天
複製代碼

一、網絡編程的基本概念

引例java

在學習網絡編程以前,咱們先看這樣一個例子。通常處於咱們這個年齡段的同窗,大都經歷過寫信/寄信的經歷,在寫信的時候,咱們通常都須要明確發送的地址、聯繫人信息以及所在地區的郵編,郵遞員能夠根據這上面的信息找到接收信件的人,接收人在閱讀過信件的內容後能夠以一樣的方式回信,這樣就使得身處異地的朋友間能夠進行通訊。編程

在這種狀況下,身處異地的朋友若是要進行通訊,必須同時知足如下條件服務器

1.接收人的所在地區及詳細的住址微信

2.須要有快遞員接收併發送markdown

慢慢的,隨着科技的發展,咱們有手機通訊、QQ聊天和微信聊天等多種通訊方式,採用這些方式咱們能夠更便捷的進行通訊,可是不管是那種方式,通訊過程當中老是須要這樣兩個元素:地址傳輸過程網絡

在網絡通訊中,這兩個元素可使用更專業的名詞來代替:多線程

一、IP地址和端口號併發

IP地址能夠幫助咱們找到通訊的接收方,端口號則能夠幫助咱們找到計算機中的具體應用,如QQ、微信等socket

二、傳輸協議ide

協議是爲了幫助咱們更好的進行傳輸,如TCP、UDP、FTP、SMTP和HTTP協議等

二、IP地址及端口號

IP地址是互聯網協議地址,它是一種統一的地址格式,互聯網中的每一臺主機都會有一個邏輯地址。在計算機網絡中,localhost(意爲「本地主機」,指「這臺計算機」)是給回的一個標準主機名,相對應的IP地址爲127.0.0.1

經過IP地址,咱們就能夠鏈接到指定的計算機了,可是若是要訪問目標計算機中的某個應用程序,還須要知道端口號。在計算中,不一樣的應用程序就是經過端口號來進行區分的。

在網絡編程中,可使用InetAddress進行主機名解析反向解析,即給定肯定的主機名,返回肯定的IP地址;給定IP地址,返回主機名

localhost解析爲127.0.0.1

127.0.0.1反向解析爲localhost

NOTE:不一樣協議的端口是能夠重複的,如UDP和TCP

端口的查詢操做

netstat -ano #查看全部的端口
netstat -ano | findstr "XXX" # 查看指定的端口
複製代碼

三、通訊協議

IP地址及端口號解決了通訊過程當中的地址問題,可是在計算機中,咱們還要解決如何通訊問題,所謂通訊就是計算機間如何交流,而通訊協議就是將計算機雙方遵循的一種規則和約定(如同普通話、英語),它能夠經過通訊信道將處於不一樣地理位置的設備鏈接起來,可以實現信息的交換和資源共享。

在計算機網絡中,經常使用的協議就是TCP/IP,它是協議簇,由多個子協議組成了,如咱們常見的TCPIPUDPARP等,咱們主要講解網絡編程中經常使用的TCP、UDP和IP

  • TCP

TCP協議是一種傳輸協議,面向鏈接、可靠的、基於字節流的傳輸層通訊協議

  • UDP

UDP是一種無鏈接的傳輸協議,無需創建鏈接就能夠發送數據包

  • IP

IP協議整個TCP/IP協議族的核心,對上可載送傳輸層各類協議的信息,例如TCP、UDP等;對下可將IP信息包放到鏈路層,經過以太網等各類技術來傳送。

TCP和UDP對比

TCP能夠類比於打電話,它具備如下特色

  • 在數據傳輸前,須要創建鏈接(三次握手),因此鏈接穩定可靠
  • 有客戶端、服務端的概念,客戶端發送,服務端接收
  • 傳輸完成後,會釋放鏈接(四次揮手)

UDP能夠類比於發短信,它具備如下特色

  • 數據傳輸前,不須要創建鏈接,因此不可靠,不穩定
  • 客戶端和服務端沒有明確界限,客戶端和服務端均可以進行收/發
  • 不須要創建鏈接,因此速度較快

四、TCP通訊代碼實踐

TCP網絡編程須要如下Java類

InetAddress:表示IP協議的地址,能夠解析IP地址和主機名

Socket:實現客戶端的套接字,創建鏈接,套接字就是兩臺機器間通信的端點

ServerSocket:實現服務端的套接字

4.1消息傳遞

客戶端

1.拿到服務端的地址及端口,InetAddress

2.創建Socket鏈接

3.發送消息

public class TcpClient {
    public static void main(String[] args) throws IOException {
        Socket socket=null;
        OutputStream os=null;
        try {
            //一、獲得服務端的地址,端口號
            InetAddress inetAddress=InetAddress.getByName("127.0.0.1");
            int port=9898;
            //二、創建Socket鏈接
          socket=new Socket(inetAddress,port);
            //三、發送消息
            os=socket.getOutputStream();
            os.write("hello,Simon".getBytes());
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //關閉資源
           os.close();
           socket.close();
        } 
    }
}
複製代碼

服務端

1.用ServerSocket設置本身的端口號

2.等待鏈接,Socket

2.1:能夠等待一次鏈接,接收到消息後關閉

2.2:等待屢次鏈接,用while(true)把等待鏈接的方法包裹起來,重複接收消息

三、獲取發送端的消息

public class TcpServer {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket=null;
        Socket socket=null;
        InputStream is=null;
        ByteArrayOutputStream baos=null;
        try {
            //一、設置本身的端口號
            serverSocket=new ServerSocket(9898);
            //二、等待客戶端的鏈接
            socket=serverSocket.accept();
            //三、讀取客戶端的消息
            is=socket.getInputStream();
            baos=new ByteArrayOutputStream();
            byte[] buffer=new byte[1024];
            int len;
            while ((len=is.read(buffer))!=-1){
                baos.write(buffer,0,len);
            }
            System.out.print(baos.toString());

        }catch (Exception e){
            e.printStackTrace();
        }finally {
            baos.close();
            is.close();
            socket.close();
            serverSocket.close();
        }
    }
}

複製代碼

4.2文件上傳

客戶端

1.建立一個鏈接

2.建立一個字節輸出流用於通訊

建立一個文件輸入流用於接收文件,而後將接收後的文件給字節輸出流用於通訊

3.輸出結束後,調用shutdownInput方法通知服務端已經發送完畢

4.關閉鏈接資源

public class TcpFileClient {
    public static void main(String[] args) throws Exception {

        //一、建立鏈接
        Socket socket = new Socket(InetAddress.getByName("127.0.0.1"), 9987);
        //二、定義一個輸出流用於通訊
        OutputStream os = socket.getOutputStream();
        FileInputStream fis = new FileInputStream(new File("simon.png"));
        byte[] buffer = new byte[1024];
        int len;
        while ((len = fis.read(buffer)) != -1) {
            os.write(buffer, 0, len);
        }
        //三、通知服務器,我已經結束了
        socket.shutdownOutput();

        //肯定服務器接收完畢,才能夠斷開鏈接,這一段代碼主要是接收服務端發出的結束信息
       InputStream inputStream= socket.getInputStream();
        byte[] buffer2=new byte[1024];
        ByteArrayOutputStream baos=new ByteArrayOutputStream();
        int len2;
        while ((len2=inputStream.read(buffer2))!=-1){
            baos.write(buffer2,0,len2);
        }
        System.out.print(baos.toString());
        //關閉鏈接
        baos.close();
        inputStream.close();
        os.close();
        fis.close();
        socket.close();

    }
}

複製代碼

服務端

1.建立一個鏈接用於等待客戶端的接入

2.建立一個輸入流

建立一個文件輸出流,將輸入流輸出

3.關閉資源

public class TcpFileServerDemo02 {
    public static void main(String[] args) throws Exception {
        //設置端口號
        ServerSocket serverSocket=new ServerSocket(9987);
        //等待鏈接
        Socket socket=serverSocket.accept();
        //獲取輸入流
        InputStream is=socket.getInputStream();
        //文件輸出
        FileOutputStream fos=new FileOutputStream(new File("snow.png"));
        byte[] buffer=new byte[1024];
        int len;
        while ((len=is.read(buffer))!=-1){
            fos.write(buffer,0,len);
        }
        //通知客戶端接收完畢
        OutputStream os=socket.getOutputStream();
        os.write("我已經接收結束,你能夠斷開了".getBytes());

        //關閉資源
        fos.close();
        is.close();
        socket.close();
        serverSocket.close();
    }
}

複製代碼

五、UDP通訊代碼

UDP不須要鏈接,可是須要知道對方的地址和端口號,主要用到了如下Java類

DatagramPacket:表示數據包,用於實現無鏈接分組傳送服務

DatagramSocket:表示用於發送和接收數據報的套接字

UDP是不存在客戶端和服務端的概念,可是爲了編程模擬方便,咱們假定存在客戶端和服務端

5.1 UDP實現消息發送

客戶端

  • 創建鏈接
  • 建立數據包
  • 發送數據包
public class UdpClientDemo1 {
    public static void main(String[] args) throws Exception {
        //一、創建鏈接(這個端口號是客戶端的)
        DatagramSocket datagramSocket=new DatagramSocket(9888);
        //二、創建數據包
        String msg="hello,Simon";
        InetAddress inetAddress=InetAddress.getByName("127.0.0.1");
        //數據,數據的起始位置,要發送的地址與端口號
        DatagramPacket datagramPacket=new DatagramPacket(msg.getBytes(),0,msg.getBytes().length,inetAddress,9887);
        //三、發送數據包
        datagramSocket.send(datagramPacket);
        //四、關閉數據流
        datagramSocket.close();

    }
}
複製代碼

服務端

  • 創建鏈接
  • 接收數據
public class UdpServerDemo2 {
    public static void main(String[] args) throws Exception{
        //創建鏈接,開放的端口
        DatagramSocket datagramSocket=new DatagramSocket(9887);
        //接收數據包
        byte[] buffer=new byte[1024];
        DatagramPacket datagramPacket=new DatagramPacket(buffer,0,buffer.length);
        datagramSocket.receive(datagramPacket);
        //打印數據包信息
        System.out.println(datagramPacket.getAddress());
        System.out.println(datagramPacket.getPort());
        System.out.println(new String(datagramPacket.getData()));
        //關閉鏈接
        datagramSocket.close();
    }
複製代碼

5.2 使用UDP循環發送和接收消息

使用while(true)方法將客戶端中的發送數據和接收數據包裹起來,只要當他們知足必定的條件時(如輸入的字符串爲"bye"),退出便可。這樣就能夠達到循環發送和接收消息。

客戶端

public class UdpSend {
    public static void main(String[] args) throws Exception {
        //一、創建鏈接
        DatagramSocket datagramSocket=new DatagramSocket(9888);
        //二、建立數據包,從鍵盤輸入
        InetAddress inetAddress=InetAddress.getByName("127.0.0.1");
        int port=9887;
        BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(System.in));
        while (true){
            String s=bufferedReader.readLine();
            DatagramPacket datagramPacket=new DatagramPacket(s.getBytes(),0,s.getBytes().length,inetAddress,9887);
            //三、發送數據
            datagramSocket.send(datagramPacket);
            if(s.equals("bye")){
                break;
            }
        }
        //四、關閉數據
        datagramSocket.close();
        }
}

複製代碼

服務端

public class UdpReceive{
    public static void main(String[] args) throws Exception {
        //一、創建鏈接
        DatagramSocket datagramSocket=new DatagramSocket(9887);
        while (true){
           //二、接收數據包
           byte[] buffer=new byte[1024];
            DatagramPacket datagramPacket=new DatagramPacket(buffer,0,buffer.length);
            datagramSocket.receive(datagramPacket);
            //三、斷開鏈接
            byte[] data=datagramPacket.getData();
            String receiveData=new String(data,0,data.length);
            System.out.println(receiveData);
            if(receiveData.equals("bye")){
                break;
            }
        }
        datagramSocket.close();
    }
}
複製代碼

5.3使用UDP實現聊天

使用UDP實現聊天,則客戶端和服務端(其實不存在客戶端和服務端的概念)既須要接收信息也須要發送消息,這就須要多線程的支持了。

咱們首先構造兩個接收類和發送的線程類,而後構造倆個用戶類進行通訊。

發送類

public class TalkSend implements Runnable {
    DatagramSocket datagramSocket = null;
    BufferedReader bufferedReader = null;

    private int fromPort;
    private int toPort;
    private String toIp;

    public TalkSend(int fromPort,int toPort,String toIp){
        this.fromPort=fromPort;
        this.toPort=toPort;
        this.toIp=toIp;

        //一、創建鏈接
        try {
            datagramSocket = new DatagramSocket(fromPort);
            //二、建立數據包,從鍵盤輸入
            bufferedReader = new BufferedReader(new InputStreamReader(System.in));
        } catch (SocketException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() {
        while (true) {
            try {
                String s = bufferedReader.readLine();
                DatagramPacket datagramPacket = new DatagramPacket(s.getBytes(), 0, s.getBytes().length, new InetSocketAddress(toIp,toPort));
                //三、發送數據
                datagramSocket.send(datagramPacket);
                if (s.equals("bye")) {
                    break;
                }
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
        //四、關閉數據
        datagramSocket.close();
    }
}
複製代碼

接收類

public class TalkReceive implements Runnable{
    DatagramSocket datagramSocket=null;
    private int port;
    private String msgFrom;

    public TalkReceive(int port,String msgFrom) {
        this.port=port;
        this.msgFrom=msgFrom;

        //一、創建鏈接
        try {
            datagramSocket=new DatagramSocket(port);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }



    @Override
    public void run() {
        while (true){
            try {
                //二、接收數據包
                byte[] buffer=new byte[1024];
                DatagramPacket datagramPacket=new DatagramPacket(buffer,0,buffer.length);
                datagramSocket.receive(datagramPacket);
                //三、斷開鏈接
                byte[] data=datagramPacket.getData();
                String receiveData=new String(data,0,data.length);
                System.out.println(msgFrom+": "+receiveData);
                if(receiveData.equals("bye")){
                    break;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        datagramSocket.close();
    }
}
複製代碼

用戶類1

public class TalkStudent {
    public static void main(String[] args) {
        new Thread(new TalkSend(7777,9999,"localhost")).start();
        new Thread(new TalkReceive(8888,"小包")).start();
    }
}
複製代碼

用戶類2

public class TalkTeacher {
    public static void main(String[] args){
        new Thread(new TalkSend(5555,8888,"localhost")).start();
        new Thread(new TalkReceive(9999,"小郎")).start();
    }
}
複製代碼
相關文章
相關標籤/搜索