1. 程式人生 > >python之寫了3個週末及幾個晚上的ftp終於完成了

python之寫了3個週末及幾個晚上的ftp終於完成了

本程式已上傳githup,點我
作業:開發一個支援多使用者線上的FTP程式
要求:
使用者加密認證
允許同時多使用者登入
每個使用者有自己的家目錄 ,且只能訪問自己的家目錄
對使用者進行磁碟配額,每個使用者的可用空間不同
允許使用者在ftp server的隨意切換目錄
允許使用者檢視當前目錄下檔案
允許上傳和下載檔案,保證檔案一致性
檔案傳輸過程中顯示進度條
支援檔案的斷點續傳

目錄結構
ftp/
|
|—–bin/
| |—–ftpClient.py # 客戶端的入口
| |—–ftpServer.py # 服務端的入口
|
|—-conf/
| |—–setting.py # 一些配置資訊
|
| —–core/
| |—–auth.py # 使用者認證模組
| |—–socket_client.py # socket客戶端主程式
| |—–socket_server.py # socket服務端主程式
| |—–upload.py # 傳輸資料模組
|
|——database/ # 已經存在的使用者資料庫檔案
|—–kai.db
|—–xiaolv.db
|
|—–download/ # 使用者下載檔案的預設地址資料夾
|
|—–home/ # 已經建立的使用者的家目錄
| |—–kai/
| | —–xiaolv/
|
|—–README.py

實現過程(主要是下載上傳檔案過程)
舉例: 客戶端向服務端下載檔案
1.判斷客戶端是否存在已下載的檔案
|
|——- 是 —–》 客戶端確認是否需要續傳 ——》 是 ——–》 服務端開始找資源—》 找到資源 —-》 開始續傳
| | |—–》未找到資源—-》 退出到下載介面
| |
| | —–》 否 —-》 退出到下載介面
|
|——- 否 —–》服務端開始找資源—》 找到資源 —-》 開始續傳
|—-》 未找到資源—-》退出到下載介面

功能實現:
1.實現使用者加密認證登入,目前暫不支援在互動介面新增使用者,需要自己手動在database/目錄下建立使用者的資料庫檔案及新增相關的使用者資訊,並在home/目錄下新增相關的目錄
2.支援檔案上傳下載,支援斷點續傳,目前只支援檔案傳輸,不支援目錄的上傳下載
3.支援目錄切換及顯示當前賬戶下的目錄

http狀態碼:
200: 客戶請求成功
202:建立的檔案已經存在
400:使用者名稱不存在,使用者認證失敗
403.11:密碼錯誤
401: 命令不存在
402 :檔案不存在
413:磁碟空間不夠用
000: 系統互動碼

—————-我是分割線————–
接下來直接上程式碼

bin/

ftp客戶端介面

import os,sys
path = os.path.dirname(os.path.abspath(__file__))
sys.path.append(path)

from core import socket_client

if __name__ == "__main__":
    host,port = "localhost",9901
    myClient = socket_client.MySocketClient(host,port)
    myClient.start()

ftp服務端介面

import sys,os

path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(path)

from core import socket_server

if __name__ == "__main__":
    HOST,PORT = "localhost",9901
    server = socket_server.socketserver.ThreadingTCPServer((HOST,PORT),socket_server.MyTCPServer)
    server.serve_forever()

conf/

setting.py

import os,sys

BASEDIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

#使用者家目錄
HOMEPATH = os.path.join(BASEDIR,"home")

#資料庫目錄
DATABASE = os.path.join(BASEDIR,"database")

#分給沒個使用者的磁碟配額
LIMITSIZE = 20480000

#使用者從服務端下載檔案的預設地址
DEFAULT_PATH = "C:\\Users\\Administrator\\Desktop\\python\\ftp\\download"

core/

auth.py

import hashlib,os,json
from conf import settings

