socket(套接字)編程--java實戰------不得不會的socket編程套路(附有聊天室實現思路)

1、socket是啥

套接字(socket)是一個抽象層,應用程序可以通過它發送或接收數據,可對其進行像對文件一樣的打開、讀寫和關閉等操作。套接字允許應用程序將I/O插入到網絡中,並與網絡中的其他應用程序進行通信。網絡套接字是IP地址與端口的組合。–這波百度百科怎麼說
來點直觀的吧,不然不會大夥還是不會!!
win+r ->輸入:cmd ->在命令臺輸入:netstat -ano 就可以看到你當前電腦的所有套接字了哦,也就是socket == ip號+端口號!!!
在這裏插入圖片描述
下面這個socket建立雙向連接之後,雙方再進行通信的一個簡單概念模型。
在這裏插入圖片描述

2、爲什麼會socket這個東西出現呢?

  • 爲了計算機可以創建更多的對外連接。
    你想啊,你的電腦有一個自己的ip何端口,先不管這個ip是局域網內還是公網,你要想建立對外的連接,你只有一個ip地址,難道你想只能對外建立一個連接嗎?當然不行啦,所有,我們將ip+端口構成的套接字,作爲一個計算機對外連接的一個標記或是說通道。

3、socket這麼厲害,那我們怎麼使用它呢?

下面就是java代碼時間,我使用TCP的方式建立socket連接。
先來來個簡單的demo,這種模式只能一發一收,因爲每次輸入的地方會有阻塞,沒啥好講的,看註釋。

3.1、simpleDemo

3.1.1、Server

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

public class Server {
    public static void main(String args[]) throws IOException {
        ServerSocket serverSocket = null;

        try {
            //監聽自己的端口
            serverSocket = new ServerSocket(6666);
        } catch (IOException e) {
            e.printStackTrace();
        }
        Socket socket = null;

        try {
            socket = serverSocket.accept();
        } catch (IOException e) {
            e.printStackTrace();
        }
        //開啓本地的socket輸入字節流
        BufferedReader socketInput = new BufferedReader(new InputStreamReader(System.in));
        //開啓本地的socket輸入的字節流的傳送通道
        PrintWriter socketInputSend = new PrintWriter(socket.getOutputStream());
        //開啓客戶端socket的字節流輸入通道
        BufferedReader socketGetother = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        //輸入本地的socket需發送的數據
        String readline = socketInput.readLine();

        while(!readline.equals("")){

            socketInputSend.println(readline);
            //刷新輸出流,馬上發出自己的該字符串
            socketInputSend.flush();

            System.out.println("Server:"+readline);
            //獲取客戶端socket的輸入數據,同樣會阻塞
            System.out.println("Client2:"+socketGetother.readLine());
            //輸入阻塞
            readline = socketInput.readLine();
        }
        socketInput.close();
        socketGetother.close();
        socket.close();
    }

}

3.1.2、Client

package com.icitic.mc.Task03.socketChat.simpleDemo;

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

public class Client {


    public static void main(String[] args) throws IOException {
        Socket socket = new Socket("127.0.0.1", 6666);

        //開啓本地的socket輸入字節流
        BufferedReader socketInput = new BufferedReader(new InputStreamReader(System.in));
        //開啓本地的socket輸入的字節流的傳送通道
        PrintWriter socketInputSend = new PrintWriter(socket.getOutputStream());
        //開啓服務器socket的字節流輸入通道
        BufferedReader socketGetother = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        //輸入本地的socket需發送的數據
        String readline = socketInput.readLine();

        while(!readline.equals("")){

            socketInputSend.println(readline);
            //刷新輸出流,馬上發出自己的該字符串
            socketInputSend.flush();

            System.out.println("Client2:"+readline);
            //獲取服務器socket的輸入數據,同樣會阻塞
            System.out.println("Server:"+socketGetother.readLine());
            //輸入阻塞
            readline = socketInput.readLine();
        }
        socketInput.close();
        socketGetother.close();
        socket.close();

    }
}

3.2、ClientToServer

掌握了簡單socket連接之後,這裏我們將客戶端和服務端程序都分兩個線程來執行,即監聽socket連接的另一端使用一個線程,自己輸入一個線程。這樣雙線程雙管齊下就可以實現無縫對話了,不必說不出口了。

3.2.1、Server


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

/**
 * 雙線程-監聽-發送
 */
public class Server{

