主頁 > 後端開發 > 1500行代碼!擁有自己的聊天室------ socket聊天室實作(GUI,執行緒,TCP)

1500行代碼!擁有自己的聊天室------ socket聊天室實作(GUI,執行緒,TCP)

2021-10-17 08:25:11 後端開發

Java學習打卡:第三十一、二天【完成sococket聊天室搭建專案】

內容導航

    • 專案需求分析
      • 基礎分析
      • 專案部分代碼摘要
        • Dao的鏈表存盤實作
        • ServerListen
        • ServerReceive
        • 再看一下客戶端的ClientReceive
      • 專案問題
        • 選擇框中出現的不是用戶名
        • 服務端點擊訊息發送按鈕沒有反應
        • 不能顯示在線人數
        • 服務端退出時沒有訊息

Java養成計劃(打卡第31,2天)


內容管理:Sockect聊天室的實作
Java界面 使用了各種組件,對于這部分不了解的不用擔心,目前掌握一個大概就OK

專案需求分析

需要完成一個簡單聊天工具的界面及功能,實作服務器中轉下的多客戶端之間的通信,系統完成的功能有

  • 程式啟動后能看到當前有那些機器上線,可彈出對話聊天框,可以在其中編輯要發送的聊天資訊,并進行發送
  • 一旦某個網內的機器上線了,可即時通知,并能更新用戶界面的用戶串列
  • 雙擊某個串列項時,可彈出對話聊天框,可以在其中編輯要發送的資訊并發送
  • 聊天界面人性化,下面時發送框,上面有已有聊天記錄,并借助滾動條看到當次所有聊天記錄
  • 當有人向本機器發送訊息時,可顯示用戶接收到的資訊,并且顯示是誰所發,同時進行資訊的回復

基礎分析

首先這是一個聊天工具,使用的是C/S結構,要模擬就要使用net的Scocket和ServerSocket模擬客戶端和服務端

這里綜合運用了多種知識,已經不再是簡單的java SE知識,其中界面編程占據主要代碼,這里可以貼幾張圖看看效果,這是我肝了2天才肝完的,這里已經可以實作多型設備的連接

分為3個包

Sever包主要是服務器的相關代碼,主要是實作與用戶的互動

Dao包是模擬的資料庫包,存盤所有的用戶資訊,實作增刪改的操作

Client是客戶代碼包,只要在電腦上運行這里的代碼,就可以出現客戶端界面,約定好ip和埠號就可以通信了,這里就真正實作了客戶端型軟體,只是軟體功能簡單,可以使用web編程實作另外一種架構
可以來看一下界面
在這里插入圖片描述
再來看一下客戶端和服務端的交流
在這里插入圖片描述

專案部分代碼摘要

Dao的鏈表存盤實作

package Dao;

/**
 * 演示程式為了簡化就不用資料庫存盤,使用單鏈表完成資料庫各項功能
 * 這里一定要寫測驗代碼檢查各項功能是否可用
 * 最開開始我測驗了add,del,find功能,卻沒有測驗getCount功能,結果存在問題,后面突然放開測驗才發現錯誤
 */
public class UserLinkList {
	private  Node head;
	private int count;

	public boolean addUser(Node client)
	{
		if(head == null)
		{//頭節點也存盤資料
			head = client;
			count++;
			return true;
		}
		else {
			Node p = head;
			for(;p.next != null;p = p.next);
			{
				p.next = client;
				count++;
				return true;
			}
		}
	}
	
	public int getCount() {
		return count;
	}
	
	public Node findUser(String name)
	{
		Node p = head;
		while(p != null )//p.next != null沒有包含最后一個結點
		{
			if(p.username.equals(name))
			{
				return p;
			}
			p = p.next;
		}
		return null;
	}
	
	public Node findUser(int index)
	{
		int pos = 0;
		Node p = head;
		while(p != null&& pos < index)
		{
			p = p.next;
			pos++;
		}
		if(p != null&& pos == index)
		{
			return p;
		}
		return null;
	}
	
