1. 程式人生 > >Java多執行緒與網路程式設計綜合使用

Java多執行緒與網路程式設計綜合使用

最近重新看多執行緒與網路程式設計這一塊的知識,好久沒碰這一塊了,都忘得差不多了,這裡將這兩個模組的知識串接一下。同時處理多執行緒與網路程式設計最為經典的例子莫過於聊天室,那我就聊天室案例作為一個回顧。
首先,我們來看以下程式碼:

package MultiTCP;

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

/**
 * 必須先啟動再連線
 * 1、建立伺服器 指定埠 ServerSocket(int port)
 * 2、接收客戶端的連線  阻塞式
 * 3、傳送資料+接收資料
 * 
 * 接收多個客戶端
 */
@SuppressWarnings("all") public class MultiServer { public static void main(String[] args) throws IOException { //1、建立伺服器 指定埠 ServerSocket server = new ServerSocket(8888); while(true)//死迴圈 一個accept 一個客戶端 { //2、接收客戶端的連線 Socket socket = server.accept(); System.out.println("一個客戶端建立連線"
); //2、傳送資料 String msg = "歡迎使用"; //3、輸出流 /*BufferedWriter bw = new BufferedWriter( new OutputStreamWriter( socket.getOutputStream())); bw.write(msg); bw.newLine();//一定要加行結束符,不然讀不到資料 bw.flush();*/
DataOutputStream dos = new DataOutputStream(socket.getOutputStream()); dos.writeUTF(msg); dos.flush(); } } }
package MultiTCP;

import java.io.DataInputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
/**
 * 1、建立客戶端 必須指定伺服器+埠  此時就在連線
 * Socket(String host,int port)
 * 2、接收資料+傳送資料
 */
@SuppressWarnings("all")
public class Client {
    public static void main(String[] args) throws UnknownHostException, IOException {
        //1、建立客戶端 必須指定伺服器+埠  此時就在連線
        Socket client = new Socket("localhost",8888);
        //2、接收資料
        /*BufferedReader br = new BufferedReader(new InputStreamReader(client.getInputStream()));
        String echo = br.readLine();//阻塞式方法
        System.out.println(echo);*/
        DataInputStream dis = new DataInputStream(client.getInputStream());
        String echo = dis.readUTF();
        System.out.println(echo);
    }
}

以上程式碼存在的問題
伺服器為一個客戶端傳送資料完畢才能連線下一個客戶端。
因此,為了解決上述的問題,我們需要為伺服器端建立多執行緒操作。

首先我們需要為聊天室添加發送資料和接收資料。

package CSNet;

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

/**
 * 建立伺服器
 */
@SuppressWarnings("all")
public class Server {
    public static void main(String[] args) throws IOException {
        ServerSocket server = new ServerSocket(9999);
        Socket client = server.accept();

        //寫出資料
        //輸入流
        DataInputStream dis = new DataInputStream(client.getInputStream());
        String msg = dis.readUTF();
        System.out.println("伺服器收到"+msg);

        //輸出流
        DataOutputStream dos = new DataOutputStream(client.getOutputStream());
        dos.writeUTF("伺服器傳送給客戶端"+msg);
        dos.flush();                
    }
}
package CSNet;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.net.UnknownHostException;

/**
 * 建立客戶端:傳送資料+接收資料
 * 寫出資料:輸出流
 * 讀取資料:輸入流
 * 輸入流與輸出流在同一個執行緒內,因此我們應該讓 輸入流與輸出流彼此獨立
 */
@SuppressWarnings("all")
public class Client {
    public static void main(String[] args) throws UnknownHostException, IOException {
        Socket client = new Socket("localhost",9999);
        //控制檯的輸入流
        BufferedReader console = new BufferedReader(new InputStreamReader(System.in));
        DataOutputStream dos = new DataOutputStream(client.getOutputStream());
        DataInputStream dis = new DataInputStream(client.getInputStream());

        while(true)
        {
            String info = console.readLine();
            //輸出流
            dos.writeUTF(info);
            dos.flush();

            //輸入流
            String msg = dis.readUTF();
            System.out.println(msg);
        }

    }
}

以上的程式碼存在輸入流與輸出流在同一個執行緒內問題,因此我們應該讓輸入流與輸出流彼此獨立。

接下來我們是需要實現多執行緒,讓輸入流與輸出流分離。對客戶端實現多執行緒。

客戶端傳送資料