class User_auth(object):
    def auth(self,account_info):
        """
        #此功能是進行使用者的登入資訊驗證,如果登入成功,那麼返回使用者對應的http狀態碼及賬戶資訊,否則只返回http狀態碼
        :param account_info: 使用者的賬戶資訊:使用者名稱,密碼
        :return:
        """
        name = account_info.split(":")[0]
        pwd = account_info.split(":")[1]
        pwd = self.hash(pwd.encode())   # 將使用者名稱的密碼轉換成hash值
        user_db_file = settings.DATABASE + r"\%s.db" %name  # 也可以寫成 "\\%s.db"  or "/%s.db"
        if os.path.isfile(user_db_file):    # 輸入的使用者名稱存在
            with open(user_db_file) as fr:
                user_db_info = json.loads(fr.read())     # or josn.load(fr)
                if pwd == user_db_info['password']:
                    return "200",user_db_info      # 確定,客戶請求成功
                else:
                    return "403.11",None     # 密碼錯誤
        else:
            return "400",None     # 使用者名稱不存在,使用者認證失敗


    def hash(self,pwd):
        """
         使用者的密碼加密
        :param self:
        :param pwd: 使用者密碼
        :return:
        """
        m = hashlib.md5()
        m.update(pwd)
        return m.hexdigest()

socket_client.py

import sys,os,time
import socket,hashlib
from core import upload
from conf import settings