    public static void main(String args[]) throws IOException {

        //服務器監聽 自身的 端口
        Socket socket = bindSocket();
        //開啓本地的socket輸入字節流
        BufferedReader socketInput = new BufferedReader(new InputStreamReader(System.in));
        //開啓本地的socket輸入的字節流的傳送通道
        PrintWriter socketInputSend = new PrintWriter(socket.getOutputStream());
        //開啓客戶端socket的字節流輸入通道
        BufferedReader socketGetother = new BufferedReader(new InputStreamReader(socket.getInputStream()));
         //開線程來監聽客戶端消息
        getMessage(socketGetother);
        //輸入本地的socket需發送的數據,會阻塞
        String readline = socketInput.readLine();
        while(!readline.equals("Bye")){
            //發送出本地消息
            sendMessage(socketInputSend,readline,socketInput);
            //輸入阻塞
            readline = socketInput.readLine();
        }
        socketClose(socketInput,socketGetother,socket);
    }

    /**
     * 內部自開線程監聽服務器端消息
     * @param socketGetother
     */
    private static void getMessage(final BufferedReader socketGetother) {
        new Thread(new Runnable() {
            public void run() {
                //獲取客戶端socket的輸入數據,readLine()同樣會阻塞
                try {
                    String readline = socketGetother.readLine();
                    while(!readline.equals("Bye")){
                        System.err.println("*************************Client2:"+readline);
                        readline = socketGetother.readLine();
                    }
                    System.out.println("------------------------聊天已結束-------------------------");
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    /**
     * 本地發送消息
     * @param socketInputSend
     * @param readline
     * @param socketInput
     * @throws IOException
     */
    private static void sendMessage(PrintWriter socketInputSend, String readline, BufferedReader socketInput) throws IOException {
        //將本地輸入的字節流放入通道
        socketInputSend.println(readline);
        //刷新輸出流,馬上發出自己的該字符串
        socketInputSend.flush();
        System.out.println("*************************myself:"+readline);

    }

    /**
     * 關閉輸入輸出和socket
     * @param socketInput
     * @param socketGetother
     * @param socket
     * @throws IOException
     */
    private static void socketClose(BufferedReader socketInput, BufferedReader socketGetother, Socket socket) throws IOException {
            socketInput.close();
            socketGetother.close();
            socket.close();
    }

    /**
     * 開啓服務器的監聽socket
     * @return
     */
    private static Socket bindSocket() {
        ServerSocket serverSocket = null;
        Socket socket = null;
        try {
            serverSocket = new ServerSocket(6666);
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            socket = serverSocket.accept();
        } catch (IOException e) {
            e.printStackTrace();
        }
        System.out.println(socket);
        return socket;

    }
}

3.2.2、Client

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

/**
 * 雙線程-監聽-發送
 */
public class Client {
    final static String LOCALHOSt = "127.0.0.1";
    final static String VM        = "192.168.154.3";
    public static void main(String[] args) throws IOException {
            Socket socket = new Socket(LOCALHOSt, 6666);
        System.out.println(socket);
            //開啓本地的socket輸入字節流
            BufferedReader socketInput = new BufferedReader(new InputStreamReader(System.in));
            //開啓本地的socket輸入的字節流的傳送通道
            PrintWriter socketInputSend = new PrintWriter(socket.getOutputStream());
            //開啓服務器socket的字節流輸入通道
            BufferedReader socketGetother = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            //開線程來監聽服務器端消息
            getMessage(socketGetother);
            //輸入本地的socket需發送的數據,readLine()會阻塞
            String readline = socketInput.readLine();
            while(!readline.equals("Bye")){
                //發送出本地消息
                sendMessage(socketInputSend,readline,socketInput);
                //輸入阻塞
                readline = socketInput.readLine();
            }
        socketClose(socketInput,socketGetother,socket);
        }
    /**
     * 內部自開線程監聽服務器端消息
     * @param socketGetother
     */
    private static void getMessage(final BufferedReader socketGetother) {
        new Thread(new Runnable() {
            public void run() {
                //獲取服務器socket的輸入數據,readLine()同樣會阻塞
                try {
                    String readline = socketGetother.readLine();
                    while(!readline.equals("Bye")){
                        System.err.println("*************************Server:"+readline);
                        readline = socketGetother.readLine();
                    }
                    System.out.println("------------------------聊天已結束-------------------------");
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    /**
     * 本地發送消息
     * @param socketInputSend
     * @param readline
     * @param socketInput
     * @throws IOException
     */
    private static void sendMessage(PrintWriter socketInputSend, String readline, BufferedReader socketInput) throws IOException {
        //將本地輸入的字節流放入通道
        socketInputSend.println(readline);
        //刷新輸出流,馬上發出自己的該字符串
        socketInputSend.flush();
        System.out.println("*************************myself:"+readline);
    }

    /**
     * 關閉輸入輸出和socket
     * @param socketInput
     * @param socketGetother
     * @param socket
     * @throws IOException
     */
    private static void socketClose(BufferedReader socketInput, BufferedReader socketGetother, Socket socket) throws IOException {
        socketInput.close();
        socketGetother.close();
        socket.close();
    }
}

3.2.3、運行結果

通過結果可以看到,對其加上多線程後,雙方都可以多發多收,家家戶戶都通網了,普天同慶。
在這裏插入圖片描述
在這裏插入圖片描述

3.3、chatRoom

這裏我得說明一下,博主沒那麼多時間使用socket實現聊天室。。我就改了下3.2.1的Server,可以實現多個Client連接,Server可以同時接收所有Client的所有消息,但是Server沒有推送出它接收的所有消息給所有Client,Server只能自己輸入並且一次只能推送給一個Client。多個Client,Server大概使用輪詢的方式實現推送給各個Client,(其實也不是輪詢,是線程競爭的結果)
在這裏插入圖片描述
我就掛出以惡個Server,Client程序和3.2.2是一樣,複製多個就行。

3.3.1、Server

package com.icitic.mc.Task03.socketChat.chatRoom;

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

/**
 * 雙線程-監聽-發送
 */
public class Server{
    static ServerSocket serverSocket = null;
    public static void main(String args[]) throws IOException {
        try {
            serverSocket = new ServerSocket(6666);
        } catch (IOException e) {
            e.printStackTrace();
        }
        for (int i = 0 ; i < 3; i++){
            new Thread(new Runnable() {
                public void run() {
                    try {
                        chatRoom();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }

    }
    /**
     * socket服務器主要邏輯代碼
     * @throws IOException
     */
    private static void chatRoom() throws IOException {
        //服務器監聽 自身的 端口
        Socket socket = bindSocket();
        //開啓本地的socket輸入字節流
        BufferedReader socketInput = new BufferedReader(new InputStreamReader(System.in));
        //開啓本地的socket輸入的字節流的傳送通道
        PrintWriter socketInputSend = new PrintWriter(socket.getOutputStream());
        //開啓客戶端socket的字節流輸入通道
        BufferedReader socketGetother = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        //開線程來監聽客戶端消息
        getMessage(socketGetother);
        //輸入本地的socket需發送的數據,會阻塞
        String readline = socketInput.readLine();
        while(!readline.equals("Bye")){
            //發送出本地消息
            sendMessage(socketInputSend,readline,socketInput);
            //輸入阻塞
            readline = socketInput.readLine();
        }
        socketClose(socketInput,socketGetother,socket);
    }

    /**
     * 內部自開線程監聽服務器端消息
     * @param socketGetother
     */
    private static void getMessage(final BufferedReader socketGetother) {
        new Thread(new Runnable() {
            public void run() {
                //獲取客戶端socket的輸入數據,readLine()同樣會阻塞
                try {
                    String readline = socketGetother.readLine();
                    while(!readline.equals("Bye")){
                        System.err.println("*************************Client2:"+readline);
                        readline = socketGetother.readLine();
                    }
                    System.out.println("------------------------聊天已結束-------------------------");
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    /**
     * 本地發送消息
     * @param socketInputSend
     * @param readline
     * @param socketInput
     * @throws IOException
     */
    private static void sendMessage(PrintWriter socketInputSend, String readline, BufferedReader socketInput) throws IOException {
        //將本地輸入的字節流放入通道
        socketInputSend.println(readline);
        //刷新輸出流,馬上發出自己的該字符串
        socketInputSend.flush();
        System.out.println("*************************myself:"+readline);

    }

    /**
     * 關閉輸入輸出和socket
     * @param socketInput
     * @param socketGetother
     * @param socket
     * @throws IOException
     */
    private static void socketClose(BufferedReader socketInput, BufferedReader socketGetother, Socket socket) throws IOException {
            socketInput.close();
            socketGetother.close();
            socket.close();
    }

    /**
     * 開啓服務器的監聽socket
     * @return
     */
    private static Socket bindSocket() {
        Socket socket = null;
        try {
            socket = serverSocket.accept();
        } catch (IOException e) {
            e.printStackTrace();
        }
        System.out.println(socket);
        return socket;

    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章