package ThreadNet;

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

/**
 * 傳送資料執行緒:用於傳送資料
 */
public class Send implements Runnable{
    //控制檯的輸入流
    private BufferedReader console;
    //管道的資料輸出流
    private DataOutputStream dos;
    //控制執行緒標識
    private boolean isRunning = true;

    //初始化
    public Send() {
        console = new BufferedReader(new InputStreamReader(System.in)); 
    }

    public Send(Socket client)
    {
        this();
        try {
            dos = new DataOutputStream(client.getOutputStream());
        } catch (IOException e) {
        }
    }

    //1、從控制檯接收資料
    private String getMsgFromConsole()
    {
        try {
            return console.readLine();
        } catch (IOException e) {
        }
        return "";
    }

    /**
     * 1、從控制檯接收資料
     * 2、傳送資料
     */
    public void send()
    {
        String msg = getMsgFromConsole();
        try {
            if(null!=msg&& !msg.equals(""))
            {
                dos.writeUTF(msg);
                dos.flush();//強制重新整理
            }
        } catch (IOException e) {
             isRunning = false;//傳送失敗,提示關閉執行緒
             CloseUtil.closeAll(dos,console);//如果不能傳送成功,直接關閉流。
        }
    }

    @Override
    public void run() {
        //執行緒體
        while(isRunning)
        {
            send();
        }
    }
}

客戶端接收資料

package ThreadNet;

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

/**
 * 接收執行緒:用於接收資料
 */
public class Receive implements Runnable{
    //管道的資料輸入流
    private DataInputStream dis ;
    //執行緒標識
    private boolean isRunning = true;

    public Receive()    {   
    }   
    public Receive(Socket client) {
        try {
            dis = new DataInputStream(client.getInputStream());
        } catch (IOException e) {
            isRunning = false;
            CloseUtil.closeAll(dis);
        }
    }

    //接收資料的方法
    public String receive()
    {
        String msg = "";
        try {
            msg = dis.readUTF();
        } catch (IOException e) {
            isRunning = false;
            CloseUtil.closeAll(dis);
        }
        return msg;
    }

    @Override
    public void run() {
        //執行緒體
        while(isRunning){
            System.out.println(receive());
        }   
    }
}

客戶端

package ThreadNet;

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

/**
 * 建立客戶端:傳送資料+接收資料
 * 寫出資料:輸出流
 * 讀取資料:輸入流
 * 輸入流與輸出流在同一個執行緒內 應該獨立出來
 */
public class Client {
    public static void main(String[] args) throws UnknownHostException, IOException {
        Socket client = new Socket("localhost",9999);
        new Thread(new Send(client)).start();//一條路徑
        new Thread(new Receive(client)).start();//一條路徑  
    }
}

伺服器

package ThreadNet;

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

/**
 * 建立伺服器
 */
public class Server {
    public static void main(String[] args) throws IOException {
        ServerSocket server = new ServerSocket(9999);
        while(true){
            Socket client = server.accept();
            //寫出資料
            //輸入流
            DataInputStream dis = new DataInputStream(client.getInputStream());
            //輸出流
            DataOutputStream dos = new DataOutputStream(client.getOutputStream());
            while(true)
            {       
                String msg = dis.readUTF();
                //System.out.println(msg);      
                dos.writeUTF("伺服器收到資料並返回"+msg);
                dos.flush();
            }
        }
    }
}

關閉流

package ThreadNet;

import java.io.Closeable;
import java.io.IOException;

/**
 * 關閉流的方法
 */