class MySocketClient(object):
    def __init__(self,host,port):
        self.host = host
        self.port = port
        self.client = socket.socket()  # 建立客戶端例項

    def start(self):
        self.client.connect((self.host,self.port))  # 連線服務端,localhost為迴環地址,一般用於測試
        while True:
            """客戶端開始輸入賬戶資訊登入"""
            name = input("\033[31m 請輸入賬戶名: \033[0m").strip()
            pwd = input("\033[31m 請輸入密碼: \033[0m").strip()
            # name = "kai";pwd = 123456
            if not name or not pwd :continue  # 如果賬戶或密碼為空請重新輸入
            userInfo = "%s:%s" % (name,pwd)
            self.client.send(userInfo.encode())  # 向服務端傳送使用者資訊
            status_code = self.client.recv(1024).decode() # 接受來自服務端的http狀態碼
            if status_code == "400":
                print("\033[1;32m 使用者名稱不存在,使用者認證失敗,請重新輸入 \033[0m")
                continue   # 使用者名稱不存在重新輸入
            if status_code == "403.11":
                print("\033[1;32m 密碼錯誤,請重新輸入 033[0m")
                continue
            if status_code == "200":
                print("\033[1;32m 登入成功\033[0m")
            self.interact()

    def interact(self):
        """
        客戶端與服務端的互動介面
        :return:
        """
        while True:
            cmd = input("""請輸入操作: 
            \033[1;31m
            eg:
            get filename  # 從服務端下載檔案
            put filename  # 從服務端上傳檔案
            ls            # 檢視當前目錄下的檔案
            cd            # 目錄切換
            pwd           # 檢視當前目錄
            mkdir dirname # 建立目錄
            >>>  \033[0m""").strip()
            cmd_action = cmd.split()[0]
            if hasattr(self,cmd_action):
                func = getattr(self,cmd_action)
                func(cmd)
            else:
                print("輸入命令不存在")

    def put(self,cmd):
        """
        客戶端上傳檔案
        :param self:
        :param cmd: 使用者的操作命令  eg: put filename
        :return:
        """
        lst = cmd.split()    # 命令分割,此次只允許一次上傳一個檔案
        if len(lst) == 2:
            filename = lst[1]
            if os.path.isfile(filename):
                self.client.send(cmd.encode())   # 向服務端傳送上傳檔案命令
                status_code = self.client.recv(1024).decode()
                total_size = os.stat(filename).st_size
                self.client.send(str(total_size).encode())
                status_code = self.client.recv(1024).decode()    # 接收來自服務端的http狀態碼,判斷賬戶是否空間充足,檔案是否存在
                if status_code == "202":
                    while True:
                        affirm_msg = input("建立的檔案已經存在,請確認是否需要續傳:1:續傳 2:不續傳:")
                        if affirm_msg == "1":
                            print("開始續傳")
                            self.client.send("000".encode())    # 傳送互動碼,避免粘包,服務端此時需要2次連續send
                            has_send_size = self.client.recv(1024).decode()     # 已經發送給服務端的檔案大小
                            send_md5 = upload.Breakpoint().transfer(filename, int(has_send_size), total_size, self.client) # 進行檔案的傳輸,返回此次傳送資料的md5
                            recv_md5 = self.client.recv(1024).decode()   # 服務端接收到此次傳送資料的md5
                            # print("\nsend",send_md5)
                            # print("recv",recv_md5)
                            if send_md5 == recv_md5 :
                                print("傳送資料成功")
                                break
                            else:
                                print("傳送資料不成功,請重新發送")
                                break
                        elif affirm_msg == "2":
                            print("不續傳")
                            break
                        else:
                            print("輸入的命令不存在")
                            continue
                elif status_code == "402":
                    print("檔案不存在")   # 檔案不存在
                    send_md5 = upload.Breakpoint().transfer(filename, 0, total_size,self.client)  # 進行檔案的傳輸,返回此次傳送資料的md5
                    recv_md5 = self.client.recv(1024).decode()  # 服務端接收到此次傳送資料的md5
                    print("\nsend md5", send_md5)
                    print("recv md5",recv_md5)
                    if send_md5 == recv_md5 : print("傳送資料成功")
                    else : print("傳送資料不成功,請重新發送")
                else:
                    print("使用者磁碟空間不夠")
            else:print("傳送的檔案不存在")
        else: print("401 error,命令不正確,一次只能上傳一個檔案")

    def get(self,cmd):
        lst = cmd.split()
        if len(lst) == 2:
            filename = lst[1]
            fileName = filename.split("\\")[-1]
            self.default_file = os.path.join(settings.DEFAULT_PATH,fileName)
            if os.path.isfile(self.default_file): # 使用者下載的檔案在預設地址下是否存在
                while True:
                    affirm_msg = input("建立的檔案已經存在,請確認是否需要續傳:1:續傳 2:不續傳:")
                    if affirm_msg == "1":
                        self.get_file(cmd)
                    elif affirm_msg == "2":
                        break
                    else:
                        print("命令不正確")
            else:
                self.get_file(cmd,exist="no")
        else: print("401 error,命令不正確,一次只能下載一個檔案")

    def get_file(self,cmd,exist="yes"):
        """
        因為get方法從客戶端下載檔案,檔案存在續傳和檔案不存在直接下載寫法一樣,故提取
        :param cmd:
        :param exist: 判斷客戶端檔案是否存在
        :return:
        """
        self.client.send(cmd.encode())
        status_code = self.client.recv(1024).decode()
        if status_code == "206":
            print("需要下載的檔案在客戶端存在,在服務端也存在,開始續傳" if exist == "yes" else "需要下載的檔案在服務端存在,在客戶端不存在,開始下載")
            has_recvd_size = os.stat(self.default_file).st_size if exist == "yes" else 0
            self.client.send(str(has_recvd_size).encode())
            total_size = self.client.recv(1024).decode()
            self.client.send("000".encode())  # 系統互動,防止粘包
            recvd_size = 0
            m = hashlib.md5()
            with open(self.default_file, "ab") as fa:
                remain_size = int(total_size) - has_recvd_size
                while recvd_size != remain_size:  # 迴圈接收來自服務端的檔案資料
                    data = self.client.recv(1024)
                    m.update(data)
                    recvd_size += len(data)
                    fa.write(data)
                    all_recvd_size = has_recvd_size + recvd_size
                    upload.Breakpoint().progress_bar(all_recvd_size,int(total_size))
                self.client.send("000".encode())  # 粘包
                send_md5 = self.client.recv(1024).decode()  # 服務端傳送資料的md5
                if has_recvd_size == int(total_size):
                    print("檔案大小一致,無需下載")
                    self.interact()    # 如果按這種邏輯(先判斷客戶端檔案是否存在,在判斷該檔案的大小是否一致)寫,只能呼叫interact()選項才能返回
                if send_md5 == m.hexdigest():
                    print("接收檔案成功")
                else:
                    print("接收檔案不成功")
        else:
            print("需要下載的檔案不存在,無法續傳")

    def mkdir(self,cmd):
        self.client.send(cmd.encode())
        status_code = self.client.recv(1024).decode()
        if status_code == "403":
            print("建立的目錄存在")
        elif status_code == "401":
            print("輸入的命令不存在")

    def pwd(self,cmd):
        self.client.send(cmd.encode())
        cur_path = self.client.recv(1024).decode()
        print(cur_path)

    def cd(self,cmd):
        """
        :param cmd:  eg cd dirname / cd .. /
        :return:
        """
        self.client.send(cmd.encode())
        status_code = self.client.recv(1024).decode()
        if status_code == "402":
            print("建立的目錄不存在")
        elif status_code == "400":
            print("命令不正確,格式 cd .. / cd dirname")
        elif status_code == "403":
            print("沒有上層目錄了")
        else:
            pass

    def ls(self,cmd):
        self.client.send(cmd.encode())
        dirs = self.client.recv(1024).decode()
        print(dirs)