	public boolean delUser(Node client)
	{//洗掉后長度也要減少
		Node p = head;
		if(p.username.equals(client.username))
		{//洗掉頭結點
			head = head.next;
			count--;
			return true;
		}
		while(p != null)
		{//忘記回圈了
			if(p.next.username.equals(client.username))
			{
				p.next = p.next.next;
				count--;
				return true;
			}
			p = p.next;
		}
		return false;
	}
	
	/**
	 * 這里可以設定一個顯示的方法,供檢查使用
	 */
	public void display() {
		Node p = head;
		int pos = 1;
		while(p != null)
		{
			System.out.println("第"+pos + "個用戶"+p.username);
			p = p.next;
			pos++;
		}
	}
}
/*	
	public static void main(String[] args) {//經過測驗發現沒有問題,可以正常使用
		Node client1 = new Node();
		client1.username = "張三";
		Node client2 = new Node();
		client2.username = "李四";
		Node client3 = new Node();
		client3.username = "王五";
		//其他的就不測驗了,反正該項就可以測驗了
		UserLinkList userLinkList = new UserLinkList();//自動初始化
		userLinkList.addUser(client1);
		userLinkList.addUser(client2);
		userLinkList.addUser(client3);
//		userLinkList.display();
		Node node = userLinkList.findUser(0);
		userLinkList.delUser(node);
		userLinkList.display();
		System.out.println(userLinkList.getCount());
	}
*/

現在撰寫這段代碼應當是非常簡單的,注意一定要測驗

ServerListen

簡單看一下這個監聽執行緒,可以監聽用戶是否上線

package Server;
/**
 * @author OMEY-PC
 *本程式的作用是實作服務器偵聽的執行緒化,其中run方法通過client = new Node();創建一個客戶端物件,通過client.socket = server.accept來設定介面,通過client.input
 *output來建立輸入輸出流
 */

import java.io.*;
import java.net.*;
import Dao.*; //連接資料
import javax.swing.*;