public class CloseUtil {
    public static void closeAll(Closeable ... io)
    {
        for(Closeable temp:io)
        {
            if(null==temp)
            {
                try {
                    temp.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

這個方仍然存在問題,伺服器端只能夠一個一個的接收,必須要等到上一條執行完,才能進入下一條,存在所謂的先後順序,並不具備多執行緒的功能。因此我們也需要對伺服器進行多執行緒。

伺服器

package MultiServer;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;

/**
 * 建立伺服器
 */
public class Server {
    private List<MyChannel> all = new ArrayList<>();

    public static void main(String[] args) throws IOException  {    
        new Server().start();

    }

    public void start() throws IOException
    {   
        ServerSocket server = new ServerSocket(9999);   
        while(true)
        {
            Socket client = server.accept();
            MyChannel channel = new MyChannel(client);
            all.add(channel);//統一管理
            new Thread(channel).start();//一條道路
        }
    }

    /**
     * 內部類
     * 一個客戶端 一條道路
     * 1、輸入流
     * 2、輸出流
     * 3、接收資料
     * 4、傳送資料
     * @author Administrator
     *
     */
    class MyChannel implements Runnable
    {
        private DataInputStream dis;
        private DataOutputStream dos;
        private boolean isRunning = true;
        private String name;

        public MyChannel() {    
        }
        //初始化
        public MyChannel(Socket client)
        {
            try {
                dis = new DataInputStream(client.getInputStream());
                dos = new DataOutputStream(client.getOutputStream());

                this.name = dis.readUTF();
                //System.out.println(this.name);
                this.send("歡迎進入聊天室");

                sendOthers(this.name+"進入了聊天室",true);            
            } catch (IOException e) {
                CloseUtil.closeAll(dos,dis);
                isRunning = false;
            }
        }

        //接收資料
        private String receive()
        {
            String msg = "";
            try {
                msg = dis.readUTF();
            } catch (IOException e) {
                CloseUtil.closeAll(dis);
                isRunning = false;
                all.remove(this);//移除自身
            }
            return msg;

        }

        //傳送資料
        private void send(String msg)
        {
            if(null==msg||msg.equals(""))
            {
                return;
            }
            try {
                dos.writeUTF(msg);
                dos.flush();
            } catch (IOException e) {
                CloseUtil.closeAll(dos);
                isRunning = false;
                all.remove(this);//移除自身 
            }
        }

        //傳送給其他客戶端
        private void sendOthers(String msg,boolean sys)
        {
            //是否為私聊  約定
            if(msg.startsWith("@")&& msg.indexOf(":")>-1)
            {
                //獲取name
                String name = msg.substring(1,msg.indexOf(":"));
                String contant = msg.substring(msg.indexOf(":")+1);
                for(MyChannel other:all)
                {
                    if(other.name.equals(name))
                    {
                        other.send(this.name+"對你悄悄的說:"+contant);
                    }
                }
            }
            else {
                for(MyChannel other:all)
                {
                    if(other ==this)
                    {
                        continue;
                    }

                    if(sys==true)//系統資訊
                    {
                        other.send("系統資訊:"+msg);
                    }
                    else {
                        //傳送其它客戶端
                        other.send(this.name+"對所有人說"+msg);
                    }

                }
            }
            /*
            //遍歷容器
            for(MyChannel others:all)
            {
                if(others == this)
                {
                    continue;
                }
                //傳送給其他客戶端
                others.send(msg);
            }*/

        }

        @Override
        public void run() {
            while(isRunning)
            {
                sendOthers(receive(),false);
            }
        }
    }

}

傳送資訊(供客服端使用)

package MultiServer;

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

/**
 * 傳送資料執行緒:用於傳送資料
 */
public class Send implements Runnable{
    //控制檯輸入流
    private BufferedReader console;
    //管道輸出流
    private DataOutputStream dos;
    //控制執行緒標識
    private boolean isRunning = true;
    //名稱
    private String name;

    //初始化
    public Send() {
        console = new BufferedReader(new InputStreamReader(System.in)); 
    }

    public Send(Socket client,String name)
    {
        this();
        try {
            dos = new DataOutputStream(client.getOutputStream());
            this.name = name;
            send(this.name);
        } catch (IOException e) {
            //e.printStackTrace();

        }
    }

    //1、從控制檯接收資料
    private String getMsgFromConsole()
    {
        try {
            return console.readLine();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return "";
    }

    /**
     * 1、從控制檯接收資料
     * 2、傳送資料
     */
    public void send(String msg)
    {

        try {
            if(null!=msg&& !msg.equals(""))
            {
                dos.writeUTF(msg);
                dos.flush();//強制重新整理
            }
        } catch (IOException e) {
            //e.printStackTrace();
             isRunning = false;
             CloseUtil.closeAll(dos,console);
        }
    }

    @Override
    public void run() {
        while(isRunning)
        {
            send( getMsgFromConsole());
        }
    }
}

接收資訊(供客服端使用)

package MultiServer;

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

/**
 * 接收執行緒:用於接收資料
 */
public class Receive implements Runnable{
    //管道的資料輸入流
    private DataInputStream dis ;
    //執行緒標識
    private boolean isRunning = true;

    public Receive()    {   
    }   
    public Receive(Socket client) {
        try {
            dis = new DataInputStream(client.getInputStream());
        } catch (IOException e) {
            isRunning = false;
            CloseUtil.closeAll(dis);
        }
    }

    //接收資料的方法
    public String receive()
    {
        String msg = "";
        try {
            msg = dis.readUTF();
        } catch (IOException e) {
            isRunning = false;
            CloseUtil.closeAll(dis);
        }
        return msg;
    }

    @Override
    public void run() {
        //執行緒體
        while(isRunning){
            System.out.println(receive());
        }   
    }
}

客戶端

package MultiServer;

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


/**
 * 建立客戶端:傳送資料+接收資料
 * 寫出資料:輸出流
 * 讀取資料:輸入流
 * 輸入流與輸出流在同一個執行緒內 應該獨立出來
 * 加入名稱
 */
public class Client {
    public static void main(String[] args) throws IOException
    {
        System.out.println("請輸入使用者名稱:");
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String name = br.readLine();
        if(name.equals(""))
        {
            return;
        }
        Socket client = new Socket("localhost",9999);
        new Thread(new Send(client,name)).start();//一條路徑
        new Thread(new Receive(client)).start();//一條路徑

    }
}

關閉流

package MultiServer;

import java.io.Closeable;
import java.io.IOException;

/**
 * 關閉流的方法
 */
public class CloseUtil {
    public static void closeAll(Closeable ... io)
    {
        for(Closeable temp:io)
        {
            if(null==temp)
            {
                try {
                    temp.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

執行結果:
Client1

Client2

相關推薦

Java執行網路程式設計綜合使用

最近重新看多執行緒與網路程式設計這一塊的知識,好久沒碰這一塊了,都忘得差不多了,這裡將這兩個模組的知識串接一下。同時處理多執行緒與網路程式設計最為經典的例子莫過於聊天室,那我就聊天室案例作為一個回顧。 首先,我們來看以下程式碼: package MultiT

java執行併發程式設計詳解

一、多執行緒1、作業系統有兩個容易混淆的概念,程序和執行緒。程序:一個計算機程式的執行例項,包含了需要執行的指令;有自己的獨立地址空間,包含程式內容和資料;不同程序的地址空間是互相隔離的;程序擁有各種資源和狀態資訊,包括開啟的檔案、子程序和訊號處理。執行緒:表示程式的執行流程

《瘋狂Java講義》讀書筆記(十):執行網路程式設計,類載入機制反射

第十六章:多執行緒1、一般而言,程序包含如下3個特徵:獨立性,動態性,併發性。併發性和並行性是兩個概念,並行指同一時刻,有多條指令在多個處理器上同時執行;併發指同一時刻只能有一條指令執行,但多個程序指令

【鐵匠Smith先生的專欄】關注Linux系統軟體開發、多媒體圖形技術、Linux OS技術、程序多執行緒併發網路程式設計、架構模式研究實踐、AI等新技術動向、C/C++最新程式設計技術、開原始碼整合應用等

關注Linux系統軟體開發、多媒體圖形技術、Linux OS技術、多程序多執行緒併發網路程式設計、架構模式研究與實踐、AI等新技術動向、C/C++最新程式設計技術、開原始碼整合與應用等...

Java語言程式設計-進階篇(七)執行並行程式設計【上】

1.簡單的多執行緒例子package test; public class hello { public static void main(String args[]){ Runnable printA = new PrintChar('a',100);

關於程序,執行程序和執行網路程式設計

程序執行緒網路 多工程式設計 : 可以有效的利用計算機資源,同時執行多個任務 程序 : 程序就是程式在計算機中一次執行的過程 程序和程式的區別: 程式是一個靜態檔案的描述,不佔計算機的系統資源 程序是一個動態的過程,佔有cpu記憶體等資源,有一定的生命週期 * 同一個程式的不同執行過程即為不同的程序

總結-Java執行高併發簡記

1、什麼是多執行緒? 一個程序可以開啟多個執行緒,每個執行緒可以併發/並行執行不同任務。 2、Java多執行緒實現方式    2.1、繼承Thread類    2.2、實現Runnable介面方式實現多執行緒    2.3、使

執行併發程式設計

前言 多執行緒併發程式設計是Java程式設計中重要的一塊內容,也是面試重點覆蓋區域,所以學好多執行緒併發程式設計對我們來說極其重要,下面跟我一起開啟本次的學習之旅吧。 正文 執行緒與程序 1 執行緒:程序中負責程式執行的執行單元執行緒本身依靠程

Java執行鎖模型-順序鎖資源鎖

順序鎖:當應用程式使用2把以上的鎖時,就容易出現因為多執行緒獲取鎖的順序不同而死鎖的情形,包括交叉獲取應用程式範圍內的多把已知鎖、交叉獲取應用程式與第三方方法中的多把鎖而造成的順序死鎖。絕大多數死鎖都是因為CPU排程多執行緒時,在執行時序上是交叉進行的而造成亂序獲得多把鎖,從

javaSE (四十)網路程式設計(TCP傳輸、伺服器執行網路程式設計練習:反轉字串、上傳檔案)

1、TCP傳輸: 1.客戶端 建立Socket連結服務端(指定ip地址,埠號),通過ip地址找到對應的伺服器 呼叫Socket的getInputStream和getOutputStream方法獲取和伺服器端相連的IO流 2.伺服器端 建立Se

Java執行下載網路檔案

新建一個JavaGUI介面 package Download; import java.awt.EventQueue; import java.awt.Font; import java.awt.event.ActionEvent; import java.awt.event.Action

免費初級中級高階大資料java視訊教程下載 加(微***信((號keepper,請備註java或掃下面2二3維4碼Java執行併發庫高階應用視訊教程下載

更多免費初級中級高階大資料java視訊教程下載 加(微***信((號keepper,請備註java或掃下面2二3維4碼Java多執行緒與併發庫高階應用視訊教程下載java視訊教程01_傳智播客_張孝祥_傳統執行緒技術回顧.rarjava視訊教程02_傳智播客_張孝祥_傳統定時器技術回顧.rarjava視訊教程

Java執行併發(三)

Condition等待和喚醒 在我們的並行程式中,避免不了某些寫成要預先規定好的順序執行,例如:先新增後修改,先買後賣,先進後出,對於這些場景,使用JUC的Conditon物件再合適不過了。 JUC中提供了Condition物件,用於讓指定執行緒等待與喚

Java執行併發(二)

Synchronized執行緒同步機制 很多執行緒同時對同一個資料或者檔案進行訪問的時候,對於這個檔案如果進行併發讀寫可能會產生問題。 多執行緒機制來保證同一個時間,只有一個執行緒對這個資源進行讀寫,來保證多執行緒環境下是健壯的。 程式碼案例:

Java執行併發(一)

多執行緒與併發的基礎問題 併發就是指程式同時處理多個任務的能力(一個程式被多個使用者訪問都能看到自己預期的結果) 併發的根源在於對多工情況下訪問資源的有效控制! 併發背後的問題 public class DownloadSimple {

Java執行android執行

https://www.cnblogs.com/zoe-mine/p/7954605.html      java多執行緒以及Android多執行緒執行緒和程序的區別執行緒和程序的本質:由CPU進行排程的併發式執行任務,多個任務被快速輪換執行,使得巨集觀上具有多個執行緒或者程

windows執行網路程式設計

第 10 章  多執行緒與網路程式設計初步  教學提示:Windows 是一個支援多工的作業系統。當在一個程式中需要啟動另外一 個程式時,需要用到多程序的程式設計方式。如果一個程序中有一些相似的任務需要同時推進, 可以為每個任務建立一個執行緒,從而形成多執行緒的程式設計。隨著網路技術的廣泛應用,網路 程式設計

Java執行併發庫高階應用之倒計時計數器CountDownLatch

CountDownLatch類是一個倒計時計數器,在完成一組正在其他執行緒中執行的操作之前,它允許一個或多個執行緒一直等待。用給定的計數初始化 CountDownLatch。由於呼叫了countDown() 方法,所以在當前計數到達零之前,await 方法會一直受阻塞。之後,

java執行高併發庫應用(二)執行建立和定時任務Timer

1、建立執行緒的兩種方式, 通過start, 執行run方法。 第一種實現runnable, 定義類實現Runnable介面 重寫Runnable介面中的run方法 通過Thread建立執行緒物件 將Runnable介面的子類物件作為實際引數傳遞

JAVA執行佇列

         JAVA 已經給我們提供了比較好的佇列實現Queue,繼承於Collection。 本次我使用的是BlockingQueue,繼承於Queue。              在Concurrent包中,BlockingQueue很好的解決了多執行緒中,如何高效安全“傳輸”資料的問題。通