if __name__ == "__main__":
    host,port = "localhost",9998
    myClient = MySocketClient(host,port)
    myClient.start()

socket_server.py

import socketserver
import hashlib,os
from core import upload
from core import auth
from conf import settings


#socketserver
class MyTCPServer(socketserver.BaseRequestHandler):
    def handle(self):
        try:
            while True:
                self.data = self.request.recv(1024).decode()   # 接受來自客戶端的賬號的登入資訊
                auth_result = auth.User_auth().auth(self.data)   #  進行使用者驗證
                status_code = auth_result[0]
                if status_code == "400" or status_code == "403.11":
                    self.request.send(status_code.encode())  # 使用者名稱不存在或密碼錯誤
                    continue
                self.request.send(status_code.encode())   # 使用者認證成功
                self.user_db_info = auth_result[1]        # 獲取使用者的資料庫資訊
                self.home_path = self.user_db_info['homepath']
                self.current_path = self.user_db_info['homepath']  # 登陸成功後立即定義一個“當前路徑”變數,供後面建立目錄,切換目錄使用
                while True:
                    self.cmd = self.request.recv(1024).decode()   # 接收客戶端的上傳或下載命令
                    self.cmd_action = self.cmd.split()[0]
                    if hasattr(self,self.cmd_action):
                        func = getattr(self,self.cmd_action)
                        func(self.cmd)
                    else:
                        self.request.send("401".encode())  # 命令不存在
        except ConnectionResetError as e:
            self.request.close()
            print(e)


    """服務端可以提供的命令"""
    def put(self,cmd):
        """
        接受客戶端的上傳檔案命令
        :param self:
        :param cmd: eg: put filename
        :return:
        """
        filename = cmd.split()[1]
        fileName = filename.split("\\")[-1]  # 檔案的名稱
        home_file = os.path.join(self.current_path, fileName)    # 判斷當前路徑下是否有上傳的檔名
        self.request.send("000".encode())  # 系統互動碼
        total_size = self.request.recv(1024).decode() # 上傳檔案大小
        remain_size = self.accountRemainSize()  # 獲得賬戶剩餘大小
        if remain_size > int(total_size):
            if os.path.isfile(home_file):
                self.request.send("202".encode())   # 建立的檔案已經存在
                self.request.recv(1024)   # 等待ack指令
                has_recvd_size = os.stat(home_file).st_size   # 已經接收的大小
                self.request.send(str(has_recvd_size).encode())
            else :
                has_recvd_size = 0
                self.request.send("402".encode())  # 建立的檔案不存在
            ###開始接受客戶端的資料
            recvd_size = 0
            m = hashlib.md5()
            with open(home_file,"ab") as fa:
                while recvd_size != int(total_size) - has_recvd_size:    # 迴圈接受來自的檔案資料
                    data = self.request.recv(1024)
                    m.update(data)
                    recvd_size += len(data)
                    fa.write(data)
                self.request.send(m.hexdigest().encode())   # 傳送接收到的檔案的md5
        else:
            self.request.send("413".encode())  # 磁碟空間不夠用

    def get(self,cmd):
        """
        接受客戶端的上傳檔案命令
        :param self:
        :param cmd: eg: get filename
        :return:
        """
        filename = cmd.split()[1]
        fileName = filename.split("\\")[-1]  # 檔案的名稱
        home_file = os.path.join(self.home_path, fileName)    # 用於判斷家目錄下是否有下載的檔名
        if os.path.isfile(home_file):    # 這裡不用判斷if status_code == "205" or status_code == "402": ,因為如果接收到客戶端的狀態碼,服務端一定要先去找檔案是否存在
            total_size = os.stat(home_file).st_size
            self.request.send("206".encode())  # 服務端有客戶需要下載的檔案
            has_send_size = self.request.recv(1024).decode()
            self.request.send(str(total_size).encode())
            self.request.recv(1024)  # 粘包
            send_md5 = upload.Breakpoint().transfer(home_file, int(has_send_size), total_size, self.request)
            self.request.recv(1024)  # 粘包
            self.request.send(send_md5.encode())
        else:
            self.request.send("402".encode())  # 服務端檔案不存在


    def accountRemainSize(self):
        """
        統計登入使用者可用目錄大小
        :return:
        """
        account_path = self.user_db_info["homepath"]
        print("homepath:",account_path,)
        limitsize = self.user_db_info['limitsize']
        used_size = 0
        for root,dirs,files in os.walk(account_path):
            used_size += sum([ os.path.getsize(os.path.join(root,filename))  for filename in files])
        return limitsize - used_size

    def mkdir(self,cmd):
        """
        建立目錄,支援建立級聯目錄
        :param cmd:
        :return:
        """
        dir = cmd.split()
        dir_path = os.path.join(self.current_path,dir[-1])
        if len(dir) == 2:
            if not os.path.isdir(dir_path):
                try:
                    os.mkdir(dir_path)
                except FileNotFoundError as e:
                    os.makedirs(dir_path)   #建立級聯目錄
                self.request.send("200".encode())
            else:
                self.request.send("403".encode())  # 建立目錄存在
        else:
            self.request.send("400".encode())

    def pwd(self,cmd):
        self.request.send(self.current_path.encode())

    def cd(self,cmd):
        dir = cmd.split()
        if len(dir) == 2:
            if dir[-1] == "..":    # 只能讓使用者在自己目錄下操作
                if len(self.current_path) > len(self.home_path):
                    self.current_path = os.path.dirname(self.current_path)
                    self.request.send("200".encode())
                else:
                    self.request.send("403".encode()) # 請求被拒絕,沒有上層目錄了
            elif os.path.isdir(self.current_path + "\\" + dir[-1]):
                self.current_path = self.current_path + "\\" + dir[-1]
                self.request.send("200".encode())
            else:
                self.request.send("402".encode())
        else:
            self.request.send("400".encode())

    def ls(self,cmd):
        dirs = os.listdir(self.current_path)
        self.request.send(str(dirs).encode())