public class ServerListen extends Thread{
	ServerSocket server;
	JComboBox combobox;
	JTextArea textarea;
	JTextField textfield;
	UserLinkList userLinkList;
	Node client;
	ServerReceive recvThread;
	public boolean isStop;
	/**
	 * 聊天服務端的用戶上下線偵聽類
	 */
	public ServerListen(ServerSocket server,JComboBox combobox,JTextArea textarea,JTextField textField,UserLinkList userLinkList) {
		this.server = server;
		this.combobox = combobox;
		this.textarea = textarea;
		this.textfield = textField;
		this.userLinkList = userLinkList;
		isStop = false;
	}
	@Override
	public void run() {
		while(!isStop && !server.isClosed())//沒有停止服務
		{
			try {
				client = new Node();
				client.socket = server.accept();//用來指代所連接的客戶端
				client.output = new ObjectOutputStream(client.socket.getOutputStream());
				client.output.flush();
				client.input = new ObjectInputStream(client.socket.getInputStream());
				client.username = (String)client.input.readObject();
				//顯示提示資訊
			    combobox.addItem(client.username);//改成用戶名
			    userLinkList.addUser(client);
			    textarea.append("用戶" + client.username+"上線"+"\n");
			    textfield.setText("在線用戶"+ userLinkList.getCount()+"人\n");
			    
			    recvThread = new ServerReceive(textarea,textfield,combobox,client,userLinkList);
			    recvThread.start();//啟動執行緒
			}catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
}

ServerReceive

該執行緒實作服務器與用戶之間的資訊互動

package Server;
/**
 * @author OMEY-PC
 *服務器收發訊息的類
 */

import java.net.ServerSocket;

import javax.swing.*;
import Dao.*;

public class ServerReceive extends Thread{
	JTextArea textarea;//訊息展示域
	JTextField textfield;//文本輸入域
	JComboBox combobox; //復選框
	Node client;//用戶
	UserLinkList userLinkList;
	public boolean isStop;
	public ServerReceive(JTextArea textarea, JTextField textfield, JComboBox combobox, Node client,
			UserLinkList userLinkList) {
		this.textarea = textarea;
		this.textfield = textfield;
		this.combobox = combobox;
		this.client = client;
		this.userLinkList = userLinkList;
		isStop = false;
	}
	
	@Override
	public void run()
	{
		//向所有人發送用戶的串列
		sendUserList();
		while(!isStop && !client.socket.isClosed())
		{
			try {//型別,對誰,狀況,行為,資訊
				String type = (String)client.input.readObject();
				if(type.equalsIgnoreCase("聊天資訊"))
				{
					String toSomebody =(String)client.input.readObject();//從客戶端接收資訊
					String status = (String)client.input.readObject();
					String action = (String)client.input.readObject();
					String message = (String)client.input.readObject();
					String msg = client.username+" "+ action + "對"+ toSomebody +" 說 " + message + "\n";//接收的訊息
					if(status.equalsIgnoreCase("悄悄話"))
					{
						msg = "[悄悄話]" + msg; //若為悄悄話,就在前面加上標識
					}
					textarea.append(msg);
					if(toSomebody.equalsIgnoreCase("所有人"))
					{
						sendToAll(msg);//這里是接受的用戶訊息,和之前的向所有人發訊息不一樣
					}
					else {//向用戶發訊息
						try {
							client.output.writeObject("聊天資訊");
							client.output.flush();//重繪流
							client.output.writeObject(msg);
							client.output.flush();
						}catch (Exception e) {
							e.printStackTrace();
						}
						Node node = userLinkList.findUser(toSomebody);
						if(node != null)
						{
							node.output.writeObject("聊天資訊");
							node.output.flush();
							node.output.writeObject(msg);//向選定資訊發送資訊
							node.output.flush();//重繪輸出流緩沖區中的資訊
						}
					}
			    }
				else if(type.equalsIgnoreCase("用戶下線"))
				{
					Node node = userLinkList.findUser(client.username);
					userLinkList.delUser(node);
					String msg = "用戶"+ client.username +"下線\n";
					int count = userLinkList.getCount();
					combobox.removeAllItems();
					combobox.addItem("所有人");
					int i = 0;
				    while(i < count)
				    {
				    	node = userLinkList.findUser(i);
				    	if(node == null)
				    	{
				    		i++;
				    		continue;
				    	}
				    	combobox.addItem(node.username);
				    	i++;
				    }
					combobox.setSelectedIndex(0);//選擇第一個,所有人
					textarea.append(msg);
					textfield.setText("在線用戶"+ userLinkList.getCount() +"人\n");
					
					sendToAll(msg);
					sendUserList();//重新發送用戶串列
					break;
				}
		    }catch (Exception e) {
				e.printStackTrace();
			}
	    }
	}
	/**
	 * 向所有人發送訊息
	 */
	public void sendToAll(String msg)
	{
		int count = userLinkList.getCount();
		int i = 0;
		while(i < count)
		{//給用戶串列中的每一個人都發送訊息
			Node node = userLinkList.findUser(i);
			if(node == null)
			{
				i++;
				continue;
			}
			try {//輸出流
				node.output.writeObject("聊天資訊");
				node.output.flush();
				node.output.writeObject(msg);//聊天訊息寫入輸出流(to client)
				node.output.flush();
			}catch (Exception e) {
				e.printStackTrace();
			}
			i++;
		}
	}
	/**
	 * 向所有人發送用戶串列
	 */
	public void sendUserList() {
		String userList = "";
		int count = userLinkList.getCount();
		int i = 0;
		while(i < count)
		{
			Node node = userLinkList.findUser(i);
			if(node == null)
			{
				i++;
				continue;
			}
			userList += node.username;
			userList += "\n";
			i++;
		}
		i = 0; //給每個人發送訊息
		while(i < count)
		{
			Node node = userLinkList.findUser(i);
			if(node == null)
			{
				i++;
				continue;
			}
			try {
				node.output.writeObject("用戶串列");
				node.output.flush();
				node.output.writeObject(userList);
				node.output.flush();
			}catch (Exception e) {
				e.printStackTrace();
			}
		}
		i++;
	}	
}
/**
 * 本程式可以實作通過執行緒向所有人發送訊息,用戶串列,以及向選定的人發送聊天訊息等,主要是是實作服務端收發訊息的執行緒化,其中sendUserList()發送串列,
 * client.input.redObject()獲取客戶端發送到服務端的訊息,通sendToAll(),將發送到發送到所有人的資訊發送到各個客戶端
 */

再看一下客戶端的ClientReceive

該執行緒是實作客戶端與系統之間的資訊互動,注解豐富

package Client;

import java.io.*;
import java.net.*;

import javax.swing.*;

public class ClientReceive extends Thread{
	private JComboBox combobox;
	private JTextArea textarea;
	Socket socket;
	ObjectOutputStream output;
	ObjectInputStream input;
	JTextField showStatus;
	public ClientReceive(JComboBox combobox, JTextArea textarea, Socket socket, ObjectOutputStream output,
			ObjectInputStream input, JTextField showStatus) {
		this.combobox = combobox;
		this.textarea = textarea;
		this.socket = socket;
		this.output = output;
		this.input = input;
		this.showStatus = showStatus;
	}
	
	@Override
	public void run() {//從服務端獲得訊息
		while(!socket.isClosed())
		{
			try {
				String type = (String)input.readObject();//獲得流,read讀取資訊
				if(type.equalsIgnoreCase("系統資訊"))
				{
					String sysmsg = (String)input.readObject();
					textarea.append("系統資訊" + sysmsg);
				}
				else if(type.equalsIgnoreCase("服務關閉"))
				{
					output.close();
					input.close();
					socket.close();
					textarea.append("服務器已經關閉!\n");
					break;
				}
				else if(type.equalsIgnoreCase("聊天資訊"))
				{
					String message = (String)input.readObject();
					textarea.append(message);
				}
				else if(type.equalsIgnoreCase("用戶串列"))
				{
					String userlist = (String)input.readObject();
					String[] usernames = userlist.split("\n"); //用換行符分隔
					combobox.removeAll();//先移出去
					int i = 0;
					combobox.addItem("所有人");
					while(i < usernames.length)
					{
						combobox.addItem(usernames[i]);
						i++;
					}
					combobox.setSelectedIndex(0);
					showStatus.setText("在線用戶"+ usernames.length +" 人");
				}
			}catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
}

其余的界面的部分就不放出來了,代碼太長,每個都有400多行,如果有興趣,就到我的gitee上去瀏覽,后面會放上地址

專案問題

選擇框中出現的不是用戶名

查找相應模塊發現是因為addItem中添加的時結點,而不是結點中的username,修改后正常

服務端點擊訊息發送按鈕沒有反應

查找監聽器部分,發現監聽器監聽該部分代碼寫錯,將button又寫成sysMessage

不能顯示在線人數

查找偵聽執行緒,啟動客戶端發現拋出例外

Cannot invoke “javax.swing.JTextField.setText(String)” because “this.textfield” is null

textfield為空,查找問題源頭;發現在構造方法中:the assignmen to variable has no effect;這是因為單詞拼寫錯誤,編譯器并沒有報錯

服務端退出時沒有訊息

系統報錯

Cannot read field “input” because “node” is null

意識到問題出在鏈表上,系統要求從0開始,而鏈表中的序號是從1開始的,修該鏈表中的findUser中的pos為0就解決

寫這個程式寫了兩天,直接廢了~~

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/319709.html

標籤:java

上一篇:一個c語言小白的成長的開始

下一篇:LeetCode 91. 解碼方法【c++/java詳細題解】

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more