JAVASE之socket編程__java那條路

來源:互聯網
上載者:User
TCP通訊
server:package day07;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.net.ServerSocket;import java.net.Socket;/** * 服務端應用程式 * @author Administrator * */public class Server {    //運行在服務端的Socket    private ServerSocket server;    /**     * 構造方法,用於初始化服務端     * @throws IOException      */    public Server() throws IOException{        try {            /*             * 建立ServerSocket時需要指定服務端連接埠             */            System.out.println("初始化服務端");            server = new ServerSocket(8088);            System.out.println("服務端初始化完畢");        } catch (IOException e) {            // TODO Auto-generated catch block            e.printStackTrace();            //沒有建立成功。拋出異常            throw e;        }    }    /**     * 服務端開始工作的方法     */    public void start(){        try{            /*             * ServerSocket的accept方法             * 用於監聽8088連接埠,等待用戶端的連結             * 該方法是一個阻塞方法,直到一個用戶端連結,否則該方法一直阻塞。             * 若一個用戶端連結了,會返回該用戶端的Socket             *              */            System.out.println("等待用戶端串連......");            Socket socket=server.accept();            System.out.println("用戶端串連了");            //接收來自用戶端的訊息            //InputStream輸入資料流           InputStream is = socket.getInputStream();            //變成字元流。。。           InputStreamReader isr = new InputStreamReader(is);           //將字元流轉換為緩衝流輸入資料流,這樣就可以按行為單位讀取字串了           BufferedReader br = new BufferedReader(isr);           String message;           while((message=br.readLine())!=null){           System.out.println("用戶端:"+"IP:"+socket.getInetAddress()+":"+socket.getPort()+"::"+message);           }        //若readLine返回null就表示無法再讀取到資訊          //windows:當用戶端與服務端中斷連線後readLine()方法會拋出異常        }catch(Exception e){            e.printStackTrace();        }    }    public static void main(String[] args){        Server server = null;        try {            server = new Server();        } catch (IOException e) {            // TODO Auto-generated catch block            e.printStackTrace();            System.out.println("服務端初始化失敗。");        }        server.start();    }}
client:package day07;import java.io.IOException;import java.io.OutputStream;import java.io.OutputStreamWriter;import java.io.PrintWriter;import java.net.Socket;import java.net.UnknownHostException;import java.util.Scanner;public class Client {//Socket,用於串連服務端的ServerSocket    private Socket socket;    //構造方法,初始化client    public Client() {        try {            /*             * 建立Socket對象時,就會嘗試根據給定的地址與連接埠串連伺服器。所以,             * 若該對象建立成功,說明與服務端串連正常             */            socket = new Socket("127.0.0.1",8088);            System.out.println("成功串連服務端");        } catch (UnknownHostException e) {            //連接埠號碼異常            e.printStackTrace();        } catch (IOException e) {            System.out.println("用戶端建立失敗");            e.printStackTrace();        }    }    //用戶端啟動方法    public void start(){        try{            /*             * 可以通過Socket的getOutputStream()             * 方法擷取一條輸出資料流,使用者與資訊發送至服務端             * 是一個位元組流,             */        OutputStream out = socket.getOutputStream();            //轉換成字元流,寫更容易些。        OutputStreamWriter osw = new OutputStreamWriter(out);        //在封裝上緩衝流  以行為單位寫字串        PrintWriter pw  = new PrintWriter(osw);        while(true){        Scanner scanner = new Scanner(System.in);         pw.println(scanner.next());        //不寫的話就不會顯示,因為是緩衝流。並沒有輸出。自己flush一下。        pw.flush();        }        }catch(Exception e){            e.printStackTrace();        }    }    public static void main(String[] args) {        Client client = new Client();        client.start();    }}
小結:

建立一個類,然後定義個socket,作為server和client,開始只是定義了是Socket類型,並沒有初始化。實現建構函式,server的建構函式寫的是連接埠,server = new ServerSocket();ip就是運行在哪個上面就是哪個了。client是傳入的伺服器的ip和連接埠 socket = new Socket(ip,連接埠)

然後都要建立個start函數用作sockt串連後的功能。
server要等待串連; accept 調用accpet是在start中的。

Socket socket=server.accept();

建立的socket是 串連用戶端的 socket

然後在主函數中建立個 server
server = new Server();調用 start函數
然後是在start中寫如何傳輸字元。socket有個getOutputStream方法,但是返回的是位元組流,那麼就要轉成字元流 就是 OutputStreamWriter ,然後在加個緩衝流,一行一行的讀寫操作 PrintWriter
server: getInputStream InputStreamReader BufferedReader

clent: getOutputStream outputStreamWriter
PrintWriter

讀取操作,若readLine返回的是null那麼就是沒有資訊在讀了。
要注意 flush() 多線程服務端

package day07;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.net.ServerSocket;import java.net.Socket;/** * java多線程socket * @author Administrator * */public class ThreadServer2 {    private ServerSocket server;        public ThreadServer2(){            try {                server = new ServerSocket(8088);            } catch (IOException e) {                // TODO Auto-generated catch block                e.printStackTrace();            }        }        public void start(){            while(true){                System.out.println("伺服器等待串連.....");            try {                Socket socket = server.accept();                //調用線程,傳入socket                hthread ht1 = new hthread(socket);                 ht1.start();            } catch (IOException e) {                // TODO Auto-generated catch block                e.printStackTrace();            }//catch            }//while        }//start    public static void main(String[] args) {        //直接建立啟動伺服器        new ThreadServer2().start();    }    //線程類,在start中調用線程,並傳入socket    class hthread extends Thread {        private Socket socket;        public hthread (Socket socket){            this.socket = socket;            System.out.println("用戶端串連成功。");        }        public void run(){            try {                //接收資料                InputStream is = socket.getInputStream();                InputStreamReader isr = new InputStreamReader(is);                BufferedReader br = new BufferedReader(isr);                String message;                while((message = br.readLine())!=null){                    System.out.println("用戶端:IP:"+socket.getInetAddress()+"  PORT"+socket.getPort()+": "+message);                }//while            } catch (IOException e) {                // TODO Auto-generated catch block                e.printStackTrace();            }finally{try {                socket.close();            } catch (IOException e) {                // TODO Auto-generated catch block                e.printStackTrace();            }}        }    }}
用Runnable,然後用線程池:
package day07;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.net.ServerSocket;import java.net.Socket;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import day07.ThreadServer2.hthread;public class ThreadciServer {    private ServerSocket server;    private ExecutorService threadPool;    public ThreadciServer(){        try {            server = new ServerSocket(8088);            //建立線程池,最多運行50個    threadPool =Executors.newFixedThreadPool(2);        } catch (IOException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }    }    public void start(){        while(true){            System.out.println("伺服器等待串連.....");        try {            Socket socket = server.accept();            //調用線程,傳入socket            Runnable ht1 = new hthread(socket);             threadPool.execute(ht1);        } catch (IOException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }//catch        }//while    }//startpublic static void main(String[] args) {    //直接建立啟動伺服器    new ThreadServer2().start();}//線程類,在start中調用線程,並傳入socketclass hthread implements Runnable {    private Socket socket;    public hthread (Socket socket){        this.socket = socket;        System.out.println("用戶端串連成功。");    }    public void run(){        try {            //接收資料            InputStream is = socket.getInputStream();            InputStreamReader isr = new InputStreamReader(is);            BufferedReader br = new BufferedReader(isr);            String message;            while((message = br.readLine())!=null){                System.out.println("用戶端:IP:"+socket.getInetAddress()+"  PORT"+socket.getPort()+": "+message);            }//while        } catch (IOException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }finally{try {            socket.close();        } catch (IOException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }}        System.out.println("一個用戶端下線了....");    }}}
又小結:

返回的,server的直接就定義輸出資料流,然後在run中原來是把資訊輸出在顯示屏,直接就pw.println(); 發送過去。
然後在client就複雜些。不是只用戶端發,然後伺服器收,在返回給用戶端,要無論用戶端是否發,有資訊就發給用戶端,那麼就要定義個輸入資料流。但是單獨給他建立個線程,收和發不是同步的。線程就是和伺服器的接收一樣的。在start方法中調用建立線程。

server:package day07;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.io.OutputStream;import java.io.OutputStreamWriter;import java.io.PrintWriter;import java.net.ServerSocket;import java.net.Socket;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import day07.ThreadciServer.hthread;public class Server_fanui {    private ServerSocket server;    private ExecutorService threadPool;    public Server_fanui(){        try {            server = new ServerSocket(8088);            //建立線程池,最多運行50個    threadPool =Executors.newFixedThreadPool(50);        } catch (IOException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }    }    public void start(){        while(true){            System.out.println("伺服器等待串連.....");        try {            Socket socket = server.accept();            //調用線程,傳入socket            Runnable ht1 = new hthread(socket);             threadPool.execute(ht1);        } catch (IOException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }//catch        }//while    }//startpublic static void main(String[] args) {    //直接建立啟動伺服器    new ThreadServer2().start();}//線程類,在start中調用線程,並傳入socketclass hthread implements Runnable {    private Socket socket;    public hthread (Socket socket){        this.socket = socket;        System.out.println("用戶端串連成功。");    }    public void run(){        try {            OutputStream os = socket.getOutputStream();            OutputStreamWriter osr = new OutputStreamWriter(os);            PrintWriter pw = new PrintWriter(osr,true);            //接收資料            InputStream is = socket.getInputStream();            InputStreamReader isr = new InputStreamReader(is);            BufferedReader br = new BufferedReader(isr);            String message;            while((message = br.readLine())!=null){            //  System.out.println("用戶端:IP:"+socket.getInetAddress()+"  PORT"+socket.getPort()+": "+message);                pw.println(message);            }//while        } catch (IOException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }finally{try {            socket.close();        } catch (IOException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }}        System.out.println("一個用戶端下線了....");    }}}
client:package day07;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.io.OutputStream;import java.io.OutputStreamWriter;import java.io.PrintWriter;import java.net.Socket;import java.net.UnknownHostException;import java.util.Scanner;import day07.Client.Getserver;public class Client_fanhui {    //Socket,用於串連服務端的ServerSocket        private Socket socket;        //構造方法,初始化client        public Client_fanhui() {            try {                /*                 * 建立Socket對象時,就會嘗試根據給定的地址與連接埠串連伺服器。所以,                 * 若該對象建立成功,說明與服務端串連正常                 */                socket = new Socket("127.0.0.1",8088);                System.out.println("成功串連服務端");            } catch (UnknownHostException e) {                //連接埠號碼異常                e.printStackTrace();            } catch (IOException e) {                System.out.println("用戶端建立失敗");                e.printStackTrace();            }        }        //用戶端啟動方法        public void start(){            try{                Runnable rserver = new Getserver();                Thread tserver = new Thread(rserver);                tserver.start();                /*                 * 可以通過Socket的getOutputStream()                 * 方法擷取一條輸出資料流,使用者與資訊發送至服務端                 * 是一個位元組流,                 */            OutputStream out = socket.getOutputStream();                //轉換成字元流,寫更容易些。            OutputStreamWriter osw = new OutputStreamWriter(out);            //在封裝上緩衝流  以行為單位寫字串            PrintWriter pw  = new PrintWriter(osw,true);            while(true){            Scanner scanner = new Scanner(System.in);             pw.println(scanner.next());            //不寫的話就不會顯示,因為是緩衝流。並沒有輸出。自己flush一下。            }            }catch(Exception e){                e.printStackTrace();            }        }        public static void main(String[] args) {            Client client = new Client();            client.start();        }        //建立個線程,一個是原來的向伺服器發資料,這個是向伺服器接收資料。        class Getserver implements Runnable{            @Override            public void run() {                try{                    System.out.println("6666");                InputStream is = socket.getInputStream();                InputStreamReader isr = new InputStreamReader(is);                BufferedReader br = new BufferedReader(isr);                String message=null;                while((message=br.readLine())!=null){                    System.out.println("伺服器說:"+message);                }                }catch(Exception e){                    e.printStackTrace();                }            }//run        }//class_getserver    }
若建立個多人同時線上聊天的方法

把別的client即在伺服器是不同的線程的如何發給別的建立一個公用的list,來儲存所有用戶端輸出資料流。list中放的是流。所以定義的時候 List,在建構函式中初始化。

將該用戶端的輸出存在在共用集合,以便使得該用戶端也能接受伺服器轉寄的訊息

append(pw)
集合中存的是流。。。不是流裡面的字串。。。。。一個client對應一個list中的流。。。。
如果client斷開了就要把他在list中對應的流也刪除。remove(pw)

統計當前線上人數。。。list的size就是

將一個用戶端發來得訊息,變數list 中所有流,都發過去。實現群聊

但是會有多線程同時操作 共用集合list 。。。 安全執行緒問題

遍曆這個集合,是迭代器,不是安全執行緒的,遍曆與add和remove不是同步的。
要保證遍曆的時候也是同步的,三個都是同步的。

要是互斥的。那就同步鎖 也叫互斥鎖。

我們調用靜態方法的時候,不需要先產生一個執行個體,可以通過類名直接調用。

靜態方法和靜態屬性為所有執行個體共用

因為多個線程操作一個list會造成線程不安全的問題(增加和刪除,遍曆沒事)
用synchronized
若兩個線程看到的是一個synchronized 是同步鎖,不能同時訪問

但是這種就是互斥,不同的地方鎖住同一個對象,就是互斥鎖了

可以把輸出資料流放入map中,給每個輸出資料流定義個名稱,
然後可以根據@暱稱,然後去map尋找就去單獨輸出,否則就是全部輸出 Udp通訊

不可靠的傳輸協議
TCP串連上用的流。連續輸出互相寫
但是UDP不是保持長串連,發完就不管了

不需要等待對方,消耗少
主要學習的就是打包和拆包 ,打包發送過去,接收到後拆包
DatagramPacket
構建接收包

構造發送包

服務端接收
DatagramSocket

執行個體化的時候給一個連接埠號碼,還是服務端的連接埠要固定

receive接收資料

用戶端發送
兩端都是DatagramSocket

server:package day07;import java.net.DatagramPacket;import java.net.DatagramSocket;/** * UDP服務端 * @author Administrator * */public class UdpServer {    public  void start(){        try{            /*             * 接收包的步驟             * 建立socket             * 建立一個合適大小的包             * 通過socket接收資料到包中             * 拆包取資料             */            //服務端建立socket的時候要傳參數連接埠            DatagramSocket server            = new DatagramSocket(8088);

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.