upload.py

import hashlib,sys,time


class Breakpoint(object):
    # 本模組確認使用者上傳或下載的檔案是否存在,如果存在是否需要斷點續傳
    def transfer(self,filename,has_send_size,total_size,conn):
        """
        進行續傳
        :param filename:
        :param has_send_size: 已經發送的檔案大小
        :param total_size: 需要傳輸檔案總大小
        :param conn: 客戶端和服務端進行資料交換的介面
        :return:
        """
        with open(filename,'rb') as fr:
            fr.seek(has_send_size)     # 定位到續傳的位置
            print("has_send",has_send_size,"total",total_size)
            m = hashlib.md5()
            if has_send_size == total_size:
                self.progress_bar(has_send_size, total_size)
            for line in fr:
                conn.send(line)
                m.update(line)
                has_send_size += len(line)
                # self.progress_bar(has_send_size,total_size)
        return m.hexdigest()

    def progress_bar(self,has_send_size,total_size):
        bar_width = 50     # 進度條長度
        process = has_send_size / total_size
        send_bar = int(process * bar_width + 0.5)     # 傳送的資料佔到的進度條長度,四捨五入取整
        sys.stdout.write("#" * send_bar  + "=" * (bar_width - send_bar) +"\r" )  # 注意點:貌似只能這麼寫才能達到要求
        sys.stdout.write("\r%.2f%%: %s%s"% (process * 100,"#" * send_bar,"=" * (bar_width - send_bar)))  # 注意點:在pycharm中要加\r\n,用sublime只要\r否則換行
        sys.stdout.flush()

————-我還是分割線———————-
最後效果圖
這裡寫圖片描述

這裡寫圖片描述

相關推薦

python3週末晚上ftp終於完成

本程式已上傳githup,點我 作業:開發一個支援多使用者線上的FTP程式 要求: 使用者加密認證 允許同時多使用者登入 每個使用者有自己的家目錄 ,且只能訪問自己的家目錄

python有用的3內置函數(filter/map/reduce)

得到 fun func 一個 == reduce 語法 too map 這三個內置函數還是非常有用的,在工作中用的還不少,順手,下面一一進行介紹 1、filter 語法:filter(function,iterable) 解釋:把叠代器通過function函數進行過

Python路58-Django安裝配置一些基礎知識點

python目錄一、安裝Django二、創建工程三、創建app四、靜態文件五、模板路徑六、設置settings七、定義路由八、定義視圖九、渲染模板十、運行Django是一款Python的web框架一、安裝Djangopip3 install django安裝完成後C:\Python35\Script下面會生成

Python月薪能翻3倍?這4行業真相字字戳心!

從2017年開始,Python成為了現象級語言,一舉拿下程式語言的C位。作為“最容易學習”的膠水語言,萬能屬性的 Python 在程式設計開發中可以說是大殺四方,幾乎都可以輕鬆勝任。 而在這背後,有層出不窮的話題和文章出來,尤其噹噹 Python進入小學課本、乃至浙江省高考的政策

python旅-日記3(記錄零基礎自己的每天學習)

2018/9/14 基礎知識 字串 正則表示式 xpath 字串 1.> len() 2.> eval(n’+'m)字串計算 3.> ord()單個字元轉為ASCII chr()整數轉為字元 4.> raw_input()輸入轉為字元型別 input()基礎型別 5.

PythonPandas(3

#常用數學,統計方法 import numpy as np import pandas as pd In [7]: df = pd.DataFrame({'key1':[4,5,3,np.nan,2], 'key2':[1,2,np.nan,4,5],

Day5:python函式(3

一、函式預設值引數 內建函式: input()、print()、int() 常用模組: 1、列表生成式 s =[1,2,3,4,5,6,7,8] for i in s: print(i+1) res = [ i+1 for i in s] res = [str(i) for i in

Python路(組合資料型別操作)

Python之路(組合資料型別及操作) 集合型別 集合型別的定義 集合是多個元素的無序組合 集合型別與數學中的集合概念一致 集合型別之間無序,每個元素唯一,不存在相同元素 集合元素之間不可或更改,不是可變資料型別 集合用大括號 {} 表示,元素間用逗號分隔 建立集合型別用 {}

python路 ---列表/字典生成式匿名函式

列表解析: l = ['aaa','bbb','ccc'] #將l內的元素全部變為大寫... L = [] for i in l: L.append(i.upper()) print(L) #使用列表解析: L = [i.upper() for i in

python爬蟲學習筆記3:bs4BeautifulSoup庫學習

Beuatiful Soup bs類對應一個HTML/xml文件的全部內容 from bs4 import BeautifulSoup import bs4 soup=BeautifulSoup('<p>data</p>','ht

Python字典中的鍵映射多

多個 pen pytho code col collect ons pan 映射 字典的鍵值是多個,那麽就可以用列表,集合等來存儲這些 鍵值 舉例 print({"key":list()}) # {‘key‘: []} print({"key":set()})

JSP三指令9內置對象

內置對象 class lang itl lan 解釋 utf port taglib 註:本文編寫方便自己以後用到時查閱 三大指令:include、 page、taglib include指令:   作用: 在當前頁面用於包含其他頁面   語法: <

3.springioc bean 的屬性

就會 ring proto 創建 spring 構造函數 問題 false 提前 1.lazy-init="false" 默認值為false,指的是bean的創建時機的spring容器一啟動就會加載這些類.有點是及時發現bean的相關錯誤,因為spring容器啟動,bean

假如高考考python編程,這些題目你會呢?

img 命令 OS 高級 map .com 學校 面試題 程序設計語言 Python(發音:英[?pa?θ?n],美[?pa?θɑ:n]),是一種面向對象、直譯式電腦編程語言,也是一種功能強大的通用型語言,已經具有近二十年的發展歷史,成熟且穩定。它包含了一組完善而且容易理

一個正定矩陣 可以成它的特徵值與正定矩陣的乘積和

最近看 Byod 的凸優化書,裡面有這個表示式,若 X X X 為正定矩陣,則

在經歷6月的學習後,我終於上架自己的第一款APP---酷課堂iOS群問答精華整理(201807

酷課堂iOS交流群 我們是一個什麼樣的組織:酷課堂iOS交流群,聚集了一群熱愛技術、有趣、有料,平均Q齡在10年以上的“老司機”,他們遍佈在全國/球各地,有知名企業iOS工程師、高校大學生、自由職業者……如果你也是這樣的人,歡迎加入我們,一起暢聊iOS技術及周邊。 “很乾”“很佛系”每晚11點後熄燈(禁言

秋招後實習初感小flag

秋招      歷經20多天的秋招,每天機器人般的生活,早起奔波於各大宣講會,參加一場又一場的筆試,晚上懷著忐忑的心情等著面試通知,從剛開始的興奮到後來的麻木,面試已經全靠本能去回答,終於也是拿了幾個還算滿意的offer,結束了自己的秋招,開始了人生的第一份職場生活

python中,向 list 新增資料種方法

//...1... def a(): list=[] for i in range(1000): list=list+[i] print(list) //...

HDFS讀檔案中涉及到的思想

HDFS讀寫檔案中涉及到的幾個思想 1.網路拓撲--節點距離計算 2.機架感知(副本節點選擇) 1.網路拓撲–節點距離計算 1.節點距離:兩個節點到達最近的共同祖先的距離總和。 2.圖解: 2.機架感知(副本節點選擇) 1.官方說明

在經歷6月的學習後,我終於上架自己的第一款APP---酷課堂iOS群問答精華整理(201807期)

酷課堂iOS交流群 我們是一個什麼樣的組織: 酷課堂iOS交流群,聚集了一群熱愛技術、有趣、有料,平均Q齡在10年以上的“老司機”,他們遍佈在全國/球各地,有知名企業iOS工程師、高校大學生、自由職業者……如果你也是這樣的人,歡迎加入我們,一起暢聊iOS技術及周邊。