4 if form.getvalue('subject'): 5 subject = form.getvalue('subject') 6 else: 7 subject = \"Not set\" 8 9 print(\"Content-type:text/html\\r\\n\\r\\n\") 10 print(\"<html>\") 11 print(\"<head>\") 12 print(\"<title>Radio for CGI Program</title>\") 13 print(\"</head>\") 14 print(\"<body>\") 15 print(\"<h2> Selected Subject is %s</h2>\" % subject) 16 print(\"</body>\") 17 print(\"</html>\") จากตวั อยา่ งโปรแกรมท่ี 14_5 บรรทดั ท่ี 4 โปรแกรมทาการตรวจสอบค่าในตวั แปร subject วา่ ถกู เลอื ก (check) หรอื ไม่ ถา้ subject ถกู เลอื ก โปรแกรมดงึ ค่าขอ้ มลู ดงั กลา่ วมาเกบ็ ไวใ้ นตวั แปร subject ซง่ึ ค่าท่ี ไดจ้ ะมี 2 สถานะเทา่ นนั้ คอื ON และ OFF แต่ถา้ ตวั แปรดงั กลา่ วไมถ่ กู เลอื ก โปรแกรมจะกาหนดค่า ใหก้ บั ตวั แปร subject เทา่ กบั \"Not set\" ในบรรทดั ท่ี 15 โปรแกรมสงั่ พมิ พข์ อ้ มลู ทเ่ี กบ็ ในตวั แปร subject ผลลพั ธก์ ารทางานของโปรแกรมแสดงในรปู ต่อไปน้ี จากรปู ดา้ นบนแสดงผลการทางานเมอ่ื เรยี กโปรแกรม radio.html ใหผ้ ใู้ ชง้ านคลกิ เลอื กวชิ าใดวชิ าหน่งึ บน radio button ดงั รปู ดา้ นบน และกดป่มุ Select Subject ผลลพั ธแ์ สดงดงั รปู ดา้ นล่าง การส่งข้อมลู ของ Text Area ไปประมวลผลกบั CGI สคริปต์ Text Area ถกู ใชก้ บั งานทต่ี อ้ งการประมวลผลขอ้ ความทม่ี จี านวนมากกวา่ 1 บรรทดั เชน่ ขอ้ มลู ทอ่ี ยอู่ าศยั หรอื ขอ้ ความทต่ี อ้ งการโพสตใ์ นเวบ็ บอรด์ เป็นตน้ ตวั อยา่ งโปรแกรมช่อื textarea.html แสดง การสรา้ งฟอรม์ HTML โดยใช้ Text Area สนบั สนุนการทางาน ภายในโปรแกรมเรยี กใช้ CGI สครปิ ต์ ชอ่ื exam14_6.py ดงั น้ี Program Example textarea.html: HTML form with Text Area ห น้ า 331
1 <form action=\"exam14_6.py\" method=\"post\" target=\"_blank\"> 2 <textarea name=\"textcontent\" cols=\"40\" rows=\"4\"> 3 This program for testing Text Area 4 </textarea> 5 <input type=\"submit\" value=\"Submit\" /> 6 </form> สาหรบั ตวั อยา่ ง CGI สครปิ ตท์ ฝ่ี ังอยใู่ นโปรแกรม textarea.html ดงั น้ี Program Example 14_6: CGI Script for processing Text Area 1 #!/Python34/python 2 import cgi, cgitb 3 form = cgi.FieldStorage() 4 if form.getvalue('textcontent'): 5 text_content = form.getvalue('textcontent') 6 else: 7 text_content = \"Not entered\" 8 9 print(\"Content-type:text/html\\r\\n\\r\\n\") 10 print(\"<html>\") 11 print(\"<head>\") 12 print(\"<title>Text Area for CGI Program</title>\") 13 print(\"</head>\") 14 print(\"<body>\") 15 print(\"<h2> Entered Text Content is %s</h2>\" % text_content) 16 print(\"</body>\") 17 print(\"</html>\") จากตวั อยา่ งโปรแกรมท่ี 14_6 บรรทดั ท่ี 4 โปรแกรมทาการตรวจสอบค่าในตวั แปร textcontent ซง่ึ เป็น ชนดิ Text Area ว่ามกี ารเขยี นขอ้ ความไวห้ รอื ไม่ ถา้ ผใู้ ชง้ านมกี ารเขยี นขอ้ ความไว้ โปรแกรมจะดงึ ค่า ขอ้ ความดงั กลา่ วมาเกบ็ ไวใ้ นตวั แปร text_content แต่ถา้ ตวั แปรดงั กลา่ วไม่มขี อ้ ความใดๆ โปรแกรมจะ กาหนดค่าใหต้ วั แปร text_content เทา่ กบั \"Not entered\" แทน ในบรรทดั ท่ี 15 โปรแกรมสงั่ พมิ พข์ อ้ มลู ทเ่ี กบ็ ในตวั แปร text_content ผลลพั ธข์ องการทางานแสดงในรปู ต่อไปน้ี จากรปู ดา้ นบนแสดงผลการทางานเมอ่ื เรยี กโปรแกรม textarea.html ใหผ้ ใู้ ชง้ านพมิ พข์ อ้ ความใดๆ กไ็ ด้ ลงบน Text Area ดงั รปู ดา้ นบน และกดป่มุ Submit ผลลพั ธแ์ สดงดงั รปู ดา้ นล่าง ห น้ า 332
การส่งข้อมลู ของ Drop Down Box ไปประมวลผลกบั CGI สคริปต์ Drop Down Box ถกู ใชก้ บั งานทต่ี อ้ งการเลอื กคาตอบเพยี ง 1 คาตอบเท่านนั้ จากคาตอบ หลายๆ คาตอบทม่ี อี ยู่ เช่น เลอื กวชิ าเรยี น หรอื เลอื กช่อื จงั หวดั ทอ่ี าศยั เป็นตน้ ตวั อยา่ งโปรแกรมชอ่ื dropdown.html แสดงการสรา้ งฟอรม์ HTML โดยใช้ Drop Down Box ภายในโปรแกรมเรยี กใช้ CGI สครปิ ตช์ ่อื exam14_7.py ดงั น้ี Program Example dropdown.html: HTML form with Drop Down Box 1 <form action=\"exam14_7.py\" method=\"post\" target=\"_blank\"> 2 <select name=\"dropdown\"> 3 <option value=\"Maths\" selected>Maths</option> 4 <option value=\"Physics\">Physics</option> 5 <option value=\"Computer\">Computer</option> 6 </select> 7 <input type=\"submit\" value=\"Submit\"/> 8 </form> สาหรบั ตวั อยา่ ง CGI สครปิ ตท์ ฝ่ี ังอยใู่ นโปรแกรม dropdown.html ดงั น้ี Program Example 14_7: CGI Script for processing Drop Down Box 1 #!/Python34/python 2 import cgi, cgitb 3 form = cgi.FieldStorage() 4 if form.getvalue('dropdown'): 5 subject = form.getvalue('dropdown') 6 else: 7 subject = \"Not entered\" 8 9 print(\"Content-type:text/html\\r\\n\\r\\n\") 10 print(\"<html>\") 11 print(\"<head>\") 12 print(\"<title>Dropdown Box for CGI Program</title>\") 13 print(\"</head>\") 14 print(\"<body>\") 15 print(\"<h2> Selected Subject is %s</h2>\" % subject) 16 print(\"</body>\") 17 print(\"</html>\") จากตวั อยา่ งโปรแกรมท่ี 14_7 บรรทดั ท่ี 4 โปรแกรมทาการตรวจสอบค่าในตวั แปร dropdown ซง่ึ เป็น ชนิด Drop Down Box ว่าไดถ้ ูกเลอื กหรอื ไม่ ถา้ ผใู้ ชเ้ ลอื กขอ้ มลู ใน Drop Down ตวั ใดตวั หน่งึ ไว้ โปรแกรมจะดงึ ค่าขอ้ ความดงั กลา่ วมาเกบ็ ไวใ้ นตวั แปร subject แต่ถา้ ตวั แปรดงั กล่าวไมไ่ ดเ้ ลอื กขอ้ มลู ห น้ า 333
ใดๆ ไว้ โปรแกรมจะกาหนดคา่ ใหต้ วั แปร subject เทา่ กบั \"Not entered\" แทน ในบรรทดั ท่ี 15 โปรแกรมสงั่ พมิ พข์ อ้ มลู ทเ่ี กบ็ ในตวั แปร subject ผลลพั ธก์ ารทางานของโปรแกรมแสดงในรปู ต่อไปน้ี จากรปู ดา้ นบนแสดงผลการทางานเมอ่ื เรยี กโปรแกรม dropdown.html ใหผ้ ใู้ ชง้ านเลอื กวชิ าใดๆ ใน Drop Down Box ดงั รปู ดา้ นบน และกดป่มุ Submit ผลลพั ธแ์ สดงดงั รปู ดา้ นล่าง การใช้คกุ กี้ (Cookie) ใน CGI สคริปต์ Cookie เป็นกลไกอยา่ งหน่ึงทช่ี ว่ ยใหผ้ พู้ ฒั นาโฮมเพจสามารถจะใชเ้ กบ็ สถานะการใชง้ านต่างๆ ของผเู้ ยย่ี มชมได้ โดยปกตแิ ลว้ โพรโทคอล HTTP เป็นโพรโทคอลทม่ี ลี กั ษณะเป็น \"stateless\" คอื ไมม่ ี กลไกเกย่ี วกบั การตรวจสอบสถานะต่างๆ ของผใู้ ชง้ าน Cookies คอื ขอ้ มลู ขนาดเลก็ ทจ่ี ะถกู ส่งไปเกบ็ ไวใ้ นบราวเซอรข์ องผใู้ ชง้ าน เพอ่ื เกบ็ ขอ้ มลู การ เขา้ เยย่ี มชม ชว่ ยใหส้ ามารถตดิ ตามว่าผใู้ ชเ้ ยย่ี มชมหน้าใดบา้ ง เรม่ิ ตน้ จากหน้าไหน หรอื จบลงดว้ ยหน้า ไหน หรอื บ่อยแค่ไหน เป็นตน้ โดย Cookies ไมไ่ ดใ้ ชส้ าหรบั ตรวจสอบว่าผใู้ ชค้ อื ใคร คกุ กี้ทางานอย่างไร คุกกม้ี ขี นั้ ตอนการทางานดงั ต่อไปน้ี 1. ผใู้ ชง้ าน (คนเขา้ เยย่ี มชมเวบ็ ไซต)์ สง่ คารอ้ งขอมายงั เวบ็ เซริ ฟ์ เวอร์ (โดยการเปิด บราวเซอรเ์ ขา้ หน้าเวบ็ ปกต)ิ ดงั รปู ท่ี 14.6 2. เวบ็ เซริ ฟ์ เวอรจ์ ะทาการสรา้ งคกุ ก้ขี น้ึ มาและสง่ กลบั ไปใหก้ บั เวบ็ บราวเซอรข์ องผใู้ ชง้ าน 3. เมอ่ื บราวเซอรใ์ นฝัง่ ของผใู้ ชง้ านยอมรบั คุกก้ี บราวเซอรจ์ ะสรา้ งแฟ้มขนาดเลก็ ใน รปู แบบขอ้ ความธรรมดา (plain text) เกบ็ ไวใ้ นฮารด์ ดสิ กข์ องผใู้ ชง้ าน ตวั อยา่ งขอ้ มลู ห น้ า 334
ของคกุ ก้ี เชน่ .2o7.net TRUE/FALSE1234755376s_vi_nvnwhg [CS]v4|3F09DC8800001DFF-A000A4A00000001|4032DDB1[CE] 4. เมอ่ื เวลาผา่ นไป ผใู้ ชง้ านสง่ คารอ้ งขอมายงั เซริ ฟ์ เวอรอ์ กี (เขา้ เยย่ี มชมเป็นครงั้ ท่ี 2) เวบ็ เซริ ฟ์ เวอรจ์ ะสามารถจดจาไดว้ ่าผใู้ ชง้ านไดท้ ากจิ กรรมใดบา้ งทผ่ี ่านมา รปู ท่ี 14.6 แสดงการทางานของ Cookie รปู แบบของ Cookie ประกอบไปดว้ ย 5 ส่วนเช่อื มต่อกนั คอื Expires: วนั หมดอายขุ องคุกก้ี เมอ่ื ไมไ่ ดก้ าหนดขอ้ มลู ในสว่ นน้ี คุกกจ้ี ะหมดอายทุ นั ทเี มอ่ื ผใู้ ชง้ านปิดบราวเซอร์ Domain: ช่อื โดเมนของเวบ็ ไซต์ Path: ตาแหน่งทอ่ี ยสู่ าหรบั เกบ็ ขอ้ มลู ของคกุ ก้ี ถา้ ไมก่ าหนดไวบ้ ราวเซอรจ์ ะเกบ็ คุกกไ้ี วใ้ นท่ี ใดๆ กไ็ ดใ้ นเครอ่ื งผใู้ ชง้ าน Secure: เมอ่ื กาหนดคา่ เทา่ กบั \"Secure\" คกุ กจ้ี ะทางานรว่ มกบั เวบ็ เซริ ฟ์ เวอรแ์ บบปลอดภยั เท่านัน้ (Secure Server) ถา้ ไมก่ าหนดไวค้ กุ กจ้ี ะไมท่ างานในโหมดปลอดภยั Name=Value: คุกกถ้ี ูกกาหนดใหม้ รี ปู แบบเป็นค่ขู องคยี ก์ บั ขอ้ มลู (Key, Value) การสร้าง Cookie จากทก่ี ล่าวมาแลว้ ว่าคุกกส้ี รา้ งขน้ึ จากฝัง่ ของเวบ็ เซริ ฟ์ เวอร์ และส่งใหก้ บั บราวเซอรข์ องผใู้ ชง้ าน ผา่ นโพรโทคอล HTTP (ซง่ึ อยใู่ นส่วนหวั ของโพรโทคอล HTTP) จากตวั อยา่ งโปรแกรมท่ี 14_8 แสดง การสรา้ งคกุ ก้ี โดยเรยี กใชเ้ มธอด SimpleCookie() ดงั น้ี Program Example 14_8: Creating Simple Cookie 1 #!C:/Python27/python 2 import time, Cookie 3 4 # Instantiate a SimpleCookie object 5 cookie = Cookie.SimpleCookie() 6 ห น้ า 335
7 # The SimpleCookie instance is a mapping 8 cookie['lastvisit'] = str(time.time()) 9 10 # Output the HTTP message containing the cookie 11 print cookie 12 print 'Content-Type: text/html\\n' 13 print '<html><body>' 14 print 'Server time is', time.asctime(time.localtime()) 15 print '</body></html>' จากตวั อยา่ งโปรแกรมท่ี 14_8 สามารถเรยี กใชง้ านไดโ้ ดยพมิ พท์ อ่ี ยใู่ นตาแหน่ง address ของบราวเซอร์ เท่ากบั http://localhost/exam14_8.py บรรทดั ท่ี 1 โปรแกรมนาเขา้ ตวั แปลภาษาคอื ไพธอนเวอรช์ นั 2.7 เขา้ มาทางาน (เน่อื งจากโมดลู Cookie ใชง้ านไดเ้ ฉพาะไพธอน 2.7 สาหรบั เวอรช์ นั 3 ขน้ึ ไปจะใชเ้ มธอด cookies() แทน) บรรทดั ท่ี 2 โปรแกรมนาเขา้ โมดลู time และ Cookie เพ่อื สนบั สนุนการทางานคุกก้ี บรรทดั ท่ี 5 โปรแกรมเรยี กเมธอด SimpleCookie() เพ่อื สรา้ งคกุ กแ้ี ละเกบ็ ไวใ้ นตวั แปรช่อื cookie บรรทดั ท่ี 8 โปรแกรมกาหนดวนั และเวลาลา่ สุดทเ่ี ขา้ เยย่ี มชมเวบ็ ไซต์ บรรทดั ท่ี 11 โปรแกรมสงั่ พมิ พค์ า่ ของคกุ กอ้ี อกจอภาพ ผลลพั ธก์ ารทางานของโปรแกรมแสดงในรปู ต่อไปน้ี เมอ่ื ตอ้ งการแสดงคา่ ขอ้ มลู ของคกุ กท้ี เ่ี กบ็ ในเครอ่ื งของผใู้ ชง้ าน สามารถเขยี นโปรแกรมไดด้ งั น้ี Program Example 14_9: Retrieving Cookie 1 #!C:/Python27/python 2 import Cookie, os, time 3 cookie = Cookie.SimpleCookie() 4 print 'Content-Type: text/html\\n' 5 print '<html><body>' 6 # The returned cookie is available in the os.environ dictionary 7 cookie_string = os.environ.get('HTTP_COOKIE') 8 # The first time the page is run there will be no cookies 9 if not cookie_string: 10 print '<p>First visit or cookies disabled</p>' 11 else: # Run the page twice to retrieve the cookie 12 print '<p>The returned cookie string was \"' + cookie_string + '\"</p>' 13 # load() parses the cookie string 14 cookie.load(cookie_string) 15 # Use the value attribute of the cookie to get it 16 lastvisit = float(cookie['lastvisit'].value) 17 print '<p>Your last visit was at', 18 print time.asctime(time.localtime(lastvisit)), '</p>' 19 print '</body></html>' ห น้ า 336
ตวั อยา่ งโปรแกรมท่ี 14_9 แสดงการดงึ ขอ้ มลู ของคกุ กท้ี อ่ี ยใู่ นเครอ่ื งมาแสดง บรรทดั ท่ี 1 โปรแกรม นาเขา้ โมดลู Cookie, os และ time เขา้ มาใชง้ าน บรรทดั ท่ี 7 โปรแกรมเรยี กใชเ้ มธอด environ.get() ท่ี อยใู่ นโมดลู os โดยมพี ารามเิ ตอร์ 1 ตวั คอื 'HTTP_COOKIE' ขอ้ มลู ทส่ี ่งกลบั มาจากเมธอดดงั กลา่ วคอื ขอ้ มลู ต่างๆ ทเ่ี กย่ี วขอ้ งกบั คุกก้ี ซง่ึ จะถูกเกบ็ ไวใ้ นตวั แปร cookie_string บรรทดั ท่ี 9 โปรแกรมจะ ตรวจสอบขอ้ มลู ในตวั แปร cookie_string ว่ามขี อ้ มลู อยหู่ รอื ไม่ ถา้ ไม่มี (ไมไ่ ดก้ าหนดคา่ cookie ไว)้ โปรแกรมจะพมิ พข์ อ้ ความว่า 'First visit or cookies disabled' แต่ถา้ ตวั แปรดงั กลา่ วมขี อ้ มลู โปรแกรม จะพมิ พข์ อ้ ความว่า 'The returned cookie string was' ตามดว้ ยค่าขอ้ มลู ของคกุ กท้ี เ่ี กบ็ อยใู่ นตวั แปร cookie_string บรรทดั ท่ี 14 โปรแกรมทาการแปลงขอ้ มลู คกุ กใ้ี หผ้ ใู้ ชง้ านสามารถอ่านไดง้ า่ ยโดยใชเ้ มธ อด cookie.load() บรรทดั ท่ี 16 และ 17 โปรแกรมไดน้ าเอาขอ้ มลู ของ lastvisit ทแ่ี ปลงแลว้ มาแสดงผล ทางจอภาพดงั รปู ขา้ งล่าง การอพั โหลดแฟ้มด้วย CGI สคริปต์ การสรา้ งฟอรม์ HTML สาหรบั อพั โหลดแฟ้มขอ้ มลู จะต้องใชแ้ อตทรบิ วิ ต์ multipart/form-data ดงั ตวั อยา่ งโปรแกรมช่อื upload.html Program Example upload.html: HTML form with upload file 1 <html> 2 <body> 3 <form enctype=\"multipart/form-data\" action=\"exam14_10.py\" method=\"post\"> 4 <p>File: <input type=\"file\" name=\"filename\" /></p> 5 <p><input type=\"submit\" value=\"Upload\" /></p> 6 </form> 7 </body> 8 </html> ผลลพั ธก์ ารทางานของโปรแกรม upload.html แสดงดงั รปู ดา้ นลา่ ง ใหผ้ ใู้ ชเ้ ลอื กแฟ้มขอ้ มลู ทต่ี อ้ งการอพั โหลดโดยคลกิ เลอื ก Browse… แลว้ กดป่มุ Upload ห น้ า 337
สาหรบั ตวั อยา่ ง CGI สครปิ ตท์ ท่ี าหน้าทอ่ี พั โหลดแฟ้มทฝ่ี ังอยใู่ นโปรแกรม upload.html ดงั น้ี Program Example 14_10: CGI Script for uploading file 1 #!C:/Python27/python 2 import cgi, os 3 import cgitb; cgitb.enable() 4 form = cgi.FieldStorage() 5 # Get filename here. 6 fileitem = form['filename'] 7 8 # Test if the file was uploaded 9 if fileitem.filename: 10 # strip leading path from file name to avoid 11 # directory traversal attacks 12 fn = os.path.basename(fileitem.filename) 13 open('/tmp/' + fn, 'wb').write(fileitem.file.read()) 14 message = 'The file \"' + fn + '\" was uploaded successfully' 15 else: 16 message = 'No file was uploaded' 17 18 print \"\"\"\\ 19 Content-Type: text/html\\n 20 <html> 21 <body> 22 <p>%s</p> 23 </body> 24 </html> 25 \"\"\" % (message,) จากตวั อยา่ งโปรแกรมท่ี 14_10 บรรทดั ท่ี 4 โปรแกรมทาการดงึ ขอ้ มลู จากฟอรม์ เกบ็ ไวใ้ นตวั แปรชอ่ื form และสกดั ขอ้ มลู จากฟอรม์ ชอ่ื วา่ filename เกบ็ ไวใ้ นตวั แปร fileitem บรรทดั ท่ี 9 โปรแกรม ตรวจสอบตวั แปร fileitem.filename ว่ามชี ่อื แฟ้มทต่ี อ้ งการอพั โหลดหรอื ไม่ ถา้ ผใู้ ชเ้ ลอื กแฟ้มทต่ี อ้ งการ อพั โหลดแลว้ โปรแกรมจะทาการกาหนดทอ่ี ยแู่ บบเตม็ (บรรทดั ท่ี 12) และทาการเปิดแฟ้มเพอ่ื เขยี น ขอ้ มลู ลงในไดเรคทรอร่ี C:\\tmp (บรรทดั ท่ี 13) และพมิ พข์ อ้ ความวา่ 'The file \" ชอ่ื แฟ้ม\" was uploaded successfully' แต่ถา้ ผใู้ ชไ้ มไ่ ดเ้ ลอื กแฟ้มทต่ี อ้ งการอพั โหลดโปรแกรมจะพมิ พข์ อ้ ความว่า 'No file was uploaded' (บรรทดั ท่ี 15) ดงั ตวั อยา่ งในรปู ขา้ งล่าง ห น้ า 338
การประยกุ ต์ CGI สคริปตท์ างานรว่ มกบั ฐานข้อมลู โดยปกตเิ วบ็ ไซตต์ ่างๆ ทเ่ี ปิดใหบ้ รกิ ารในปัจจบุ นั เชน่ รา้ นคา้ ออนไลน์ ธนาคาร เวบ็ บอรด์ จะมี การจดั เกบ็ ขอ้ มลู ของลกู คา้ เอาไว้ เพ่อื ความสะดวกในการทาธรุ กรรมในอนาคต เวบ็ ไซตต์ ่างๆ ทไ่ี ดก้ ล่าว มาแลว้ น้ี นิยมใชฐ้ านขอ้ มลู ในการเกบ็ ขอ้ มลู และใช้ภาษาสครปิ ต์ (VB, PHP, Perl, ASP script) ในการ บรหิ ารจดั การฐานขอ้ มลู อยเู่ บอ้ื งหลงั เชน่ การเพมิ่ การลบ การปรบั ปรงุ และการแสดงผลขอ้ มลู เป็นตน้ ในหวั ขอ้ น้ผี เู้ ขยี นจะแนะนาวธิ กี ารใชง้ านไพธอนสครปิ ต์ในการบรหิ ารจดั การฐานขอ้ มลู เชน่ เดยี วกบั ภาษาสครปิ ตอ์ ่นื ๆ โดยในตวั อยา่ งจะใหล้ กู คา้ ป้อนขอ้ มลู ชอ่ื -สกุล อายุ เพศ และรายไดผ้ ่านทางฟอรม์ HTML จากนนั้ HTML จะเรยี กไพธอนสครปิ ตเ์ พอ่ื ดาเนินการเพม่ิ ขอ้ มลู ลงฐานขอ้ มลู ช่อื DBTest ใน ตารางชอ่ื ว่า employee โปรแกรมชอ่ื employeeForm.html มหี น้าทร่ี บั ขอ้ มลู ของพนกั งาน ประกอบไป ดว้ ย ช่อื (Name), นามสกุล (Surname), อายุ (Age), เพศ (Sex) และรายได้ (Income) จากนนั้ ทาการ เพมิ่ ขอ้ มลู เหลา่ น้ลี งในฐานขอ้ มลู โดยใช้ CGI สครปิ ต์ ชอ่ื exam14_11.py ดงั น้ี Program Example employeeForm.html: 1 <html> 2 <head> 3 <title>User Registration</title> 4 </head> 5 <body> 6 <form action=\"exam14_11.py\" method=\"post\" target=\"_blank\"> 7 <h2>Employee Form</h2> 8 <h3>Name: <input maxlength=\"35\" name=\"name\" size=\"25\" type=\"text\"/></h3> 9 <h3>Surname: <input maxlength=\"35\" name=\"surname\" size=\"25\" type=\"text\"/></h3> 10 <h3>Age: <input maxlength=\"3\" name=\"age\" size=\"5\" type=\"text\"/></h3> 11 <h3>Sex: <strong>Male</strong><input name=\"male\" type=\"radio\"/> Female<input name=\"female\" type=\"radio\" /></h3> 12 <h3>Salary: <select name=\"income\" size=\"1\"><option value=\"20000\">20000</option><option value=\"25000\">25000</option><option value=\"30000\">30000</option></select></h3> 13 </body> 14 <input type=\"submit\" value=\"Submit\" /> ห น้ า 339
15 </form> 16 </html> สครปิ ต์ exam14_11.py แสดงไดด้ งั น้ี Program Example exam14_11.py: 1 #!/Python27/python 2 import cgi, cgitb 3 import MySQLdb 4 5 form = cgi.FieldStorage() 6 if form.getvalue('name'): 7 name = form.getvalue('name') 8 else: 9 name = \"NOT SET\" 10 11 if form.getvalue('surname'): 12 surname = form.getvalue('surname') 13 else: 14 surname = \"NOT SET\" 15 16 if form.getvalue('age'): 17 age = int(form.getvalue('age')) 18 else: 19 age = 0 20 21 if form.getvalue('male'): 22 sex = 'M' 23 elif form.getvalue('female'): 24 sex = \"F\" 25 26 if form.getvalue('income'): 27 income = int(form.getvalue('income')) 28 else: 29 income = 20000 30 31 sql_insert = \"INSERT INTO EMPLOYEE(FIRST_NAME, LAST_NAME, AGE, SEX, INCOME) VALUES('%s', '%s', %d, '%c', %d)\" %(name, surname, age, sex, income) 32 db = MySQLdb.connect(\"127.0.0.1\",\"root\",\"abc123\",\"DBTest\" ) 33 cursor = db.cursor() 34 try: 35 cursor.execute(sql_insert) 36 db.commit() 37 except: 38 db.rollback() 39 db.close() 40 41 print(\"Content-type:text/html\\r\\n\\r\\n\") 42 print(\"<html>\") 43 print(\"<head>\") 44 print(\"<body>\") 45 print(\"<h2>Name is %s</h2>\" % name) 46 print(\"<h2>Surname is %s</h2>\" % surname) 47 print(\"<h2>Age is %s</h2>\" % age) ห น้ า 340
48 print(\"<h2>Sex is %s</h2>\" % sex) 49 print(\"<h2>Income is %s</h2>\" % income) 50 print(\"<h2>--- Inserted list ---</h2>\") 51 print(\"<h2>%s</h2>\" % sql_insert) 52 print(\"</body>\") 53 print(\"</html>\") เมอ่ื เรยี กใชง้ าน employee.html ผลลพั ธท์ ไ่ี ดแ้ สดงดงั รปู ดา้ นลา่ ง ใหผ้ ใู้ ชก้ รอกขอ้ มลู จนครบแลว้ กดป่มุ Submit ดงั น้ี เมอ่ื เพม่ิ ขอ้ มลู เรยี บรอ้ ยแลว้ โปรแกรมจะแสดงผลลพั ธด์ งั น้ี เมอ่ื ตอ้ งการแสดงผลขอ้ มลู ทถ่ี ูกจดั เกบ็ ในฐานขอ้ มลู จะใช้ CGI สครปิ ตช์ ่อื exam14_12.py ในการดงึ ขอ้ มลู มาแสดงผลดงั น้ี Program Example exam14_12.py: 1 #!/Python27/python 2 import cgi, cgitb 3 import MySQLdb 4 5 sql_select = \"SELECT * FROM EMPLOYEE\" 6 db = MySQLdb.connect(\"127.0.0.1\",\"root\",\"abc123\",\"DBTest\" ) ห น้ า 341
7 cursor = db.cursor() 8 9 print(\"Content-type:text/html\\r\\n\\r\\n\") 10 print(\"<html>\") 11 print(\"<head>\") 12 print(\"<body>\") 13 try: 14 count = 1 15 cursor.execute(sql_select) 16 results = cursor.fetchall() 17 for row in results: 18 name = row[0] 19 surname = row[1] 20 age = row[2] 21 sex = row[3] 22 income = row[4] 23 print(\"<h2>----- User[%d] -----</h2>\" % count) 24 print(\"<h2>Name=%s, Surname=%s, Age=%d, Sex=%c, Income=%d</h2>\" % (name, surname, age, sex, income)) 25 count += 1 26 except: 27 print (\"Error: unable to fecth data\") 28 db.close() 29 30 print(\"</body>\") 31 print(\"</html>\") ผลลพั ธข์ องโปรแกรม exam14_12.py แสดงไดด้ งั น้ี จบบทท่ี 14 ห น้ า 342
บทที่ 15 การเขียนโปรแกรมกบั มลั ติเธรดและระบบเครอื ข่าย (Multithread and Network Programming) แอพพลเิ คชนั ทใ่ี ชง้ านกนั อยใู่ นปัจจบุ นั เช่น จดหมายอเิ ลก็ ทรอนกิ ส์ (Hotmail, Gmail, Yahoo), เวบ็ ไซตใ์ หบ้ รกิ ารต่างๆ (Facebook, Instargram, Twisster, Ebay, Amazon, Sanook และอ่นื ๆ), เซริ ช์ เอน็ จนิ (Google, Bring, Yahoo) เป็นตน้ แอพพลเิ คชนั เหล่าน้ี เบอ้ื งหลงั ลว้ นเกดิ ขน้ึ มาจากการเขยี น โปรแกรมโดยใชเ้ ทคนิคของระบบเครอื ขา่ ยและมลั ตเิ ธรดดง้ิ เกอื บทงั้ สน้ิ 1. เธรดและโพรเซส (Threads and Processes) ระบบคอมพวิ เตอรส์ มยั ใหมอ่ นุญาตใหผ้ ใู้ ช้งานสามารถประมวลผลโปรแกรมไดม้ ากกว่า 1 โปรแกรมพรอ้ มๆ กนั โดยเรยี กเทคนิคน้วี ่า มลั ตโิ พรเซสซงิ่ (Multi-Processing/Multi-Threading) ซง่ึ สง่ ผลใหแ้ อพพลเิ คชนั ต่างๆ สามารถรองรบั การเช่อื มต่อและใชง้ านจากผใู้ ชง้ านไดม้ ากกว่า 1 ชอ่ งทาง ตวั อยา่ งเชน่ ผใู้ ชง้ านสามารถใชอ้ เี มล์ (Gmail) ไดพ้ รอ้ มๆ กนั เป็นจานวนมาก (1,000 users ขน้ึ ไป) Process (โพรเซส) คอื โปรแกรมทเ่ี รม่ิ ต้นตงั้ แต่อยใู่ นควิ (Queue) เพ่อื รอการประมวลผลดว้ ย ซพี ยี ู ไปจนถงึ การยุตกิ ารทางาน (Terminate) หรอื อธบิ ายใหเ้ ขา้ ใจงา่ ยๆ คอื เม่อื ผใู้ ชง้ านดบั เบล้ิ คลกิ ท่ี ไอคอนของโปรแกรมใดโปรแกรมหน่งึ ขน้ึ มาทางาน และโปรแกรมดงั กลา่ วทางานไปเรอ่ื ยๆ จนกว่าจะปิด โปรแกรม เรยี กโปรแกรมทก่ี าลงั ทางานนนั้ ว่าโพรเซสนนั่ เอง โดยช่วงชวี ติ ของโพรเซสมอี ยหู่ ลายสถานะ (Process State) ดงั รปู ท่ี 15.1 ห น้ า 343
รปู ท่ี 15.1 แสดงสถานะของโพรเซสและเธรด 1. Ready queue (New) คอื สถานะการเลอื กโปรแกรมเพ่อื นามาสรา้ งเป็นโพรเซส โดยเลอื ก โปรแกรมดงั กล่าวมาจากหน่วยความจาสารอง (ฮารด์ ดสิ ก)์ เมอ่ื โปรแกรมถูกเรยี กเขา้ มาแลว้ ระบบปฏบิ ตั กิ ารจะเปลย่ี นโปรแกรมเป็นโพรเซส โดยการสรา้ ง Process Control Block (PCB) และจดั เตรยี มพน้ื ทห่ี น่วยความจาในการประมวลผล เตรยี มจดั ตารางงาน ใหก้ บั ซพี ยี ู และเตรยี มอุปกรณ์ IO ต่างๆ ทจ่ี าเป็นของโพรเซสเพ่อื ใหท้ างานไดส้ าเรจ็ จากนนั้ โพรเซสจะถกู นาไปเขา้ ควิ (Job Queue) เพ่อื รอประมวลผลในสถานะต่อไป 2. Ready คอื สถานะของโพรเซสทเ่ี ตรยี มเขา้ ไปใชง้ านหน่วยประมวลผลกลาง (ซพี ยี )ู ใน สถานะน้อี าจจะมาจาก New หรอื Waiting หรอื Running กไ็ ด้ กระบวนการทม่ี าจาก New, Waiting, Running จะเขา้ ควิ เพ่อื รอเขา้ ใชห้ น่วยประมวลผลกลาง หรอื เรยี กวา่ Ready Queue กไ็ ด้ 3. Running คอื สถานะของโพรเซสทไ่ี ดเ้ ขา้ ไปใชง้ านหน่วยประมวลผลกลาง ณ เวลาใดเวลา หน่งึ โดยจะมเี พยี ง 1 โพรเซสเท่านนั้ ทส่ี ามารถเขา้ ไปประมวลผลในซพี ยี ไู ด้ เน่อื งจากซพี ยี ู ทางานดว้ ยความเรว็ สงู มาก จงึ ไม่มปี ัญหาในเรอ่ื งการรอคอย 4. Terminate หรอื dead เป็นสถานะโพรเซสทไ่ี ดร้ บั การประมวลผลเสรจ็ เรยี บรอ้ ยแลว้ หรอื โพ รเซสทม่ี กี ารทางานทผ่ี ดิ ปกตเิ กดิ ขน้ึ 5. Waiting เป็นสถานะของโพรเซสทไ่ี ดเ้ ขา้ ไปประมวลผลกบั ซพี ยี แู ลว้ และมกี ารเรยี กใช้ อุปกรณ์รบั - สง่ ขอ้ มลู หรอื อุปกรณ์ต่างๆ ซง่ึ ทรพั ยากรเหลา่ นนั้ ยงั ไมว่ ่าง หรอื มโี พรเซสอ่นื กาลงั ใชง้ านอยู่ โพรเซสเหล่านนั้ จะเปลย่ี นจากสถานะ Running ไปเป็น Waiting หรอื เรยี กวา่ Device Queue หรอื Waiting Queue กไ็ ด้ Thread (เธรด) คอื สว่ นประกอบยอ่ ยของโพรเซส โดยปกตโิ พรเซสทม่ี ี 1 เธรด เรยี กว่า Single thread แต่ถา้ 1 โพรเซสมหี ลายเธรดจะเรยี กวา่ มลั ตเิ ธรด (Multithread) ตวั อยา่ งเชน่ เมอ่ื ผใู้ ชง้ านเปิด โปรแกรมเวบ็ บราวดเ์ ซอร์ (Firefox, Crome, IE) พรอ้ มกบั เรยี กไปยงั เวบ็ ไซตท์ ต่ี อ้ งการ เช่น ห น้ า 344
www.goole.com เรยี กไดว้ ่าเป็น 1 โพรเซส แต่เมอ่ื บราวดเ์ ซอรเ์ รม่ิ ทางานอาจจะแบง่ เป็นหลายๆ เธรด โดยเธรดแรกทาหน้าทโ่ี หลดรปู ภาพจาก google มายงั เครอ่ื งผใู้ ชง้ าน เธรดทส่ี องทาหน้าทโ่ี หลดขอ้ ความ Text และเธรดทส่ี ามทาหน้าทแ่ี สดงผลแอนนิเมชนั บนหน้าบราวดเ์ ซอรข์ องผใู้ ชง้ าน เป็นตน้ เพราะฉะนนั้ ก่อนการเขยี นโปรแกรมกบั ระบบเครอื ข่ายมคี วามจาเป็นตอ้ งเขา้ ใจการทางานของ เธรดก่อนเสมอ เพราะว่าความเขา้ ใจเรอ่ื งเธรดจะช่วยใหผ้ เู้ ขยี นโปรแกรมสามารถเขยี นโปรแกรมกบั ระบบเครอื ขา่ ยไดด้ ขี น้ึ 2. การเขียนโปรแกรมกบั เธรด ไพธอนไดจ้ ดั เตรยี มโมดลู สาหรบั บรหิ ารจดั การกบั เธรดช่อื วา่ threading ผเู้ ขยี นโปรแกรมจะตอ้ ง ขยาย (extend) คลาสช่อื ว่า Thread เขา้ มาทางาน ดงั ตวั อยา่ งโปรแกรมท่ี 15.1 Program Example 15.1: Thread class 1 from threading import Thread 2 3 class myThread(Thread): 4 def __init__(self, name): 5 Thread.__init__(self) 6 self.name = name 7 8 def run(self): 9 print(\"Hello, my name is %s\\n\" %self.getName()) 10 11 process1 = myThread(\"Thread 1\") 12 process2 = myThread(\"Thread 2\") 13 process3 = myThread(\"Thread 3\") 14 process1.start() 15 process2.start() 16 process3.start() ผลลพั ธท์ เ่ี กดิ ขน้ึ จากการรนั โปรแกรมดงั น้ี Hello, my name is Thread 1 Hello, my name is Thread 2 OUTPUT Hello, my name is Thread 3 จากตวั อยา่ งโปรแกรมท่ี 15.1 แสดงการสรา้ งและใชง้ านเธรด โดยบรรทดั ท่ี 1 โปรแกรมนาเขา้ คลาส Thread ทอ่ี ยภู่ ายใตโ้ มดลู threading เขา้ มาทางาน บรรทดั ท่ี 3 สรา้ งคลาสใหมช่ อ่ื MyThread โดยสบื ทอดคณุ สมบตั มิ าจากคลาส Thread บรรทดั ท่ี 4 สรา้ งคอนสตรกั เตอรช์ ่อื __init__(สาหรบั ผอู้ ่านทไ่ี มม่ ี ความรเู้ กย่ี วกบั OOP สามารถอ่านเพม่ิ เตมิ ไดใ้ นบทท่ี 11) ทาหน้าทก่ี าหนดคา่ แอตทรบิ วิ เบอ้ื งตน้ ใหก้ บั คลาส myThread โดยบรรทดั ท่ี 5 ทาการโอเวอรไ์ รดเ์ มธอดคอนสตรกั เตอรข์ องคลาสแม่ (คลาส Thread) ซง่ึ จะตอ้ งทาเสมอ บรรทดั ท่ี 6 กาหนดคา่ ใหแ้ อตทรบิ วิ name ของคลาส myThread จาก อารก์ วิ เมนตท์ ส่ี ่งมาจากการสรา้ งอนิ สแตนซ์ในบรรทดั ท่ี 11 – 13 เมอ่ื โปรแกรมเรยี กใชเ้ มธอด start() กบั เธรดทส่ี รา้ งขน้ึ จะส่งผลใหไ้ พธอนคน้ หาเมธอดทม่ี ชี ่อื วา่ run() ทนั ที ซง่ึ เมธอด run() ในโปรแกรมน้ีจะ ห น้ า 345
ทาหน้าทพ่ี มิ พข์ อ้ ความว่า \"Hello, my name is\" ตามดว้ ยชอ่ื ของเธรด เช่น \"Hello, my name is Thread 1\" บรรทดั ท่ี 11 – 13 โปรแกรมสรา้ งอนิ สแตนซข์ องเธรดช่อื process1, process2 และ process3 พรอ้ มกบั สง่ อารก์ วิ เมนตเ์ ป็นชอ่ื ของเธรด คอื \"Thread 1\", \"Thread 2\" และ \"Thread 3\" ตามลาดบั บรรทดั ท่ี 14 – 16 สงั่ เรมิ่ ตน้ การทางานของเธรด ส่งผลใหโ้ ปรแกรมเรยี กใชเ้ มธอด run() อตั โนมตั ิ 1. โมดลู Threading จากทก่ี ล่าวมาแลว้ ในตอนต้นวา่ การเขยี นโปรแกรมควบคุมเธรดจาเป็นตอ้ งนาเขา้ โมดลู threading เขา้ มาทางานก่อนเสมอ ในหวั ขอ้ น้จี ะกล่าวถงึ เมธอดต่างๆ ทโ่ี มดลู threading เตรยี มไวใ้ ห้ ดงั น้ี เมธอด threading.activeCount() คนื ค่าจานวนของเธรดทก่ี าลงั ทางานอย่ใู นควิ เมธอด threading.currentThread() คนื ค่าจานวนของเธรดทก่ี าลงั ประมวลผลกบั ซพี ยี ู เมธอด threading.enumerate() คนื ค่ารายการของเธรดทงั้ หมดทถ่ี กู สรา้ งขน้ึ มาในระบบ ทงั้ หมด ภายในโมดลู threading มคี ลาสทส่ี าคญั ในการสรา้ งและใชง้ านเธรดคอื คลาสชอ่ื Thread ซง่ึ ใน คลาสดงั กลา่ วมเี มธอดทส่ี าคญั ดงั น้ี เมธอด run() ทาหน้าทเ่ี รม่ิ ตน้ การทางานของเธรด ซง่ึ ผเู้ ขยี นโปรแกรมจะตอ้ งเพมิ่ โปรแกรมตน้ ฉบบั ทต่ี อ้ งการลงในส่วนน้ี เมธอดน้ีจะทางานทนั ทหี ลงั จากมกี ารเรยี กเมธ อด start() เมธอด start() ทาหน้าทก่ี ระตุน้ ใหเ้ ธรดทางาน โดยจะมคี วามสมั พนั ธโ์ ดยตรงกบั เมธอด run() ตามทไ่ี ดก้ ลา่ วมาแลว้ เมธอด join([time]) ทาหน้าทก่ี าหนดเวลาใหก้ บั เธรดก่อนทเ่ี ธรดดงั กล่าวจะยตุ กิ าร ทางาน หรอื พดู งา่ ยๆ คอื กาหนดเวลาเพ่อื ใหย้ ตุ กิ ารทางานนนั่ เอง เมธอด isAlive() ทาหน้าทต่ี รวจสอบว่าเธรดยงั คงทางานอยหู่ รอื ไม่ เมธอด getName() คนื คา่ เป็นช่อื ของเธรด เมธอด setName() กาหนดช่อื ใหก้ บั เธรด สรปุ ขนั้ ตอนการสรา้ งและใช้งานเธรดจากโมดลู threading และคลาส Thread ดงั นี้ 1. นาเขา้ โมดลู threading เชน่ from threading import Thread ห น้ า 346
2. สรา้ งคลาสใหม่และตอ้ งสบื ถอดคณุ สมบตั มิ าจากคลาส Thread เช่น class myClass(Thread): 3. โอเวอรไ์ รดด์ ง้ิ (Overriding) เมธอดคอนสตรกั เตอรข์ องคลาส Thread คอื __init__(self [,args]) ซง่ึ จะมอี ารก์ วิ เมนตห์ รอื ไมก่ ไ็ ด้ (option) เชน่ Thread.__init__(self) 4. โอเวอรไ์ รดด์ ง้ิ เมธอด run() ของคลาส Thread โดยผเู้ ขยี นโปรแกรมจะใชพ้ น้ื ทต่ี รง สว่ นน้ใี นการเขยี นโปรแกรมเพอ่ื ใหเ้ ธรดทางานตามทต่ี อ้ งการ def run(self): \"Please writing your code in here!\" หลงั จากทผ่ี เู้ ขยี นโปรแกรมสรา้ งอนิ สแตนซห์ รอื อ๊อปเจก็ ตข์ องคลาสขน้ึ มาแลว้ จะสงั่ ใหเ้ ธรด ทางานโดยการเรยี กผา่ นเมธอด start() ซง่ึ จะส่งผลใหโ้ ปรแกรมเขา้ ไปทางานในเมธอด run() ทนั ที จาก โปรแกรมตวั อยา่ งท่ี 15.2 แสดงการสรา้ งเธรดจากโมดลู threading เพม่ิ เตมิ จากตวั อยา่ งท่ี 15.1 Program Example 15.2: Thread and sleep 1 from threading import Thread 2 import time 3 4 class myThread (Thread): #Extending Thread class 5 def __init__(self, threadID, name, counter): 6 Thread.__init__(self) #Overriding __init__ method 7 self.threadID = threadID 8 self.name = name 9 self.counter = counter 10 11 def printTime(self, threadName, delay, counter): 12 while counter: 13 time.sleep(delay) 14 print (\"%s: %s\" % (threadName, time.ctime(time.time()))) 15 counter -= 1 16 17 def run(self): #Overriding run method 18 print (\"Starting \" + self.name) 19 self.printTime(self.name, self.counter, 5) 20 print (\"Exiting \" + self.name) 21 22 # Create new threads 23 thread1 = myThread(1, \"Thread-1\", 1) 24 thread2 = myThread(2, \"Thread-2\", 2) 25 26 # Start new Threads 27 thread1.start() 28 thread2.start() 29 ห น้ า 347
30 print (\"Exiting Main Thread\") ผลลพั ธท์ เ่ี กดิ ขน้ึ จากการรนั โปรแกรมดงั น้ี Starting Thread-1 Starting Thread-2 OUTPUT Exiting Main Thread Thread-1: Thu Mar 13 15:49:07 2014 Thread-2: Thu Mar 13 15:49:08 2014 Thread-1: Thu Mar 13 15:49:08 2014 Thread-1: Thu Mar 13 15:49:09 2014 Thread-2: Thu Mar 13 15:49:10 2014 Thread-1: Thu Mar 13 15:49:10 2014 Thread-1: Thu Mar 13 15:49:11 2014 Exiting Thread-1 Thread-2: Thu Mar 13 15:49:12 2014 Thread-2: Thu Mar 13 15:49:14 2014 Thread-2: Thu Mar 13 15:49:16 2014 Exiting Thread-2 ตวั อยา่ งโปรแกรมท่ี 15.2 แสดงการสรา้ งและใชง้ านเธรดอกี รปู แบบหน่งึ โดยเรม่ิ จากบรรทดั ท่ี 1 ทาการ นาเขา้ โมดลู threading เขา้ มาใชง้ าน บรรทดั ท่ี 2 นาเขา้ โมดลู ทจ่ี ดั การดา้ นเวลา (Time) มาทางาน บรรทดั ท่ี 4 สรา้ งคลาสใหม่ช่อื myThread โดยไดร้ บั การสบื ทอดคณุ สมบตั เิ กย่ี วกบั เธรดมาจากคลาสแม่ ชอ่ื Thread บรรทดั ท่ี 5 สรา้ งเมธอดคอนสตรกั เตอรช์ ่อื __init__() ของคลาส myThread ซง่ึ มคี ณุ สมบตั ิ ในการกาหนดคา่ เรมิ่ ตน้ ใหก้ บั คลาส myThread เรมิ่ ตน้ จากการโอเวอรไ์ รดด์ ง้ิ เมธอด __init__ ของคลาส Thread (บรรทดั ท่ี 6) ต่อจากนนั้ กาหนดค่าใหก้ บั แอตทรบิ วิ threadID, name และ counter ในบรรทดั ท่ี 7, 8 และ 9 ตามลาดบั บรรทดั ท่ี 11 สรา้ งเมธอดช่อื วา่ printTime() ทาหน้าทพ่ี มิ พช์ ่อื และเวลาของแต่ละเธรดทก่ี าลงั ทางาน โดยมพี ารามเิ ตอร์ 3 ตวั คอื threadName, delay และ counter ตามลาดบั (ส่วนพารามเิ ตอร์ self นนั้ หมายถงึ การอา้ งคลาสของตวั เอง ถา้ ไมใ่ ส่ใวโ้ ปรแกรมจะเขา้ ใจว่าเป็นการเรยี กเมธอดในคลาสแม่ แทน จะทาใหโ้ ปรแกรมเกดิ ความผดิ พลาด) เมธอด printTime() จะทาการหน่วงเวลาการพมิ พโ์ ดย เรยี กใชเ้ มธอด time.sleep() ซง่ึ เวลาทใ่ี ชห้ น่วงมหี น่วยเป็นวนิ าที (บรรทดั ท่ี 13) คาสงั่ ลปู while ใน โปรแกรมจะทางานไปเรอ่ื ยๆ จนกวา่ คา่ ในตวั แปร counter จะมคี ่าเทา่ กบั 0 บรรทดั ท่ี 14 โปรแกรม เรยี กใชเ้ มธอด time.ctime(time.time()) ซง่ึ จะสง่ ผลใหเ้ วลาทแ่ี สดงมรี ปู แบบเป็น เดอื น วนั วนั ท่ี เวลา ปี เช่น Thu Mar 13 15:49:12 2014 บรรทดั ท่ี 17 โอเวอรไ์ รดด์ ง้ิ เมธอด run() ภายในเมธอดดงั กล่าว โปรแกรมจะสงั่ พมิ พข์ อ้ ความว่า \"Starting \" และตามดว้ ยช่อื ของเธรด เมอ่ื พมิ พข์ อ้ ความเรยี บรอ้ ยแลว้ จะเรยี กใชเ้ มธอด printTime() ซง่ึ ตอ้ งสง่ อารก์ วิ เมนตไ์ ปดว้ ย 3 คา่ คอื ชอ่ื (name), เวลาทใ่ี ชห้ น่วงการ ทางาน (delay) และตวั นบั (counter) การเรยี กเมธอดดงั กลา่ วจะตอ้ งขน้ึ ตน้ ดว้ ย keyword คาวา่ self เสมอ เมอ่ื โปรแกรมกลบั มาจากการทางานในเมธอด prinTime() แลว้ จะพมิ พข์ อ้ ความว่า \"Exiting\" และ ตามดว้ ยช่อื ของเธรดนนั่ ๆ (บรรทดั ท่ี 20) ห น้ า 348
บรรทดั ท่ี 23 และ 24 โปรแกรมสรา้ งอนิ สแตนซข์ องคลาส myThread ชอ่ื วา่ thread1 และ thread2 โดยส่งอารก์ วิ เมนตใ์ หก้ บั คลาส myThread 3 ตวั คอื threadID, name และ counter เมอ่ื สรา้ ง อนิ สแตนซแ์ ลว้ โปรแกรมสงั่ ใหเ้ ธรดทางานโดยเรยี กใชง้ านผ่านเมธอด start() ซง่ึ จะทาใหเ้ มธอด run() ในคลาส myThread ทางานทนั ที ผลลพั ธท์ ไ่ี ดแ้ สดงใหเ้ หน็ วา่ โปรแกรมจะพมิ พข์ อ้ ความสลบั กนั ระหว่าง เธรด 1 และเธรด 2 ไปเรอ่ื ยๆ จนกว่าเธรดจะเสรจ็ สน้ิ การทางาน Note: การสรา้ งอนิ สแตนซจ์ ะส่งผลให้ เมธอด __init__() ในคลาสทางานทนั ที ซง่ึ จะถูกใชใ้ น การเตรยี มสภาพแวดลอ้ มใหพ้ รอ้ มก่อนโปรแกรมทางาน Tips: การเรยี กใชต้ วั แปร (แอตทรบิ วิ ) และเมธอด ใน OOP จะใชค้ านาหน้าดว้ ย self เสมอ มิ เชน่ นัน้ แลว้ โปรแกรมจะเขา้ ใจวา่ เป็นการเรยี กใชแ้ อตทรบิ วิ หรอื เมธอดจากคลาสแม่ 2. ซิงโครไนซเ์ ธรด (Threads Synchronization) จากทก่ี ล่าวมาแลว้ ขา้ งตน้ ว่าโพรเซสสามารถสรา้ งเธรดไดม้ ากกว่า 1 เธรด ซง่ึ เธรดต่างๆ เหล่านนั้ มคี วามสามารถในการทางานไดพ้ รอ้ มๆ กนั หากเธรดเหล่านนั้ ทางานทเ่ี ป็นอสิ ระไมข่ น้ึ ต่อกนั โดยสน้ิ เชงิ จะไมเ่ กดิ ปัญหาใดๆ แต่ในความเป็นจรงิ เธรดเหล่านนั้ ตอ้ งใชท้ รพั ยากรรว่ มกนั ไม่มากกน็ ้อย ดงั นนั้ การทางานของเธรดใดเธรดหน่งึ อาจมผี ลกระทบทางออ้ มกบั เธรดอ่นื ๆ โดยผ่านทางทรพั ยากรท่ี ใชง้ านรว่ มกนั เพ่อื มใิ หส้ ่งผลกระทบและเกดิ ความเสยี หายต่อระบบ จงึ เป็นหน้าทข่ี องระบบปฏบิ ตั กิ ารท่ี จะตอ้ งควบคมุ หรอื หลกี เลย่ี งการทางานของแต่ละเธรดทม่ี าเกย่ี วขอ้ งกนั (Interaction) หน้าทน่ี ้เี รยี กว่า การเขา้ จงั หวะกนั ของเธรด หรอื การซงิ โครไนซเ์ ธรด (Threads Synchronization) โมดลู threading ไดจ้ ดั เตรยี มกลไกสาหรบั จดั การเรอ่ื งซงิ โครไนซเ์ ธรดใหก้ บั ผเู้ ขยี นโปรแกรมไว้ ดงั น้คี อื เมธอด threading.Lock() ทาหน้าทล่ี อ็ คทรพั ยากรทต่ี อ้ งการใชง้ านรว่ มกนั ออ็ ปเจก็ ตท์ ่ี คนื จากเมธอดดงั กลา่ วจะถูกใชใ้ นการควบคมุ ทรพั ยากร acquire([blocking]) เป็นเมธอดทท่ี างานรว่ มกบั ออ็ ปเจก็ ตท์ ส่ี รา้ งจากเมธอด Lock() ทา หน้าทบ่ี งั คบั ใหเ้ ธรดทางานในโหมดซงิ โครไนซ์ โดยมพี ารามเิ ตอร์ 1 ตวั คอื blocking ซง่ึ เป็นอ๊อปชนั (option) ถา้ กาหนดให้ blocking = 0 เมธอด acquire() จะคนื ค่าเป็น 0 ทนั ทเี มอ่ื ไมส่ ามารถลอ็ คทรพั ยากรได้ และคนื ค่าเป็น 1 เมอ่ื สามารถลอ็ คทรพั ยากรได้ เมอ่ื blocking ถกู กาหนดเป็น 1 โปรแกรมจะหยดุ การรอ้ งขอทรพั ยากรทต่ี อ้ งการลอ็ ค จากเธรดอ่นื ๆ ไว้ และจะรอจนกวา่ ทรพั ยากรทต่ี อ้ งการจะถูกปลดปลอ่ ยเพอ่ื ทาการลอ็ ค สรปุ สนั้ ๆ คอื ถา้ กาหนด blocking เป็น 1 โปรแกรมจะรอจนกว่าจะลอ็ คทรพั ยากรได้ ห น้ า 349
เมธอด release() ทาหน้าทป่ี ลดปล่อยทรพั ยากรทโ่ี ปรแกรมลอ็ คหรอื ครอบครองอยู่ การใชง้ านซงิ โครไนซเ์ ธรดแสดงในตวั อยา่ งโปรแกรมท่ี 15.3 Program Example 15.3: Threads Syncronization 1 from threading import Thread 2 import threading 3 import time 4 5 class myThread (Thread): 6 def __init__(self, threadID, name, counter): 7 Thread.__init__(self) 8 self.threadID = threadID 9 self.name = name 10 self.counter = counter 11 12 def printTime(self, threadName, delay, counter): 13 while counter: 14 time.sleep(delay) 15 print (\"%s: %s\" % (threadName, time.ctime(time.time()))) 16 counter -= 1 17 18 def run(self): 19 print (\"Starting \" + self.name) 20 # Get lock to synchronize threads 21 threadLock.acquire() 22 self.printTime(self.name, self.counter, 3) 23 # Free lock to release next thread 24 threadLock.release() 25 26 threadLock = threading.Lock() 27 threads = [] 28 29 # Create new threads 30 thread1 = myThread(1, \"Thread-1\", 1) 31 thread2 = myThread(2, \"Thread-2\", 2) 32 33 # Start new Threads 34 thread1.start() 35 thread2.start() 36 37 # Add threads to thread list 38 threads.append(thread1) 39 threads.append(thread2) 40 41 # Wait for all threads to complete 42 for t in threads: 43 t.join() 44 45 print (\"Exiting Main Thread\") ผลลพั ธท์ เ่ี กดิ ขน้ึ จากการรนั โปรแกรมดงั น้ี ห น้ า 350
Starting Thread-1 Starting Thread-2 OUTPUT Thread-1: Thu Mar 13 18:13:57 2014 Thread-1: Thu Mar 13 18:13:58 2014 Thread-1: Thu Mar 13 18:13:59 2014 Thread-2: Thu Mar 13 18:14:01 2014 Thread-2: Thu Mar 13 18:14:03 2014 Thread-2: Thu Mar 13 18:14:05 2014 Exiting Main Thread ตวั อยา่ งโปรแกรมท่ี 15.3 แสดงการสรา้ งและใชง้ านซงิ โครไนซเ์ ธรด บรรทดั ท่ี 5 และ 12 โปรแกรมสรา้ ง คลาสชอ่ื myThread และ เมธอด printTime() ตามลาดบั ซง่ึ จะทางานเหมอื นโปรแกรมท่ี 15.2 บรรทดั ท่ี 18 โปรแกรมทาการโอเวอรไ์ รดด์ ง้ิ เมธอด run() จากคลาส Thread ซง่ึ เมธอดดงั กล่าวทาหน้าทพ่ี มิ พช์ อ่ื เธรดและเวลาของเธรดนนั้ ๆ ออกจอภาพ ตามทไ่ี ดก้ ล่าวมาแลว้ ขา้ งตน้ วา่ เธรดจะทางานเป็นอสิ ระต่อกนั ดงั นนั้ Thread-1 และ Thread-2 จะเขา้ ใชง้ านเมธอด printTime() สลบั กนั ไป ขน้ึ อย่กู บั ว่าเมธอดใดจะ เขา้ ถงึ เมธอด prinTime() ไดก้ ่อนกนั ดงั นนั้ ในโปรแกรมน้ไี ดอ้ อกคาสงั่ (บรรทดั ท่ี 21) ใหเ้ กดิ การ ซงิ โครไนซเ์ ธรด ซง่ึ หมายความว่าถา้ เธรดใดเธรดหน่งึ ครอบครองเมธอด printTime() แลว้ เธรดนนั้ ๆ จะ ทางานจนกวา่ จะสาเรจ็ โดยใชเ้ มธอด threadLock.acquire() เมอ่ื เธรดทางานเสรจ็ แลว้ จะปลดปล่อยเมธ อด printTime() คนื ส่รู ะบบเพ่อื ใหเ้ ธรดอ่นื ๆ นาไปใชง้ านต่อได้ โดยการเรยี กใช้ threadLock.release() ดงั ในบรรทดั ท่ี 24 และแสดงการทางานในรปู ท่ี 15.2 บรรทดั ท่ี 26 สรา้ งอนิ สแตนซช์ ่อื threadLock เพ่อื ใชส้ าหรบั ทาซงิ โครไนซเ์ ธรด บรรทดั ท่ี 27 สรา้ งตวั แปรลสิ ตช์ อ่ื threads เพ่อื ใชส้ าหรบั เกบ็ ออ็ ปเจก็ ตข์ องเธรดทงั้ หมดทส่ี รา้ งขน้ึ บรรทดั ท่ี 30 โปรแกรมสรา้ งอนิ สแตนซจ์ ากคลาส myThread ช่อื thread1 และ thread2 ตามลาดบั บรรทดั ท่ี 34 ออก คาสงั่ กระตุ้นใหเ้ ธรดทางานผ่านเมธอด start() บรรทดั ท่ี 38 เพมิ่ เธรดลงในตวั แปรชนดิ ลสิ ต์ บรรทดั ท่ี 42 โปรแกรมใชล้ ูป for ดงึ ออ็ ปเจก็ ตเ์ ธรดออกจากตวั แปร threads และใชเ้ มธอด join() เพอ่ื กาหนดใหเ้ ธรด ทงั้ หมดรอเพอ่ื จบการทางานพรอ้ มๆ กนั Thread-1 Thread-2 wait threadLock.acquire() Resource def printTime() threadLock.release() รปู ท่ี 15.2 แสดงการซงิ โครไนซเ์ ธรดระหว่าง Thread-1 และ Thread-2 ห น้ า 351
3. การจดั ลาดบั ความสาคญั ของคิวสาหรบั มลั ติเธรด (Multithreaded Priority Queue) โมดลู Queue อนุญาตใหผ้ เู้ ขยี นโปรแกรมสามารถสรา้ งควิ และกาหนดลาดบั การทางานของ เธรดในควิ ไดด้ ว้ ยตนเอง ซง่ึ มเี มธอดใหใ้ ชง้ านดงั น้ี เมธอด get() ดงึ ขอ้ มลู ออกจากควิ put() นาขอ้ มลู เขา้ ส่คู วิ qsize() คนื ค่าจานวนของขอ้ มลู ทงั้ หมดทอ่ี ยใู่ นควิ empty() ตรวจสอบวา่ ควิ ว่างหรอื ไม่ คา่ ทส่ี ง่ คนื เท่ากบั 0 แสดงวา่ ควิ วา่ ง ค่าอ่นื ๆ แสดง ว่าควิ ไมว่ า่ ง full() ตรวจสอบว่าควิ เตม็ หรอื ไม่ ค่าทส่ี ่งคนื เท่ากบั 0 แสดงวา่ ควิ เตม็ คา่ อ่นื ๆ แสดงว่า ควิ ไมเ่ ตม็ การสรา้ งและใชง้ านควิ แสดงในตวั อยา่ งโปรแกรมท่ี 15.4 Program Example 15.4: Multithreaded Priority Queue 1 from threading import Thread 2 import threading 3 import queue 4 import time 5 6 exitFlag = 0 7 8 class myThread(Thread): 9 def __init__(self, threadID, name, q): 10 Thread.__init__(self) 11 self.threadID = threadID 12 self.name = name 13 self.q = q 14 15 def run(self): 16 print(\"Starting \" + self.name + \"\\n\") 17 self.process_data(self.name, self.q) 18 print(\"Exiting \" + self.name + \"\\n\") 19 20 def process_data(self, threadName, q): 21 while not exitFlag: 22 queueLock.acquire() 23 if not workQueue.empty(): 24 data = q.get() 25 queueLock.release() 26 print(\"%s processing %s\\n\" % (threadName, data)) 27 else: 28 queueLock.release() 29 time.sleep(1) ห น้ า 352
30 31 threadList = [\"Thread-1\", \"Thread-2\", \"Thread-3\"] 32 nameList = [\"One\", \"Two\", \"Three\", \"Four\", \"Five\"] 33 queueLock = threading.Lock() 34 workQueue = queue.Queue(10) 35 threads = [] 36 threadID = 1 37 38 # Create new threads 39 for tName in threadList: 40 thread = myThread(threadID, tName, workQueue) 41 thread.start() 42 threads.append(thread) 43 threadID += 1 44 45 # Fill the queue 46 queueLock.acquire() 47 for word in nameList: 48 workQueue.put(word) 49 queueLock.release() 50 51 # Wait for queue to empty 52 while not workQueue.empty(): 53 pass 54 55 # Notify threads it's time to exit 56 exitFlag = 1 57 58 # Wait for all threads to complete 59 for t in threads: 60 t.join() 61 print(\"Exiting Main Thread\") ผลลพั ธท์ เ่ี กดิ ขน้ึ จากการรนั โปรแกรมดงั น้ี Starting Thread-1 Starting Thread-3 OUTPUT Starting Thread-2 Thread-1 processing One Thread-3 processing Two Thread-2 processing Three Thread-1 processing Four Thread-3 processing Five Exiting Thread-2 Exiting Thread-1 Exiting Thread-3 Exiting Main Thread ตวั อยา่ งโปรแกรมท่ี 15.4 แสดงการสรา้ งและใชง้ านควิ สาหรบั มลั ตเิ ธรด บรรทดั ท่ี 3 โปรแกรมนาเขา้ โมดลู queue เพ่อื ใชส้ าหรบั สรา้ งควิ ในการบรหิ ารจดั การเธรด บรรทดั ท่ี 6 กาหนดตวั แปร exitFlag เพอ่ื ใชส้ าหรบั ควบคุมเพอ่ื ใหเ้ ธรดยตุ กิ ารทางาน ในเบอ้ื งตน้ กาหนดค่าใหก้ บั ตวั แปรดงั กล่าวเท่ากบั 0 บรรทดั ท่ี 8 สรา้ งคลาสชอ่ื myThread โดยมพี ารามเิ ตอร์ 3 ตวั คอื threadID, Name และ q (queue) ห น้ า 353
บรรทดั ท่ี 15 – 18 โปรแกรมโอเวอรไ์ รดด์ ง้ิ เมธอด run() จากคลาส Thread ทาหน้าทพ่ี มิ พข์ อ้ ความวา่ \"Starting\" ตามดว้ ยช่อื เธรด จากนนั้ โปรแกรมเรยี กใชง้ านเมธอด process_data() เมธอดดงั กล่าว ตอ้ งการพารามเิ ตอร์ 2 ตวั คอื ช่อื เธรด (threadName) และควิ (Queue) เมอ่ื เมธอด process_data() ทางานเสรจ็ แลว้ โปรแกรมจะพมิ พข์ อ้ ความวา่ \"Exiting\" ตามดว้ ยช่อื ของเธรด บรรทดั ท่ี 20 - 29 ฟังชนั ชอ่ื process_data() ทาหน้าทด่ี งึ เธรดออกมาจากควิ (workQueue) และนามาประมวลผล เป็นทท่ี ราบกนั ดแี ลว้ ว่าเธรดจะแข่งขนั กนั เขา้ ใชท้ รพั ยากร (ในทน่ี ้คี อื workQueue) ดงั นนั้ ฟังชนั ดงั กล่าวจงึ ใชเ้ มธอด queueLock.acquire() เพอ่ื รอ้ งขอการเขา้ ใชค้ วิ ถา้ เธรดใดเธรดหน่งึ ครอบครองควิ ไดส้ าเสรจ็ เธรดอ่นื ๆ จะตอ้ งรอคอยจนกว่าเธรดทใ่ี ชง้ านควิ อยจู่ ะทางานเสรจ็ ก่อน เมอ่ื เธรดเขา้ ใชง้ านควิ จะเรยี กใชเ้ มธอด workQueue.empty() เพอ่ื ตรวจสอบก่อนว่ามขี อ้ มลู อยใู่ นควิ หรอื ไม่ ถา้ ไม่มโี ปรแกรมจะปลดปล่อยควิ แต่ถา้ มขี อ้ มลู อยใู่ นควิ โปรแกรมจะใชเ้ มธอด q.get() เพ่อื ดงึ ขอ้ มลู ใน ควิ ออกมาทางาน เมอ่ื เธรดทางานเสรจ็ แลว้ จะปลดปล่อยควิ (workQueue) คนื ใหก้ บั เธรดอ่นื ๆ ไดใ้ ชง้ าน บา้ ง โดยใชเ้ มธอด queueLock.release() บรรทดั ท่ี 31 สรา้ งชอ่ื เธรดเกบ็ ไวใ้ นตวั แปร threadList โดยมอี ยู่ 3 ช่อื คอื \"Thread-1\", \"Thread- 2\" และ \"Thread-3\" บรรทดั ท่ี 32 กาหนดขอ้ มลู เพ่อื เกบ็ ในควิ ประกอบดว้ ย \"One\", \"Two\", \"Three\", \"Four\" และ \"Five\" บรรทดั ท่ี 33 สรา้ งออ็ ปเจก็ ตช์ ่อื queueLock ทาหน้าทล่ี อ็ คและปลดปลอ่ ยทรพั ยากร ทเ่ี ธรดแขง่ ขนั กนั ใชง้ าน (workQueue) บรรทดั ท่ี 34 สรา้ งควิ ชอ่ื workQueue ทส่ี ามารถเกบ็ ขอ้ มลู ได้ 10 ออ็ ปเจก็ ต์ บรรทดั ท่ี 36 สรา้ งตวั แปร threadID เพอ่ื ใชก้ าหนดจานวนเธรดทจ่ี ะรนั ในโปรแกรม บรรทดั ท่ี 39 – 43 โปรแกรมสรา้ งอนิ สแตนซข์ องคลาส myThread ซง่ึ มพี ารามเิ ตอร์ 3 ตวั คอื threadID, tName และ workQueue ในทน่ี ้โี ปรแกรมสรา้ งเธรดขน้ึ มา 3 เธรดคอื ThreadID = 1 มชี อ่ื ว่า Thread-1, ThreadID = 2 มชี อ่ื ว่า Thread-2, ThreadID = 3 มชี ่อื ว่า Thread-3 จากนนั้ โปรแกรมสงั่ ให้ เธรดเหลา่ น้ที างานทนั ทดี ว้ ยเมธอด start() และโปรแกรมทาการเพม่ิ เธรดเหล่าน้ลี งในลสิ ตข์ องเธรดชอ่ื threads บรรทดั ท่ี 46 – 49 โปรแกรมทาการเพม่ิ ขอ้ มลู ลงในควิ (workQueue) แต่เป็นไปไดข้ ณะเมอ่ื โปรแกรมกาลงั เพมิ่ ขอ้ มลู ลงในควิ ดงั กล่าว ปรากฎวา่ เธรดทส่ี งั่ ใหท้ างานไปแลว้ อาจจะเขา้ มาอ่านขอ้ มลู ในควิ พรอ้ มๆ กบั การเพม่ิ ขอ้ มลู ลงในควิ เช่นเดยี วกนั ดงั นนั้ โปรแกรมจาเป็นตอ้ งเรยี กใชง้ านเมธอด queueLock.acquire() เพอ่ื ครอบครองควิ และทาการเพมิ่ ขอ้ มลู ใหเ้ สรจ็ ก่อน เมอ่ื ทางานเสรจ็ แลว้ จงึ ปลดปลอ่ ยควิ ดว้ ยเมธอด queueLock.release() เพอ่ื ใหเ้ ธรดอ่นื ๆ สามารถใชง้ านควิ ต่อได้ เมอ่ื โปรแกรมใสข่ อ้ มลู ในควิ เสรจ็ เรยี บรอ้ ยแลว้ โปรแกรมจะทางานต่อในบรรทดั ท่ี 53 (ในขณะน้ี เธรดอ่นื ๆ กย็ งั คงทางานอย่เู ชน่ เดยี วกนั ) โปรแกรมจะประมวลผลคาสงั่ while โดยมเี งอ่ื นไขวา่ โปรแกรมจะวนทางานไปเรอ่ื ยๆ จนกวา่ ควิ (workQueue) จะว่าง โปรแกรมจงึ จะยตุ กิ ารทางานในลปู while นนั่ หมายถงึ วา่ เธรดต่างๆ ทท่ี างานอยไู่ ดด้ งึ ขอ้ มลู ในควิ ไปทางานจนหมดแลว้ นนั่ เอง ลปู while ห น้ า 354
จงึ ยตุ กิ ารทางาน เมอ่ื ควิ วา่ งแลว้ สง่ ผลใหโ้ ปรแกรมหลุดจากลปู while (แต่เธรด \"Thread-1\", \"Thread-2\", \"Thread-3\" ยงั คงทางานอย)ู่ แลว้ มาทางานในบรรทดั ท่ี 56 ซง่ึ เป็นการกาหนดค่าใหก้ บั ตวั แปรชอ่ื exitFlag=1 ส่งผลใหเ้ ธรดยตุ กิ ารทางาน (บรรทดั ท่ี 21) บรรทดั ท่ี 59 โปรแกรมทาการวนลปู พรอ้ มกบั ใชเ้ มธอด join() เพอ่ื กาหนดใหเ้ ธรดทุกๆ ตวั ยตุ ิ การทางานพรอ้ มกนั (ถกู Terminate พรอ้ มกนั ) 3. การเขียนโปรแกรมเครือข่ายแบบไคลเอน็ ต์-เซิรฟ์ เวอร์ (Clients-Servers) สถาปัตยกรรมแบบไคลเอน็ ต-์ เซฟิ เวอร์ ประกอบไปดว้ ย 2 สว่ นคอื ระบบคอมพวิ เตอรท์ ม่ี ี ประสทิ ธภิ าพสงู ทาหน้าทเ่ี ป็นเซฟิ เวอร์ ซง่ึ จะใหบ้ รกิ ารอย่ตู ลอดเวลา และเครอ่ื งไคลเอน็ ต์ (Desktop PC, Notebook, smartphone, mobile-phone) ซง่ึ จะทาหน้าทร่ี อ้ งขอบรกิ าร ไคลเอน็ ตอ์ าจจะเป็นเครอ่ื ง ทท่ี างานตลอดเวลาหรอื ไมก่ ไ็ ดแ้ ละไมส่ ามารถสอ่ื สารกนั เองได้ สาหรบั เครอ่ื งเซฟิ เวอรจ์ ะตอ้ งมหี มายเลข ไอพที ค่ี งท่ี (Fixed IP Address) รปู ท่ี 15.3 แสดงสถาปัตยกรรมแบบไคลเอน็ ต์-เซริ ฟ์ เวอร์ จากรปู เครอ่ื ง ทท่ี าหน้าทเ่ี ป็นเวบ็ เซฟิ เวอร์ (Web Server) จะคอยตอบสนองต่อการรอ้ งขอจากเครอ่ื งไคลเอน็ ตท์ ่ี ทางานดว้ ยโปรแกรมเวบ็ บราวดเ์ ซอร์ เป็นตน้ รปู ท่ี 15.3 แสดงสถาปัตยกรรมแบบไคลเอน็ ต์-เซริ ฟ์ เวอร์ 1. หมายเลขไอพีแอดเดรส (IP Address) คอมพวิ เตอรท์ เ่ี ชอ่ื มต่อกบั ระบบเครอื ขา่ ยจาเป็นตอ้ งมหี มายเลขไอพแี อดเดรสเพอ่ื ใช้ สาหรบั ตดิ ต่อสอ่ื สารกนั โดยหมายเลขไอพดี งั กลา่ วตอ้ งไมซ่ ้ากนั เลย (Unique) สาหรบั รปู แบบ ของไอพแี อดเดรสคอื ddd.ddd.ddd.ddd โดย d คอื เลขฐานสบิ และไอพแี อดเดรสแต่ละกลุม่ ห น้ า 355
(ddd) จะมคี ่าระหวา่ ง 0 - 255 ตวั อยา่ งเช่น 192.168.5.10, 136.112.195.77 เป็นตน้ แต่การ จดจาหมายเลขไอพแี อดเดรสเป็นเรอ่ื งยากเน่อื งจากมคี วามยาวมาก ดงั นนั้ จงึ นยิ มเรยี กชอ่ื เครอ่ื งแทน เชน่ John-PC, Computer001 เป็นตน้ สาหรบั การเขยี นโปรแกรมเพ่อื แสดงขอ้ มลู ไอพแี อดเดรสและช่อื เครอ่ื ง ไพธอนไดจ้ ดั เตรยี มเครอ่ื งมอื ไวใ้ หค้ อื โมดลู socket ดงั น้ี เมธอด gethostname() แสดงช่อื เครอ่ื งคอมพวิ เตอรท์ ก่ี าลงั ทางานอยู่ ถา้ คอมพวิ เตอรไ์ มไ่ ดก้ าหนดช่อื ไว้ เมธอดดงั กลา่ วจะแสดงขอ้ ความผดิ พลาดออกมา (exception) เชน่ >>> from socket import * >>> gethostname() 'K-PC' เมธอด gethostbyname(ipName) แสดงหมายเลขไอพแี อดเดรสของเครอ่ื งทก่ี าลงั ทางานอยู่ โดย ipName คอื ชอ่ื ของเครอ่ื งคอมพวิ เตอร์ ถา้ กาหนดชอ่ื เครอ่ื งไมถ่ ูก โปรแกรมจะแสดงขอ้ ผดิ พลาดออกมา เช่น >>> gethostbyname(gethostname()) '192.168.1.104' >>> gethostbyname('John') Traceback (most recent call last): File \"<pyshell#11>\", line 1, in <module> gethostbyname('John') socket.gaierror: [Errno 11001] getaddrinfo failed ชอ่ื เครอ่ื งทใ่ี ชส้ าหรบั ทดสอบการทางานของโปรแกรมเบอ้ื งตน้ เรยี กวา่ \"localhost\" มี หมายเลขไอพแี อดเดรสคอื 127.0.0.1 หรอื เรยี กว่า IP Loopback กไ็ ด้ หมายเลขไอพดี งั กลา่ วจะ ไมส่ ามารถเช่อื มต่อกบั ระบบเครอื ขา่ ยได้ แต่มหี น้าทส่ี าหรบั ทดสอบการทางานของแอพพลเิ ค ชนั ทพ่ี ฒั นาขน้ึ ก่อนการนาไปใชง้ านจรงิ เท่านนั้ รปู แบบคาสงั่ ทใ่ี ชแ้ สดงไอพี localhost คอื >>> gethostbyname('localhost') '127.0.0.1' 2. พอรต์ , เซิรฟ์ เวอร์ และไคลเอน็ ต์ (Ports, Servers and Clients) พอรต์ คอื ช่องทางการเช่อื มต่อระหวา่ งเครอ่ื งเซริ ฟ์ เวอรแ์ ละไคลเอน็ ต์ เปรยี บเสมอื นท่าเรอื ขนสง่ สนิ คา้ ซง่ึ เรอื ทุกๆ ลาจะตอ้ งนาเรอื สนิ ค้ามาจอดทท่ี ่าเรอื (เปรยี บเสมอื นพอรต์ ) ก่อนจากนนั้ กจ็ งึ ทา การขนถ่ายสนิ คา้ (เปรยี บเสมอื นขอ้ มลู ) ได้ พอรต์ จะถูกกาหนดเป็นเลขจานวนเตม็ มคี ่าระหว่าง 0 – 65535 โดยพอรต์ ตงั้ แต่ 0 – 1024 ถูกจองไวเ้ พ่อื ใชง้ านพเิ ศษหรอื เรยี กวา่ (Well known ports) เชน่ ทา หน้าทเ่ี ป็นเวบ็ เซริ ฟ์ เวอร์ (พอรต์ 80), จดหมายอเิ ลก็ ทรอนิกส์ (25), โดเมนเนมเซริ ฟ์ เวอร์ (53) เป็นตน้ ดงั นนั้ หมายเลขพอรต์ ตงั้ แต่ 0 – 1024 จงึ ไมค่ วรนาไปใชใ้ นการเขยี นโปรแกรม สาหรบั เครอ่ื งไคลเอน็ ต์ ห น้ า 356
โดยปกตเิ มอ่ื มกี ารส่อื สารจะตอ้ งใชพ้ อรต์ เชน่ เดยี วกบั เครอ่ื งเซริ ฟ์ เวอร์ แต่ระบบปฏบิ ตั กิ ารจะสุ่มเลอื ก ขน้ึ มาใหเ้ องอตั โนมตั ิ การเขยี นโปรแกรมเช่อื มต่อระหวา่ งเซริ ฟ์ เวอรแ์ ละไคลเอน็ ตจ์ ะใชโ้ มดลู Socket ใน การทางาน ซง่ึ Socket จะทาหน้าทส่ี รา้ งลงิ คเ์ พอ่ื เช่อื มต่อระหวา่ งโพรเซสทท่ี างานอยใู่ นฝัง่ เซริ ฟ์ เวอรก์ บั ไคลเอน็ ตใ์ นลกั ษณะโฮสต์ (Host) ต่อโฮสต์ (เซริ ฟ์ เวอร์ 1 เครอ่ื งกบั ไคลเอน็ ต์ 1 เคร่อื ง) เรยี กวา่ การเปิด ซอ็ กเกต็ (Socket) หรอื เปิดพอรต์ กไ็ ด้ ผเู้ ขยี นโปรแกรมสามารถเปิด Socket ไดม้ ากกว่า 1 Socket บน หมายเลขพอรต์ เดยี วกนั ได้ (เหมอื นท่าเรอื ทส่ี ามารถรองรบั การจอดเรอื ไดห้ ลายลา) โดยปกตแิ ลว้ ความเรว็ ในการสอ่ื สารระหวา่ งไคลเอน็ ตแ์ ละเซริ ฟ์ จะแตกต่างกนั เน่อื งจากเซริ ฟ์ เวอรจ์ ะมปี ระสทิ ธภิ าพ การทางานทส่ี งู กว่าไคลเอน็ ตม์ าก และเซริ ฟ์ เวอรจ์ ะตอ้ งรองรบั การรอ้ งขอจากไคลเอน็ ตเ์ ป็นจานวนมาก ในเวลาเดยี วกนั ส่งผลใหก้ ารสอ่ื สารระหว่างเซริ ฟ์ เวอรแ์ ละไคลเอน็ ตอ์ าจจะเกดิ ปัญหาในการรบั ส่งขอ้ มลู ได้ ดงั นนั้ เพ่อื แกป้ ัญหาดงั กล่าวจงึ ใชบ้ ฟั เฟอร์ (Buffer) เขา้ มาชว่ ยในการทางาน ถา้ จะอธบิ ายใหง้ า่ ยๆ บฟั เฟอรค์ อื การจดั เรยี งขอ้ มลู ไวใ้ นพน้ื ทท่ี ไ่ี ดจ้ ดั เตรยี มไวก้ ่อน เมอ่ื เครอ่ื งคอมพวิ เตอรพ์ รอ้ มทางานแลว้ จะนาขอ้ มลู ในบฟั เฟอรไ์ ปทางานนัน่ เอง จากรปู ท่ี 15.4 แสดงความสมั พนั ธร์ ะหว่างเซริ ฟ์ เวอร,์ ไคลเอน็ ต์ พอรต์ , Sockets และ บฟั เฟอร์ Server (Host) Port Buffer (5000) Socket SocketSocket Port Client 1 Port Port (1234) (1234) (3500) Client 3 Client 2 รปู ท่ี 15.4 แสดงความสมั พนั ธร์ ะหว่าง Server, Clients, Port และ Sockets 3. เร่ิมต้นการเขียนโปรแกรมเซิรฟ์ เวอร-์ ไคลเอน็ ต์ โดยปกตแิ ลว้ เครอ่ื งคอมพวิ เตอรท์ กุ ๆ เครอ่ื งจะเปิดพอรต์ ทท่ี าหน้าทต่ี ่างๆ ไวม้ ากมาย ผใู้ ชง้ าน สามารถตรวจสอบการเปิดพอรต์ บนเครอ่ื งของตนเองโดยใชค้ าสงั่ netstate ใน Command rompt บน DOS ดงั น้ี ห น้ า 357
เรยี กโปรแกรม Command prompt บน DOS โดยคลกิ ท่ี Start run พมิ พ์ คาสงั่ cmd และกดป่มุ Enter ผลลพั ธท์ ไ่ี ดแ้ สดงในรปู ดา้ นลา่ ง พมิ พค์ าสงั่ netstat –na ท่ี Command prompt และกดป่มุ Enter จากรปู ดา้ นบนตรงส่วนท่ี Highlight ไวค้ อื หมายเลขพอรต์ ต่างๆ ทเ่ี ครอ่ื งคอมพวิ เตอรเ์ ปิดไว้ การเขียนโปรแกรมฝัง่ ไคลเอน็ ต์ ในตวั อยา่ งน้ผี เู้ ขยี นจะแนะนาการเขยี นโปรแกรมเพอ่ื ดงึ วนั และเวลาจากเครอ่ื งเซริ ฟ์ เวอรม์ า แสดงผลในเครอ่ื งไคลเอน็ ต์ โดยอาศยั โมดลู Socket ชว่ ยในการเขยี นโปรแกรม ซง่ึ ขนั้ ตอนการเขยี น โปรแกรมฝัง่ ไคลเอน็ ตด์ งั น้คี อื 1) สรา้ ง Socket (ออ็ ปเจก็ ตซ์ ๊อกเกต็ ) 2) เปิด Socket ดว้ ยพอรต์ ทส่ี ามารถใชง้ านได้ (แนะนาใหใ้ ชพ้ อรต์ ตงั้ แต่ 1,024 ขน้ึ ไป) ใน เครอ่ื งผใู้ ชเ้ อง (Localhost) ในทน่ี ้ีจะใชพ้ อรต์ หมายเลข 5,000 3) ดงึ วนั และเวลาจากเครอ่ื งเซริ ฟ์ เวอรผ์ า่ น Socket 4) แสดงผลวนั และเวลาออกจอภาพ สาหรบั ตวั อยา่ งโปรแกรมฝัง่ ไคลเอน็ ตแ์ สดงไดด้ งั น้ี Program Example 15.5: Easy Client Script (receive day/time) 1 ''' Client for obtaining the day and time from localhost.''' 2 from socket import * ห น้ า 358
3 4 HOST = 'localhost' 5 PORT = 5000 6 BUFFER_SIZE = 1024 7 ADDRESS = (HOST, PORT) #(127.0.0.1, 5000) 8 9 server = socket(AF_INET, SOCK_STREAM) #Create a socket 10 server.connect(ADDRESS) #Connect it to a host 11 data_bytes = server.recv(BUFFER_SIZE) #Read a string from it 12 dayAndTime = bytes.decode(data_bytes) 13 print(dayAndTime) 14 server.close() #Close the connection จากตวั อยา่ งโปรแกรมท่ี 15.5 แสดงโปรแกรมฝัง่ ไคลเอน็ ต์ ทาหน้าทร่ี อ้ งขอวนั และเวลาบนเครอ่ื ง เซริ ฟ์ เวอรม์ าแสดงผล บรรทดั ท่ี 2 นาเขา้ โมดลู Socket โดยเลอื กคลาส, เมธอดทงั้ หมด (*) ทอ่ี ยใู่ นโมดลู ดงั กล่าวเขา้ มาทางาน บรรทดั ท่ี 4 กาหนดค่าใหก้ บั ตวั แปร HOST เท่ากบั 'localhost' (127.0.0.1) บรรทดั ท่ี 5 กาหนดคา่ หมายเลขพอรต์ (PORT) เท่ากบั 5000 ซง่ึ หมายเลขพอรต์ ดงั กลา่ วอยบู่ นฝัง่ เซริ ฟ์ เวอร์ บรรทดั ท่ี 6 กาหนดขนาดของบฟั เฟอรเ์ ท่ากบั 1024 โดยมหี น่วยเป็นไบต์ (Bytes) บรรทดั ท่ี 7 กาหนดค่าใหก้ บั ตวั แปร ADDRESS มคี ่าไอพเี ท่ากบั 127.0.0.1 และหมายเลขพอรต์ เท่ากบั 5000 (ใน ตวั อยา่ งน้ี เซริ ฟ์ เวอรแ์ ละไคลเอน็ ตจ์ ะทางานอยใู่ นเครอ่ื งเดยี วกนั ) บรรทดั ท่ี 9 สรา้ งซอ็ กเกต็ คอนเน็คชนั ดว้ ยคลาส socket โดยคลาสดงั กลา่ วตอ้ งการพารามเิ ตอร์ 2 ตวั คอื AF_INET ซง่ึ เป็นมาตรฐานการ เชอ่ื มต่อของโพรโทรคอล TCP/IP และ SOCK_STREAM คอื การเช่อื มต่อเหมอื นกบั การสรา้ งท่อจาลอง (Pipe) ขอ้ มลู ทร่ี บั และส่ง โดยขอ้ มลู จะไหลในท่อดงั กล่าวอยา่ งต่อเน่อื ง บรรทดั ท่ี 10 สงั่ ใหเ้ กดิ การ เช่อื มต่อระหวา่ งไคลเอน็ ตแ์ ละเซริ ฟ์ เวอรด์ ว้ ยหมายเลขไอพแี อดเดรสและพอรต์ ทก่ี าหนดไวใ้ น ADDRESS บรรทดั ท่ี 11 ไคลเอน็ ตร์ อ้ งขอขอ้ มลู จากเซริ ฟ์ เวอรด์ ว้ ยเมธอด server.recv(BUFFER_SIZE) โดย BUFFER_SIZE คอื ขนาดของบฟั เฟอรท์ ฝ่ี ัง่ ไคลเอน็ ตจ์ ะรบั ขอ้ มลู ได้ สงู สดุ ในแต่ละรอบ คา่ ทส่ี ง่ กลบั มาจากเซริ ฟ์ เวอรค์ อื วนั และเวลาเกบ็ ไวใ้ นตวั แปรชอ่ื data_bytes แต่ เน่อื งจากขอ้ มลู ทส่ี ่งและรบั ของ Socket ในไพธอน 3.0 ขน้ึ ไปจะใชข้ อ้ มลู ชนิด byte แทนสตรงิ ดงั นนั้ จงึ จาเป็นตอ้ งทาการแปลงจาก byte เป็นสตรงิ เสยี ก่อนโดยใชเ้ มธอด bytes.decode() ดงั แสดงในบรรทดั ท่ี 12 ขอ้ มลู ทแ่ี ปลงแลว้ จะเกบ็ ไวใ้ นตวั แปรช่อื dayAndTime ต่อจากนนั้ บรรทดั ท่ี 13 พมิ พข์ อ้ มลู ทอ่ี ยใู่ นตวั แปร dayAndTime บรรทดั ท่ี 14 สงั่ ใหโ้ ปรแกรมฝัง่ ไคลเอน็ ตย์ ตุ กิ ารเช่อื มต่อ สง่ ผลใหเ้ ซริ ฟ์ เวอรป์ ิดชอ่ ง ทางการเช่อื มต่อจากเครอ่ื งไคลเอน็ ตด์ งั กล่าว เมอ่ื เขยี นโปรแกรมเสรจ็ แลว้ ทดสอบสงั่ รนั โปรแกรมจะเกดิ ขอ้ ผดิ พลาดขน้ึ คอื Traceback (most recent call last): File \"C:/Python34/Ch15/Ch15_5.py\", line 10, in <module> server.connect(ADDRESS) ConnectionRefusedError: [WinError 10061] No connection could be made because the target machine actively refused it ห น้ า 359
ขอ้ ผดิ พลาดทเ่ี กดิ ขน้ึ เน่อื งจากโปรแกรมฝัง่ เซริ ฟ์ เวอรย์ งั ไมไ่ ดท้ างานนัน่ เอง ดงั นนั้ ผเู้ ขยี นโปรแกรมตอ้ ง สงั่ รนั โปรแกรมเซริ ฟ์ เวอรก์ ่อนไคลเอน็ ตเ์ สมอ การเขียนโปรแกรมฝัง่ เซิรฟ์ เวอร์ โปรแกรมในฝัง่ เซริ ฟ์ เวอรจ์ ะแตกต่างจากฝัง่ ไคลเอน็ ตเ์ ลก็ น้อยคอื เมอ่ื โปรแกรมฝัง่ ไคลเอน็ ต์ เช่อื มต่อกบั เซริ ฟ์ เวอรแ์ ลว้ จากนนั้ ไคลเอน็ ตจ์ ะรบั และส่งขอ้ มลู กบั เซริ ฟ์ เวอรอ์ ยา่ งต่อเน่อื ง เมอ่ื รบั ส่ง ขอ้ มลู เสรจ็ เรยี บรอ้ ยแลว้ ไคลเอน็ ตจ์ ะรอ้ งขอเพ่อื ยตุ กิ ารเช่อื มต่อจากเซริ ฟ์ เวอร์ ซง่ึ เป็นอนั หมดหน้าท่ี ของไคลเอน็ ต์ แต่สาหรบั เซริ ฟ์ เวอรแ์ ลว้ จะไม่ยตุ กิ ารทางาน โดยเซริ ฟ์ เวอรจ์ ะทางานต่อไปเรอ่ื ยๆ เพอ่ื รอ การเช่อื มต่อจากไคลเอน็ ตอ์ ่นื ๆ ต่อไป สาหรบั ขนั้ ตอนการเขยี นโปรแกรมในฝัง่ เซริ ฟ์ เวอรด์ งั น้ี 1) สรา้ ง Socket ซง่ึ จะทาใหไ้ ดซ้ อ็ กเกต็ ออ็ ปเจก็ ต์ (Socket Object) 2) ผกู (Bind) ออ็ ปเจก็ ตข์ องซอ็ กเกต็ ทไ่ี ดจ้ ากขนั้ ตอนท่ี 1 เขา้ กบั หมายเลขไอพแี อดเดรส และพอรต์ 3) กาหนดจานวนไคลเอน็ ตท์ ส่ี ามารถเขา้ ใชง้ านเซริ ฟ์ เวอรไ์ ดพ้ รอ้ มๆ กนั 4) รอรบั การเชอ่ื มต่อ, ส่อื สารขอ้ มลู และยตุ กิ ารเชอ่ื มต่อจากไคลเอน็ ต์ แสดงดงั รปู ท่ี 15.5 รปู ท่ี 15.5 (ก) แสดงการทางานของเซริ ฟ์ เวอร์ ห น้ า 360
รปู ท่ี 15.5 (ข) แสดงภาพรวมการทางานของไคลเอนต์ - เซริ ฟ์ เวอร์ สาหรบั ตวั อยา่ งโปรแกรมฝัง่ เซริ ฟ์ เวอรแ์ สดงไดด้ งั น้ี Program Example 15.6: Easy Server Script (send day/time) 1 ''' Server for providing the day and time.''' 2 from socket import * 3 from time import ctime 4 5 HOST = 'localhost' 6 PORT = 5000 7 BUFFER_SIZE = 1024 8 ADDRESS = (HOST, PORT) #(127.0.0.1, 5000) 9 10 server = socket(AF_INET, SOCK_STREAM) 11 server.bind(ADDRESS) 12 server.listen(5) 13 14 while True: 15 print('waiting for connection...') 16 client, address = server.accept() 17 print('connected from: ', address) 18 data_bytes = str.encode(ctime()) 19 client.send(data_bytes) 20 client.close() ห น้ า 361
จากตวั อยา่ งโปรแกรมท่ี 15.6 แสดงโปรแกรมฝัง่ เซริ ฟ์ เวอร์ ทาหน้าทร่ี อรบั การเช่อื มต่อจากไคลเอน็ ต์ และจะสง่ วนั และเวลาบนเครอ่ื งเซริ ฟ์ เวอรใ์ หก้ บั โปรแกรมฝัง่ ไคลเอน็ ตน์ าไปแสดงผล บรรทดั ท่ี 10 สรา้ ง Socket ของเซริ ฟ์ เวอรช์ ่อื server เหมอื นกบั ในตวั อยา่ งท่ี 15.5 บรรทดั ท่ี 11 โปรแกรมผกู ไอพแี อดเดรส และพอรต์ เขา้ กบั ออ็ ปเจก็ ตช์ ่อื server บรรทดั ท่ี 12 กาหนดใหเ้ ซริ ฟ์ เวอรส์ ามารถรองรบั การเชอ่ื มต่อจาก ไคลเอน็ ตไ์ ดพ้ รอ้ มๆ กนั เท่ากบั 5 เครอ่ื ง บรรทดั ท่ี 14 กาหนดใหโ้ ปรแกรมทางานตลอดเวลาเพ่อื รอรบั การเช่อื มต่อจากไคลเอน็ ต์ บรรทดั ท่ี 15 เซริ ฟ์ เวอรส์ งั่ พมิ พข์ อ้ ความว่า 'waiting for connection...' จากนนั้ เซริ ฟ์ เวอรจ์ ะเฝ้ารอการเชอ่ื มต่อจากไคลเอน็ ต์ เม่อื ไคลเอน็ ตเ์ ชอ่ื มต่อเขา้ มา (บรรทดั ท่ี 16) และ เซริ ฟ์ เวอรต์ อบรบั การเช่อื มต่อแลว้ เซริ ฟ์ เวอรจ์ ะพมิ พข์ อ้ ความวา่ 'connected from: ' ตามดว้ ย หมายเลขไอพแี ละพอรต์ ตามลาดบั ดงั ในบรรทดั ท่ี 17 จากนนั้ โปรแกรมจะดงึ วนั และเวลาจากระบบดว้ ย เมธอด ctime() จากโมดลู time แต่ในไพธอนเวอรช์ นั 3.0 ขน้ึ ไป จะไม่รองรบั การสง่ สตรงิ ผ่าน Socket จงึ จาเป็นตอ้ งเปลย่ี นวนั และเวลาเป็นขอ้ มลู ชนิดไบตเ์ สยี ก่อนดว้ ยเมธอด str.encode(ctime()) และเกบ็ ไว้ ในตวั แปรชอ่ื data_byte แสดงในบรรทดั ท่ี 18 จากนนั้ โปรแกรมจะส่งวนั เวลาทถ่ี ูกแปลงเป็นไบตแ์ ลว้ ใหก้ บั เครอ่ื งไคลเอน็ ตด์ ว้ ยเมธอด send() เมอ่ื เซริ ฟ์ เวอรส์ ่งขอ้ มลู ให้ไคลเอน็ ตเ์ สรจ็ เรยี บรอ้ ยแลว้ จะปิด การเช่อื มต่อทนั ทดี ว้ ยเมธอด close() วิธีการรนั โปรแกรมเซิรฟ์ เวอรแ์ ละไคลเอน็ ต์ เน่อื งจากไพธอนอนุญาตใหผ้ เู้ ขยี นโปรแกรมสามารถรนั โปรแกรมไดค้ รงั้ ละ 1 โปรแกรมเท่านนั้ จาก Phython Shell หรอื Python IDLE ดงั นนั้ เมอ่ื จะสงั่ รนั โปรแกรมไคลเอน็ ต์-เซริ ฟ์ เวอรพ์ รอ้ มๆ กนั บน Python IDLE จะทาใหเ้ ซริ ฟ์ เวอรป์ ิดพอรต์ และยตุ กิ ารทางานทนั ที เพ่อื ใหก้ ารรนั โปรแกรมดงั กล่าว สามารถทางานไดจ้ ะตอ้ งใช้ MS-DOS ช่วยดงั น้ี การรนั โปรแกรมเซิรฟ์ เวอร์ สงั่ รนั โปรแกรมฝัง่ เซริ ฟ์ เวอรจ์ าก Python IDLE จะทาใหไ้ ดผ้ ลลพั ธด์ งั รปู ดา้ นล่าง เมอ่ื สงั่ รนั โปรแกรมไคลเอน็ ตจ์ ะทาใหเ้ ซริ ฟ์ เวอรแ์ สดงผลลพั ธด์ งั รปู ดา้ นลา่ ง ห น้ า 362
การรนั โปรแกรมไคลเอน็ ต์ เลอื ก start ของวนิ โดวส์ Run พมิ พค์ าว่า cmd และกดป่มุ Enter ดงั รปู เปลย่ี นไดเรคทรอรไ่ี ปยงั ทเ่ี กบ็ โปรแกรมตน้ ฉบบั ของไคลเอน็ ต์ (ในทน่ี ้ชี อ่ื Ch15_5.py) จากตวั อยา่ งน้ีโปรแกรมจะเกบ็ อย่ใู น C:\\Python34\\Ch15 ดงั นนั้ ใหใ้ ช้ คาสงั่ เปลย่ี นไดเรคทรอรด่ี งั น้ี cd c:\\Python34\\Ch15 ดงั รปู เมอ่ื กดป่มุ Enter จะเปลย่ี นไดเรคทรอรไ่ี ปยงั c:\\Python34\\Ch15 ทนั ที จากนนั้ ใช้ คาสงั่ เพอ่ื รนั โปรแกรมไคลเอน็ ตด์ งั น้ี c:\\Python34\\Ch15>python Ch15_5.py ผลลพั ธแ์ สดงดงั รปู ดา้ นล่าง ไคลเอน็ ตจ์ ะสงั่ พมิ พว์ นั และเวลาทส่ี ่งมาจากเซริ ฟ์ เวอรบ์ นจอภาพ ผใู้ ชง้ านสามารถสงั่ รนั โปรแกรมไคลเอน็ ตไ์ ดเ้ รอ่ื ยๆ ไมจ่ ากดั จานวน แต่ไคลเอน็ ตจ์ ะเขา้ ใชง้ านเซริ ฟ์ เวอรไ์ ดพ้ รอ้ มๆ กนั ได้ เพยี ง 5 เครอ่ื งเท่านัน้ 4. การเขียนโปรแกรมพดู คยุ ผา่ นเน็ตเวิรค์ (Chat) จากตวั อยา่ งโปรแกรมในหวั ขอ้ ทผ่ี า่ นมาเป็นการสอ่ื สารในลกั ษณะทางเดยี ว คอื ไคลเอน็ ตร์ บั ขอ้ มลู จากเซริ ฟ์ เวอรอ์ ยา่ งเดยี วโดยท่ไี คลเอน็ ตไ์ มไ่ ดส้ ง่ ขอ้ มลู ใดๆ กลบั ไปใหก้ บั เซริ ฟ์ เวอรเ์ ลย ดงั นนั้ ใน ตวั อยา่ งต่อไปน้ี ผเู้ ขยี นจะแนะนาการเขยี นโปรแกรม Chat ซง่ึ มคี วามสามารถในการสอ่ื สารแบบ สองทศิ ทาง คอื ผใู้ ชง้ านสามารถพมิ พข์ อ้ ความโตต้ อบกบั เซริ ฟ์ เวอรไ์ ดต้ ลอดเวลา จนกวา่ ผใู้ ชง้ านจะยตุ ิ (ปิดโปรแกรม) การทางานลง แต่ในโปรแกรมน้ียงั มขี อ้ จากดั คอื โปรแกรมสามารถโตต้ อบระหวา่ ง ห น้ า 363
เซริ ฟ์ เวอรแ์ ละไคลเอน็ ตใ์ นขณะหน่งึ ๆ ไดเ้ พยี ง 1 ต่อ 1 เท่านัน้ สาหรบั ไคลเอน็ ตอ์ ่นื ๆ ทต่ี อ้ งการสอ่ื สาร กบั เซริ ฟ์ เวอรจ์ ะตอ้ งรอใหไ้ คลเอน็ ตท์ ก่ี าลงั ทางานอย่ยู ตุ กิ ารทางานเสยี ก่อน จงึ จะสามารถส่อื สารกบั เซริ ฟ์ เวอรด์ งั กล่าวได้ สาหรบั การแกป้ ัญหาเกย่ี วกบั ขอ้ จากดั ของการสอ่ื สารทก่ี ลา่ วมาแลว้ จะแสดงใน หวั ขอ้ ถดั ไป ตวั อยา่ งโปรแกรม Chat ในฝัง่ เซริ ฟ์ เวอรแ์ สดงไดด้ งั น้ี Program Example 15.7: Chat Server Script 1 '''Chat Server Script''' 2 from socket import * 3 4 HOST = 'localhost' 5 PORT = 5000 6 BUFFER_SIZE = 1024 7 ADDRESS = (HOST, PORT) #(127.0.0.1, 5000) 8 9 server = socket(AF_INET, SOCK_STREAM) 10 server.bind(ADDRESS) 11 server.listen(5) 12 13 while True: 14 print('waiting for connection...') 15 client, address = server.accept() 16 print('connected from: ', address) 17 client.send(str.encode('Welcome to my Chat room!')) 18 19 while True: 20 message = bytes.decode(client.recv(BUFFER_SIZE)) 21 if not message: 22 print(\"Client diconnected\") 23 client.close() 24 break 25 else: 26 print(message) 27 client.send(str.encode(input('> '))) เมอ่ื สงั่ รนั โปรแกรม Chat เซริ ฟ์ เวอรจ์ ะไดผ้ ลลพั ธด์ งั น้ี จากตวั อยา่ งโปรแกรมท่ี 15.7 แสดงโปรแกรม Chat เซริ ฟ์ เวอร์ ทาหน้าทร่ี อรบั การเชอ่ื มต่อ (บรรทดั ท่ี 14, 15) จากไคลเอน็ ต์ เมอ่ื ไคลเอน็ ตเ์ ช่อื มต่อเขา้ มาและเซริ ฟ์ เวอรย์ อมรบั การเช่อื มต่อแลว้ เซริ ฟ์ เวอรจ์ ะ พมิ พข์ อ้ ความว่า 'connected from: ' ตามดว้ ยหมายเลขไอพแี อดเดรสของเครอ่ื งไคลเอน็ ต์ (บรรทดั ท่ี ห น้ า 364
16) ต่อจากนนั้ เซริ ฟ์ เวอรจ์ ะสง่ ขอ้ ความไปใหก้ บั ไคลเอน็ ตค์ อื 'Welcome to my Chat room!' โดย ขอ้ ความดงั กลา่ วจะตอ้ งถกู แปลงเป็นไบตเ์ สยี ก่อน (บรรทดั ท่ี 17) ดว้ ยเมธอด str.encode() บรรทดั ท่ี 19 โปรแกรมจะวนลูปเพอ่ื ใหเ้ ซริ ฟ์ เวอรท์ างานไปเรอ่ื ยๆ แบบไมร่ จู้ บ เมอ่ื เซริ ฟ์ เวอรส์ ง่ ขอ้ ความว่า 'Welcome to my Chat room!' ไปใหก้ บั ไคลเอน็ ตแ์ ลว้ เซริ ฟ์ เวอรจ์ ะรอรบั ขอ้ มลู จาก Socket วา่ เครอ่ื ง ไคลเอน็ ตส์ ง่ ขอ้ มลู มาใหห้ รอื ยงั (บรรทดั ท่ี 20) ถา้ มกี ารส่งขอ้ มลู มายงั เซริ ฟ์ เวอร์ ขอ้ มลู เหลา่ นนั้ จะถูก แปลงจากไบตเ์ ป็นสตรงิ เสยี ก่อนดว้ ยเมธอด bytes.decode() แต่ถา้ ไคลเอน็ ตไ์ มส่ ่งขอ้ มลู เขา้ มา เซริ ฟ์ เวอรจ์ ะรอไปเรอ่ื ยๆ ขอ้ มลู ทถ่ี กู แปลงเป็นสตรงิ แลว้ จะนาไปตรวจสอบวา่ ขอ้ มลู ดงั กลา่ วมคี วามยาว หรอื ไม่ (บรรทดั ท่ี 21) ถา้ ค่าในตวั แปร message เท่ากบั 0 แสดงวา่ ไคลเอน็ ตก์ ดป่มุ Enter เทา่ นัน้ โดย ไมพ่ มิ พข์ อ้ ความใดๆ เลย สง่ ผลใหเ้ ซริ ฟ์ เวอรพ์ มิ พข์ อ้ ความวา่ \"Client diconnected\" และปิดการ เชอ่ื มต่อกบั ไคลเอน็ ตน์ นั้ ๆ ทนั ที และหลุดออกจากลูป while ชนั้ ใน (บรรทดั ท่ี 19) ทนั ทดี ว้ ยคาสงั่ break แต่ถา้ ค่าในตวั แปร message ไมเ่ ทา่ กบั 0 แสดงวา่ ไคลเ์ อนตส์ ่งขอ้ มลู เขา้ มา เซริ ฟ์ เวอรจ์ ะพมิ พ์ ขอ้ ความดงั กล่าว (บรรทดั ท่ี 26) ออกทางจอภาพของฝัง่ เซริ ฟ์ เวอร์ และสง่ สตรงิ คาว่า input('> ') ใหก้ บั ไคลเอน็ ต์ (บรรทดั ท่ี 27) เมอ่ื คาสงั่ น้ไี ปปรากฎในฝัง่ ไคลเอน็ ตจ์ ะถกู แปลความหมายคอื ฟังชนั input() ซง่ึ จะกลายเป็น prompt บนฝัง่ ไคลเอน็ ตน์ นั่ เอง สรปุ การทางานของเซริ ฟ์ เวอรค์ อื เซริ ฟ์ เวอรจ์ ะปิดการ เช่อื มต่อจากไคลเอน็ ตเ์ มอ่ื ไคลเอน็ ตก์ ดป่มุ Enter อยา่ งเดยี วเทา่ นนั้ ตวั อยา่ งโปรแกรม Chat ในฝัง่ ไคลเอน็ ตแ์ สดงไดด้ งั น้ี Program Example 15.8: Chat Client Script 1 '''Chat Client Script''' 2 from socket import * 3 4 HOST = 'localhost' 5 PORT = 5000 6 BUFFER_SIZE = 1024 7 ADDRESS = (HOST, PORT) #(127.0.0.1, 5000) 8 9 server = socket(AF_INET, SOCK_STREAM) 10 server.connect(ADDRESS) 11 messageFromServer = bytes.decode(server.recv(BUFFER_SIZE)) 12 print(messageFromServer) 13 14 while True: 15 message = input('> ') 16 if not message: 17 break 18 server.send(str.encode(message)) 19 reply = bytes.decode(server.recv(BUFFER_SIZE)) 20 if not reply: 21 print('Server disconnected') 22 break 23 print(reply) ห น้ า 365
24 server.close() เมอ่ื สงั่ รนั โปรแกรม Chat ไคลเอน็ ตบ์ น MS-DOS จะไดผ้ ลลพั ธด์ งั น้ี ทดสอบพมิ พข์ อ้ ความบนฝัง่ ไคลเอน็ ต์ เช่น 'I'm client' จะไดผ้ ลลพั ธด์ งั น้ี ขอ้ ความ 'I'm client' จะไปปรากฎทฝ่ี ัง่ เซริ ฟ์ เวอร์ ใหท้ ดสอบพมิ พข์ อ้ ความว่า 'I'm server' บนฝัง่ เซริ ฟ์ เวอร์ ขอ้ ความจะปรากฎบนฝัง่ ไคลเอน็ ตด์ งั รปู ดา้ นล่าง ใหผ้ ใู้ ชก้ ดป่มุ Enter จะทาใหไ้ คลเอน็ ตย์ ตุ กิ ารทางาน ทนั ทดี งั รปู สาหรบั โปรแกรมบนฝัง่ เซริ ฟ์ เวอรจ์ ะปิดการเช่อื มต่อของไคลเอน็ ต์ และรอการเชอ่ื มต่อจากไคลเอน็ ต์ เครอ่ื งอ่นื ๆ ต่อไป ห น้ า 366
สาหรบั ตวั อยา่ งโปรแกรม Chat Client จะมลี กั ษณะการทางานคลา้ ยกบั ฝัง่ เซริ ฟ์ เวอร์ แต่แตกต่างกนั คอื โปรแกรมฝัง่ ไคลเอน็ ตจ์ ะยตุ กิ ารทางานทนั ทเี มอ่ื กดป่มุ Enter แต่เซริ ฟ์ เวอรจ์ ะทางานต่อไปเรอ่ื ยๆ โดย ไมย่ ตุ กิ ารทางานจนกว่าผใู้ ชจ้ ะปิดโปรแกรม 5. โปรแกรม Chat ท่ีสามารถรองรบั ไคลเอน็ ตพ์ ร้อมๆ กนั หลายเครอื่ ง จากตวั อยา่ ง Chat ทผ่ี ่านมาในหวั ขอ้ ทแ่ี ลว้ มขี อ้ จากดั คอื ในขณะหน่งึ ๆ จะมเี พยี งไคลเอน็ ต์ เครอ่ื งเดยี วเท่านนั้ ทส่ี ามารถสอ่ื สารกบั เซริ ฟ์ เวอรไ์ ด้ โดยไคลเอน็ ตอ์ ่นื ๆ ตอ้ งรอจนกว่าไคลเอน็ ตท์ ก่ี าลงั ส่อื สารอยจู่ ะยตุ กิ ารทางาน ในตวั อยา่ งน้ผี เู้ ขยี นจะปรบั ปรงุ ใหเ้ ซริ ฟ์ เวอรส์ ามารถรองรบั การเช่อื มต่อจาก ไคลเอน็ ตไ์ ดพ้ รอ้ มๆ กนั โดยใชค้ ุณสมบตั เิ รอ่ื งของเธรดในหวั ขอ้ ทผ่ี ่านมาช่วยในการทางาน เมอ่ื ไคลเอน็ ตร์ อ้ งขอมายงั เซริ ฟ์ เวอร์ เซริ ฟ์ เวอรจ์ ะสรา้ งเธรด (Spawns) เพอ่ื จดั การส่อื สารกบั ไคลเอน็ ต์ (Client Handler) เหลา่ นนั้ โดยแยกเป็นอสิ ระจากกนั แสดงดงั รปู ท่ี 15.6 Server Waits for connection 2 Spawns request Client 1 Client Handler Server Script send/recv 3 Client Script Socket connection รปู ท่ี 15.6 แสดงการทางานของเซริ ฟ์ เวอรท์ ส่ี ามารถรองรบั ไคลเอน็ ตไ์ ดพ้ รอ้ มๆ กนั ไคลเอน็ ตร์ อ้ งขอการเช่อื มต่อไปยงั เซริ ฟ์ เวอรผ์ ่าน Socket เมอ่ื เซริ ฟ์ เวอรย์ อมรบั การเช่อื มต่อจากไคลเอน็ ตแ์ ลว้ เซริ ฟ์ เวอรจ์ ะสรา้ งเธอด (Spawns) เพอ่ื จดั การกบั ไคลเอน็ ต์ (Client Handler) แต่ละเครอ่ื ง ไคลเอน็ ตส์ ่งและรบั (send/recv) ขอ้ มลู กบั เซริ ฟ์ เวอร์ ผา่ น Socket (Socket Connection) ยตุ กิ ารเชอ่ื มต่อโดยกดป่มุ Enter โดยปราศจากขอ้ ความใดๆ ขนั้ ตอนออกแบบโปรแกรม Chat 1. ออกแบบฐานขอ้ มลู สาหรบั เกบ็ ขอ้ ความท่ไี คลเอน็ ตส์ นทนากนั 2. ออกแบบเซริ ฟ์ เวอรท์ ส่ี ามารถจดั การกบั ไคลเอน็ ตไ์ ดห้ ลายๆ เครอ่ื งพรอ้ มกนั (Spawns) โดยการลงทะเบยี นดว้ ยช่อื ผใู้ ช้ (User name), เกบ็ และดงึ ขอ้ มลู (Store/Retrieve) การ สนทนาจากฐานขอ้ มลู ใหก้ บั ไคลเอน็ ต,์ ส่งขอ้ ความไปยงั ทุกๆ ไคลเอน็ ต์ (Broadcasting), ยตุ กิ ารเช่อื มต่อเมอ่ื ผใู้ ชย้ กเลกิ การใชง้ าน (โดยกดป่มุ Enter ท่ี ห น้ า 367
ปราศจากขอ้ ความใดๆ), ลอ็ คและปลดปลอ่ ยทรพั ยากรท่ใี ชท้ างานรว่ มกนั เพอ่ื ให้ ทรพั ยากรเหล่านนั้ ถกู เขา้ ใชง้ านอยา่ งถกู ตอ้ ง (เน่อื งจากโปรแกรมเขยี นดว้ ยเธรด) 3. ออกแบบไคลเอน็ ตท์ ส่ี ามารถสนทนาโตต้ อบกบั ไคลเอน็ ตอ์ ่นื ๆ ไดพ้ รอ้ มกนั หลายๆ เครอ่ื ง และยตุ กิ ารทางานของโปรแกรมดว้ ยการกดป่มุ Enter โดยปราศจากขอ้ ความ ใดๆ สาหรบั รปู แบบการทางานทงั้ ระบบแสดงในรปู ท่ี 15.7 Waits for Chat Client connection Client 1 Chat Server request 2 Server send/recv Client 2 Spawns send/recv ... ClientHandler Client N (Thread 1) Broadcasting Lock send/recv 3 Message Resources ClientHandler (Thread 2) Socket Release connection Resources ... ClientHandler (Thread N) Store/retrieve Chat Database Lock/Release database 1 data รปู ท่ี 15.7 แสดงภาพรวมของโปรแกรม Chat สรา้ งโปรแกรมชื่อ chatDatabase.py เพื่อใช้สาหรบั เกบ็ ข้อมลู การสนทนา Program Example chatDatabase.py: 1 '''Chat Database Script''' 2 class chatRecord(): 3 def __init__(self): 4 self.data = [] 5 6 def addMessage(self, message): 7 self.data.append(message) 8 9 def getMessage(self, messageID): 10 if len(self.data) == 0: 11 return 'No message yet!' 12 elif messageID == 0: #get all message for database 13 return '\\n'.join(self.data) 14 elif messageID != 0: #get a chunk of message 15 temp = self.data[messageID:] ห น้ า 368
16 return '\\n'.join(temp) 17 else: 18 return \"\\n\" จากตวั อยา่ งโปรแกรม chatDatabase.py แสดงการสรา้ งฐานขอ้ มลู อยา่ งงา่ ยเพอ่ื ใชส้ าหรบั เกบ็ ขอ้ มลู การ สนทนาระหวา่ งไคลเอน็ ต์ โดยฐานขอ้ มลู ดงั กล่าวยงั มจี ุดอ่อนคอื ขอ้ มลู การสนทนาจะสญู หายเมอ่ื ยตุ กิ าร ทางานของเซริ ฟ์ เวอร์ ถา้ ผเู้ ขยี นโปรแกรมตอ้ งการใหข้ อ้ มลู การสนทนาถกู เกบ็ แบบถาวร ผเู้ ขยี นแนะนา ใหเ้ กบ็ เป็น Text ไฟล์ (เหมาะสาหรบั ไคลเอน็ ตท์ ม่ี จี านวนน้อย), เกบ็ ใน XML ไฟล์ (จานวนไคลเอน็ ต์ ปานกลาง) และเกบ็ ในฐานขอ้ มลู เช่น MySQL, Oracle, Informix (สาหรบั ไคลเอน็ ต์จานวนมาก) บรรทดั ท่ี 2 - 4 สรา้ งคลาสช่อื chatRecord() โดยมคี อนสตรกั เตอรท์ าหน้าทก่ี าหนดค่าเรม่ิ ต้น ใหก้ บั ตวั แปรชนิดลสิ ตช์ อ่ื data เพอ่ื ทาหน้าทเ่ี กบ็ ขอ้ ความการสนทนาของไคลเอน็ ตท์ งั้ หมด บรรทดั ท่ี 6 – 7 สรา้ งเมธอดชอ่ื addMessage() ทาหน้าทเ่ี พม่ิ ขอ้ ความการสนทนาไวใ้ นตวั แปร data โดยใชเ้ มธอด append() มพี ารามเิ ตอร์ 1 ตวั คอื message (ขอ้ ความการสนทนา) บรรทดั ท่ี 9 – 18 สรา้ งเมธอดชอ่ื ว่า getMessage() ทาหน้าดงึ ขอ้ มลู จากฐานขอ้ มลู (ในตวั แปร data) โดยมพี ารามเิ ตอร์ 1 ตวั (messageID) คอื หมายเลขบรรทดั ของขอ้ ความในฐานขอ้ มลู ถา้ messageID เท่ากบั 0 โปรแกรมจะดงึ ขอ้ มลู ทงั้ หมด ในฐานขอ้ มลู สง่ กลบั ไปใหก้ บั ไคลเอน็ ต์ ถา้ messageID เป็นเลขจานวนเตม็ ทไ่ี มเ่ ท่ากบั 0 โปรแกรมจะ ดงึ ขอ้ มลู ตงั้ แต่ตาแหน่งบรรทดั ใน messageID ถงึ ตาแหน่งขอ้ มลู บรรทดั สดุ ทา้ ยของตวั แปร data สาหรบั คาสงั่ '\\n'.join(self.data) ทาหน้าทเ่ี ช่อื มต่อขอ้ ความในตวั แปร data เขา้ ไวด้ ว้ ยกนั เช่น data[0] = 'Hello', data[1] = 'World!' และ data[2] = 'Python' เมอ่ื ใชค้ าสงั่ '\\n'.join(self.data) จะไดผ้ ลลพั ธค์ อื 'Hello\\nWorld!\\nPython' ถา้ ตวั แปร data ไมม่ ขี อ้ มลู ใดๆ เกบ็ อยโู่ ปรแกรมจะคนื ค่าใหก้ บั ไคลเอน็ ตเ์ ป็น 'No message yet!' สรา้ งโปรแกรมชื่อ ChatServer.py เพ่ือทาหน้าท่ีเป็นเซิรฟ์ เวอรส์ าหรบั โปรแกรม Chat Program Example ChatServer.py 1 '''Chat Server for a multi-client chat room''' 2 from socket import * 3 from chatDatabase import chatRecord 4 from threading import Thread 5 import threading 6 from time import ctime 7 8 class clientHandler(Thread): 9 def __init__(self, client, record, address): 10 Thread.__init__(self) 11 self._client = client 12 self._record = record 13 self._address = address 14 15 #broadcasting chat messages to all connected clients ห น้ า 369
16 def broadCastingMessage(self, activeClient, message): 17 #Do not send the message to server and the client who has send the message to us 18 19 for socket in CONNECTIONS_LIST: 20 if socket != server and socket != activeClient: 21 try: 22 broadcastMessage = str.encode(message) 23 socket.send(broadcastMessage) 24 except: # broken socket connection may be, chat 25 client pressed ctrl+c for example 26 print (\"Client (%s) is offline\" 27 %self._address) 28 broadCastingMessage(socket, (\"Client (%s) 29 30 is offline\" %self._address)) 31 socket.close() CONNECTIONS_LIST.remove(socket) 32 33 def run(self): self._client.send(str.encode('Welcome to the chat 34 35 room')) 36 self._name = bytes.decode(self._client.recv(BUFSIZE)) 37 #Geting all messages from database and send them to 38 client in the first time 39 allMessage = self._record.getMessage(0) 40 self._client.send(str.encode(allMessage)) 41 while True: 42 message = 43 44 bytes.decode(self._client.recv(BUFSIZE)) if not message: 45 print('Client disconnected') 46 self._client.close() 47 CONNECTIONS_LIST.remove(self._client) 48 break else: 49 message = ctime() + ': [' + self._name + '] - 50 51 ->' + message 52 self._record.addMessage(message) 53 #Broadcasing a new messages to every clients 54 threadLock.acquire() 55 self.broadCastingMessage(self._client, 56 57 message) 58 threadLock.release() 59 HOST = 'localhost' PORT = 5000 BUFSIZE = 4096 ADDRESS = (HOST, PORT) #(127.0.0.1, 5000) # List to keep track of all socket connections CONNECTIONS_LIST = [] # Creating Threads Synchronization threadLock = threading.Lock() record = chatRecord() ห น้ า 370
60 server = socket(AF_INET, SOCK_STREAM) 61 server.bind(ADDRESS) 62 server.listen(10) 63 # Add server socket to the list 64 CONNECTIONS_LIST.append(server) 65 print (\"Chat server started on port \" + str(PORT)) 66 67 while True: 68 print('Waiting for connection...') 69 client, address = server.accept() 70 print('...connected from:', address) 71 # Lock CONNECTIONS_LIST for inserting connected client 72 threadLock.acquire() 73 CONNECTIONS_LIST.append(client) 74 # Release CONNECTIONS_LIST 75 threadLock.release() 76 handler = clientHandler(client, record, address) 77 handler.start() จากตวั อยา่ งโปรแกรม ChatServer.py แสดงการสรา้ งเซริ ฟ์ เวอร์ Chat บรรทดั ท่ี 3 นาเขา้ ฐานขอ้ มลู ทไ่ี ด้ สรา้ งไวแ้ ลว้ คอื แฟ้ม chatDatabase.py เขา้ มาทางาน โดยใชค้ าสงั่ form chatDatabase import chatRecord (ช่อื คลาสในแฟ้ม chatDatabase.py) บรรทดั ท่ี 4 – 5 นาเขา้ โมดลู threading เพอ่ื ใช้ สาหรบั สรา้ งเธรดของไคลเอน็ ตแ์ ต่ละเครอ่ื ง บรรทดั ท่ี 6 นาเขา้ โมดลู time เพอ่ื ใชส้ รา้ งวนั และเวลา บรรทดั ท่ี 8 สรา้ งคลาสชอ่ื clientHandler() ทาหน้าทจ่ี ดั การไคลเอน็ ตท์ เ่ี ช่อื มต่อเขา้ มายงั เซริ ฟ์ เวอร์ Chat โดยคลาสดงั กล่าวสบื ทอดคณุ สมบตั มิ าจากคลาส Thread ซง่ึ ส่งผลใหค้ ลาส clientHandler มคี ณุ สมบตั เิ ป็นเธรดไปดว้ ย ในคลาสน้สี รา้ งคอนสตรกั เตอร์ (บรรทดั ท่ี 9 - 13) ทาหน้าท่ี กาหนดค่าเรมิ่ ตน้ ใหก้ บั ตวั แปรต่างๆ ก่อนคลาสดงั กล่าวจะทางาน สาหรบั ค่าเรม่ิ ต้นทก่ี าหนดคอื ออ็ ป เจก็ ตข์ องไคลเอน็ ต์ (self._client), ตวั แปรทอ่ี า้ งองิ ไปยงั ฐานขอ้ มลู (self._record) และหมายเลขไอพี แอดเดรส (self._address) บรรทดั ท่ี 16 โปรแกรมสรา้ งเมธอดช่อื broadCastingMessage() ทาหน้าท่ี ส่งขอ้ ความสนทนาจากไคลเอน็ ตเ์ ครอ่ื งใดเครอ่ื งหน่งึ ไปยงั ทกุ ๆ เครอ่ื งทก่ี าลงั ส่อื สารกบั เซริ ฟ์ เวอร์ Chat อยู่ เมธอดน้ตี อ้ งการพารามเิ ตอร์ 2 ตวั คอื ออ็ ปเจก็ ต์ (activeClient) ของไคลเอน็ ตเ์ ครอ่ื งใดเครอ่ื งหน่งึ ท่ี กาลงั เช่อื มต่ออยู่ และขอ้ ความ (message) ทไ่ี คลเอน็ ตต์ อ้ งการสง่ ไปยงั ไคลเอน็ ตอ์ ่นื ๆ ทงั้ หมดทอ่ี ยใู่ น ระบบ บรรทดั ท่ี 18 โปรแกรมนาเอาออ็ ปเจก็ ตข์ อง Socket ทเ่ี กบ็ รวบรวมไวใ้ นตวั แปร CONNECTIONS_LIST มาตรวจสอบว่าเป็นออ็ ปเจก็ ต์ Socket ของเซริ ฟ์ เวอรห์ รอื ไม่ หรอื เป็นออ็ ป เจก็ ตข์ องตนเองหรอื ไม่ อธบิ ายงา่ ยๆ คอื ถา้ ไคลเอน็ ต์ A สง่ ขอ้ ความไปยงั ไคลเอน็ ตอ์ ่นื ๆ ในระบบ ขอ้ ความดงั กลา่ วไมค่ วรถูกส่งไปยงั Socket ของเซริ ฟ์ เวอรแ์ ละ Socket ของตวั เอง (ไคลเอน็ ต์ A) โดยใช้ เงอ่ื นไขในโปรแกรมบรรทดั ท่ี 19 ต่อจากนนั้ บรรทดั ท่ี 21 โปรแกรมวนลูปสง่ ขอ้ ความสนทนาไปยงั ทุกๆ เครอ่ื งยกเวน้ เซริ ฟ์ เวอรแ์ ละตวั เอง ถา้ โปรแกรมไมส่ ามารถส่งขอ้ ความไปยงั เครอ่ื งไคลเอน็ ตใ์ ดๆ ได้ เซริ ฟ์ เวอรจ์ ะสง่ ขอ้ ความไปบอกไคลเอน็ ตอ์ ่นื ๆ วา่ มไี คลเอน็ ตบ์ างตวั ไมส่ ามารถเช่อื มต่อได้ และ ห น้ า 371
โปรแกรมจะทาการปิด Socket ของไคลเอน็ ตเ์ ครอ่ื งทไ่ี มต่ อบสนอง และลบออ็ ปเจก็ ตข์ องไคลเอน็ ต์ ดงั กล่าวออกจากตวั แปร CONNECTIONS_LIST (บรรทดั ท่ี 25 - 28) โดยคาสงั่ ทงั้ หมดจะทางานอยู่ ภายใตก้ ารควบคุมของคาสงั่ try…except บรรทดั ท่ี 30 โปรแกรมทาการโอเวอรไ์ รดเ์ มธอด run() ของคลาส Thread เพ่อื ทาหน้าทค่ี วบคุม การสนทนาระหวา่ งไคลเอน็ ต์ โดยเรม่ิ ตน้ เซริ ฟ์ เวอรจ์ ะส่งขอ้ ความวา่ 'Welcome to the chat room' (บรรทดั ท่ี 31) ไปใหไ้ คลเอน็ ตก์ ่อน จากนนั้ ไคลเอน็ ตจ์ ะส่งชอ่ื ผใู้ ชก้ ลบั ไปใหเ้ ซริ ฟ์ เวอร์ (บรรทดั ท่ี 32) ขนั้ ตอนต่อไปเซริ ฟ์ เวอรจ์ ะส่งขอ้ มลู การสนทนาทม่ี อี ย่ทู งั้ หมดในฐานขอ้ มลู ไปใหก้ บั ไคลเอน็ ต์ (บรรทดั ท่ี 34 - 35) จากนนั้ เซริ ฟ์ เวอรจ์ ะทาการวนลปู ดว้ ยคาสงั่ while True: ซง่ึ จะทาใหโ้ ปรแกรมทางานไปเรอ่ื ยๆ จนกวา่ จะปิดโปรแกรม บรรทดั ท่ี 37 – 49 โปรแกรมเซริ ฟ์ เวอรจ์ ะรบั ขอ้ มลู จากไคลเอน็ ตเ์ ครอ่ื งใดเครอ่ื ง หน่งึ ทส่ี ่งเขา้ มา และส่งขอ้ ความดงั กลา่ วไปใหก้ บั ไคลเอน็ ตเ์ ครอ่ื งอ่นื ๆ พรอ้ มกนั ทงั้ หมด ถา้ ไคลเอน็ ต์ เครอ่ื งใดๆ กดป่มุ Enter 1 ครงั้ เซริ ฟ์ เวอรจ์ ะถอื วา่ เป็นการยตุ กิ ารเช่อื มต่อของไคลเอน็ ตเ์ ครอ่ื งนนั้ ๆ (บรรทดั ท่ี 38) แต่ถา้ เป็นขอ้ ความปกติ เซริ ฟ์ เวอรจ์ ะเรยี กใชง้ านเมธอด broadCastingMessage() เพอ่ื ส่งขอ้ ความไปยงั ทุกๆ ไคลเอน็ ต์ แต่การเรยี กใชเ้ มธอด broadCastingMessage() อาจจะถกู เรยี กใชจ้ าก เธรดอ่นื ๆ พรอ้ มๆ กนั ได้ ดงั นนั้ เซริ ฟ์ เวอรจ์ ะใชค้ ณุ สมบตั กิ ารลอ็ ค (threadLock.acquire()) เมธอด broadCastingMessage() ไวก้ ่อน เพ่อื ไมใ่ หเ้ ธอดอ่นื ๆ เขา้ มาแยง่ ใชง้ าน เมอ่ื ใชง้ านเมธอด broadCastingMessage() เสรจ็ เรยี บรอ้ ยแลว้ เซริ ฟ์ เวอรจ์ ะปลดปลอ่ ยทรพั ยากรดว้ ยเมธอด threadLock.release() เพ่อื ใหเ้ ธรด (ไคลเอน็ ตเ์ ครอ่ื งอ่นื ๆ) อ่นื ๆ เรยี กใชเ้ มธอด broadCastingMessage() บา้ ง บรรทดั ท่ี 53 ผเู้ ขยี นทาการเปลย่ี นขนาดของบฟั เฟอรจ์ าก 1024 จากในตวั อย่างทผ่ี า่ นมาเป็น 4098 เน่อื งจากตอ้ งการใหโ้ ปรแกรมสามารถรองรบั ขอ้ ความทม่ี ขี นาดยาวขน้ึ บรรทดั ท่ี 56 สรา้ งตวั แปร ชนิดลสิ ตช์ ่อื CONNECTIONS_LIST เพ่อื ใชส้ าหรบั เกบ็ ออ็ ปเจก็ ตข์ อง Socket ไคลเอน็ ตท์ กุ ๆ เครอ่ื ง เอาไว้ รวมถงึ ออ็ ปเจก็ ต์ Socket ของเซริ ฟ์ เวอรด์ ว้ ย เพราะตอ้ งการใชอ้ า้ งองิ ในกรณที ต่ี อ้ งการสง่ ขอ้ ความไปยงั ทกุ ๆ เครอ่ื งไคลเอน็ ตน์ นั่ เอง บรรทดั ท่ี 58 สรา้ งตวั แปรสาหรบั ใชล้ ๊อคและปลดปลอ่ ย ทรพั ยากรในกรณีทอ่ี าจจะเกดิ การแยง่ ชงิ กนั เขา้ ใชง้ านของไคลเอน็ ตเ์ ธรด บรรทดั ท่ี 59 สรา้ งอนิ สแตนซ์ ของคลาส chatDatabase เพ่อื ใชเ้ กบ็ ขอ้ มลู การสนทนา บรรทดั ท่ี 64 เพมิ่ ออ็ ปเจก็ ต์ Socket ของ เซริ ฟ์ เวอรไ์ วใ้ นตวั แปร CONNECTIONS_LIST เพ่อื ใชส้ าหรบั อา้ งองิ ในอนาคต บรรทดั ท่ี 67 - 77 เซริ ฟ์ เวอรท์ าการวนลปู รอรบั การเชอ่ื มต่อจากไคลเอน็ ต์ เมอ่ื มไี คลเอน็ ตร์ อ้ งขอเขา้ มา และเซริ ฟ์ เวอร์ ยอมรบั การเชอ่ื มต่อดงั กลา่ วแลว้ เซริ ฟ์ เวอรจ์ ะเกบ็ ออ็ ปเจก็ ต์ Socket ของไคลเอน็ ตไ์ วใ้ นตวั แปร CONNECTIONS_LIST ไปเรอ่ื ยๆ จากนนั้ เซริ ฟ์ เวอรจ์ ะสรา้ งเธรดของไคลเอน็ ต์ โดยมพี ารามเิ ตอร์ 3 ตวั คอื ออ็ ปเจก็ ต์ Socket ของไคลเอน็ ต์ (client), ออ็ ปเจก็ ตฐ์ านขอ้ มลู (record) และหมายเลขไอพี ห น้ า 372
แอดเดรส (address) แลว้ เซริ ฟ์ เวอรก์ ท็ าการเรม่ิ ตน้ เธรดทนั ที (บรรทดั ท่ี 77) ส่งผลใหเ้ ธรดแต่ละเธรดท่ี สรา้ งขน้ึ จะดแู ลไคลเอน็ ตแ์ ต่ละเครอ่ื งแบบ 1 เธรด ต่อ 1 เครอ่ื งนนั่ เอง Note: ในตวั อยา่ งทผ่ี ่านมาทงั้ หมด โปรแกรมจะทางานอยภู่ ายในเครอ่ื งเดยี วเท่านนั้ ถา้ ผเู้ ขยี นโปรแกรมตอ้ งการใหเ้ ซริ ฟ์ เวอรแ์ ละไคลเอน็ ตท์ างานอยตู่ ่างเครอ่ื งกนั ใหก้ าหนด HOST จาก 'localhost' เป็นหมายเลขไอพแี อดเดรสแทน เช่น HOST = '192.168.1.10' และ ไคลเอน็ ตจ์ ะตอ้ งกาหนด HOST ใหเ้ หมอื นกบั ฝัง่ เซริ ฟ์ เวอรด์ ว้ ย สร้างโปรแกรมชื่อ ChatClient.py เพื่อทาหน้าท่ีเป็นไคลเอน็ ต์ Program Example ChatClient.py 1 '''Chat Client for a multi-client chat room''' 2 from socket import * 3 4 HOST = 'localhost' 5 PORT = 5000 6 BUFSIZE = 4096 7 ADDRESS = (HOST, PORT) #(127.0.0.1, 5000) 8 9 server = socket(AF_INET, SOCK_STREAM) 10 server.connect(ADDRESS) 11 messageFromServer = bytes.decode(server.recv(BUFSIZE)) 12 print(messageFromServer) 13 name = input('Enter your name: ') 14 userName = str.encode(name) 15 server.send(userName) 16 17 while True: 18 receiveMessage = bytes.decode(server.recv(BUFSIZE)) 19 if not receiveMessage: 20 print('Server disconnected') 21 break 22 print(receiveMessage) 23 sendMessage = input('> ') 24 if not sendMessage: 25 print('Server disconnected') 26 break 27 server.send(str.encode(sendMessage)) 28 server.close() จากตวั อยา่ งโปรแกรม ChatClient.py แสดงการสรา้ งไคลเอน็ ต์ Chat ซง่ึ เหมอื นในตวั อยา่ งท่ี 15.8 ทกุ ประการ แตกต่างกนั คอื ขนาดของบฟั เฟอรก์ าหนดใหม้ ขี นาดใหญ่ขน้ึ จาก 1024 เป็น 4096 ดงั ในบรรทดั ท่ี 6 ขนั้ ตอนการสงั่ รนั โปรแกรม ChatServer และ ChatClient ห น้ า 373
1) สงั่ รนั ChatServer.py ผา่ นทาง Python IDLE โดยการกดป่มุ F5 หรอื เลอื กเมนู Run Run Module F5 ดงั รปู 2) สงั่ รนั ChatClient.py ผา่ นทาง MS-DOS โดยใชค้ าสงั่ python ChatClient.py และกดป่มุ Enter จากนนั้ ใหท้ ดสอบ Chat ดงั รปู ดา้ นลา่ ง จบบทท่ี 15 ห น้ า 374
บทท่ี 16 การเขียนโปรแกรมระบบสารสนเทศภมู ิศาสตรเ์ บอื้ งต้น (Introduction to Geographic Information System Programming: GISP) ท่ีมา: www.winona.edu 1. ระบบสารสนเทศภมู ิศาสตร์ (Geographic Information System: GIS ) ระบบสารสนเทศภมู ศิ าสตร์ คอื กระบวนการทางานเกย่ี วกบั ขอ้ มลู ในเชงิ พน้ื ทด่ี ว้ ยระบบ คอมพวิ เตอร์ เพอ่ื ใชก้ าหนดขอ้ มลู และสารสนเทศทม่ี คี วามสมั พนั ธก์ บั ตาแหน่งในเชงิ พน้ื ท่ี เชน่ ทอ่ี ยู่ อาศยั ทรพั ยากรธรรมชาติ โดยสมั พนั ธก์ บั ตาแหน่งในแผนท่ี เสน้ รงุ้ และเสน้ แวง ขอ้ มลู และแผนทใ่ี น GIS เป็นระบบขอ้ มลู สารสนเทศทอ่ี ยใู่ นรปู ของตารางขอ้ มลู และฐานขอ้ มลู ซง่ึ รปู แบบและความสมั พนั ธข์ องขอ้ มลู เชงิ พน้ื ทท่ี งั้ หลายสามารถนามาวเิ คราะหด์ ว้ ย GIS เพอ่ื ทาใหส้ อ่ื ความหมายในเรอ่ื งการเปลย่ี นแปลงทส่ี มั พนั ธก์ บั เวลาได้ เช่น การแพรข่ ยายของโรคระบาด การ เคล่อื นยา้ ยถนิ่ ฐาน การบุกรกุ ทาลาย การเปลย่ี นแปลงของการใชพ้ น้ื ท่ี ฯลฯ ขอ้ มลู เหลา่ น้ี เมอ่ื ปรากฏบน แผนทท่ี าใหส้ ามารถแปลและส่อื ความหมายใชง้ านไดง้ า่ ย (ขอ้ มลู ตน้ ฉบบั ทงั้ หมดคดั ลอกมาจาก http://www.gisthai.org/) GIS เป็นระบบขอ้ มลู ขา่ วสารทเ่ี กบ็ ไวใ้ นคอมพวิ เตอร์ แต่สามารถแปลความหมายเช่อื มโยงกบั สภาพภมู ศิ าสตรอ์ ่นื ๆ สภาพการทางานของระบบสมั พนั ธก์ บั สดั ส่วนระยะทางและพน้ื ทจ่ี รงิ บนแผนท่ี ขอ้ มลู ทจ่ี ดั เกบ็ ใน GIS มลี กั ษณะเป็นขอ้ มลู เชงิ พน้ื ท่ี (Spatial Data) ทแ่ี สดงในรปู ของภาพ (graphic) แผนท่ี (map) ทเ่ี ช่อื มโยงกบั ขอ้ มลู เชงิ บรรยาย (Attribute Data) หรอื ฐานขอ้ มลู (Database) การ เช่อื มโยงขอ้ มลู ทงั้ สองประเภทเขา้ ดว้ ยกนั จะทาใหผ้ ใู้ ชส้ ามารถทจ่ี ะแสดงขอ้ มลู ทงั้ สองประเภทได้ พรอ้ มๆ กนั ห น้ า 375
องคป์ ระกอบของ GIS (Components of GIS) องคป์ ระกอบหลกั ของระบบ GIS จดั แบง่ ออกเป็น 5 ส่วนใหญ่ๆ คอื อุปกรณ์คอมพวิ เตอร์ (Hardware) โปรแกรม (Software) ขนั้ ตอนการทางาน (Methods) ขอ้ มลู (Data) และบุคลากร (People) ดงั รปู ท่ี 16.1 โดยมรี ายละเอยี ดของแต่ละองคป์ ระกอบดงั ต่อไปน้ี 1. อุปกรณ์คอมพวิ เตอร์ คอื เครอ่ื งคอมพวิ เตอรร์ วมไปถงึ อุปกรณ์ต่อพว่ งต่างๆ เช่น Digitizer, Scanner, Plotter, Printer หรอื อ่นื ๆ เพ่อื ใชใ้ นการนาเขา้ ขอ้ มลู ประมวลผล แสดงผล และผลติ ผลลพั ธข์ องการทางาน 2. ซอฟตแ์ วร์ คอื ชุดของคาสงั่ สาเรจ็ รปู เช่น โปรแกรม ArcGIS, QGIS, MapInfo เป็นตน้ ซง่ึ ประกอบดว้ ยฟังก์ชนั การทางานและเครอ่ื งมอื ทจ่ี าเป็นต่างๆ สาหรบั นาเขา้ และ ปรบั แต่งขอ้ มลู จดั การระบบฐานขอ้ มลู เรยี กคน้ วเิ คราะห์ และจาลองภาพ 3. ขอ้ มลู คอื ขอ้ มลู ต่างๆ ทจ่ี ะใชใ้ นระบบ GIS และถูกจดั เกบ็ ในรปู แบบของฐานขอ้ มลู โดย ไดร้ บั การดแู ลจากระบบจดั การฐานขอ้ มลู หรอื DBMS ขอ้ มลู จะเป็นองคป์ ระกอบท่ี สาคญั รองลงมาจากบคุ ลากร 4. บคุ ลากร คอื ผปู้ ฏบิ ตั งิ านซง่ึ เกย่ี วขอ้ งกบั ระบบสารสนเทศภมู ศิ าสตร์ เชน่ ผนู้ าเขา้ ขอ้ มลู ชา่ งเทคนิค ผดู้ แู ลระบบฐานขอ้ มลู ผเู้ ชย่ี วชาญสาหรบั วเิ คราะหข์ อ้ มลู ผบู้ รหิ าร ซง่ึ ตอ้ งใชข้ อ้ มลู ในการตดั สนิ ใจ บุคลากรจะเป็นองคป์ ระกอบทส่ี าคญั ทส่ี ุดในระบบ GIS เน่อื งจากถ้าขาดบุคลากร ขอ้ มลู ทม่ี อี ยมู่ ากมายมหาศาลนนั้ กจ็ ะเป็นเพยี งขยะไมม่ คี ุณคา่ ใดเลยเพราะไมไ่ ดถ้ กู นาไปใชง้ าน อาจจะกล่าวไดว้ า่ ถา้ ขาดบุคลากรกจ็ ะไม่มรี ะบบ GIS 5. วธิ กี ารหรอื ขนั้ ตอนการทางาน คอื วธิ กี ารทอ่ี งคก์ รนนั้ ๆ นาเอาระบบ GIS ไปใชง้ านโดย แต่ละระบบ แต่ละองคก์ รยอ่ มมคี วามแตกต่างกนั ออกไป ฉะนนั้ ผปู้ ฏบิ ตั งิ านตอ้ งเลอื ก วธิ กี ารในการจดั การกบั ปัญหาทเ่ี หมาะสมทส่ี ดุ สาหรบั ของหน่วยงานนนั้ ๆ เอง รปู ท่ี 16.1 แสดงองคป์ ระกอบของ GIS ห น้ า 376
สาหรบั ความรพู้ น้ื ฐานเกย่ี วกบั ระบบ GIS อยนู่ อกเหนือหนงั สอื เลม่ น้ี ผทู้ ส่ี นใจสามารถอ่าน เพม่ิ เตมิ ไดจ้ ากหนงั สอื หรอื ตาราเกย่ี วกบั ระบบสารสนเทศภมู ศิ าสตรท์ ม่ี จี าหน่ายอยทู่ วั่ ไป หรอื อ่านได้ จากเวบ็ ไซต์ http://www.gisthai.org/ การเขยี นโปรแกรมกบั ระบบ ArcGIS ผเู้ ขยี นโปรแกรมตอ้ งมี ความรเู้ กย่ี วกบั การเขยี นโปรแกรมเชงิ วตั ถุดว้ ย ซง่ึ สามารถอ่านไดจ้ ากบทท่ี 11 ในหนงั สอื เลม่ น้ี ในบทน้ีผเู้ ขยี นมเี ป้าหมายเพอ่ื ใหผ้ อู้ ่านสามารถเขยี นโปรแกรมไพธอนควบคุมการทางานของ ซอฟตแ์ วรใ์ นองคป์ ระกอบขอ้ ท่ี 2 ของ GIS ดา้ นบน ใหส้ ามารถทางานไดอ้ ย่างมปี ระสทิ ธภิ าพ ดงั นนั้ ผอู้ ่านควรตอ้ งมคี วามรพู้ น้ื ฐานเกย่ี วกบั การใชง้ านระบบ GIS (ArcGIS) มาบา้ งพอสมควร สาหรบั ซอฟตแ์ วรท์ ผ่ี เู้ ขยี นนามาทดสอบการเขยี นโปรแกรมคอื ArcGIS (ESRI) เวอรช์ นั 10.x ซง่ึ เป็นซอฟตแ์ วร์ ทไ่ี ดร้ บั ความนิยมเป็นอยา่ งมากในการประมวลผลขอ้ มลู ดา้ น GIS ในปัจจบุ นั 2. การติดตงั้ โปรแกรม ArcGIS โปรแกรม ArcGIS เป็นซอฟตแ์ วรท์ ใ่ี ชส้ าหรบั ประมวลผลงานดา้ น GIS ซง่ึ ครอบคลุม การทางานดา้ น GIS เกอื บทงั้ หมด ผใู้ ชง้ านสามารถดาวน์โหลดและตดิ ตงั้ ทดลองใชง้ านไดฟ้ รี ประมาณ 60 วนั ไดจ้ าก http://www.esri.com/software/arcgis/arcgis-for-desktop/free-trial ปัจจบุ นั เป็นเวอรช์ นั ท่ี 10.2 ในขนั้ ตอนก่อนการดาวน์โหลดผใู้ ชต้ อ้ งทาการลงทะบยี นเพ่อื ขอ Authorize number ผา่ นทาง email สาหรบั ขนั้ ตอนการตดิ ตงั้ ArcGIS เวอรช์ นั ทดลอง (ArcGIS_Desktop_10xx_xxxxxx.exe) ดงั น้คี อื ตอ้ งตดิ ตงั้ Microsoft .NET Framework 3.5 SP1 หรอื สงู กว่าก่อนเสมอ ถา้ มซี อพตแ์ วร์ ArcGIS เวอรช์ นั เก่า ใหถ้ อนการตดิ ตงั้ เวอรช์ นั เดมิ ออกก่อน ดบั เบลิ คลกิ แฟ้มช่อื ArcGIS_Desktop_10xx_xxxxxx.exe เพ่อื ทาการขยาย แฟ้มทใ่ี ชใ้ นการตดิ ตงั้ (ใหเ้ กบ็ แฟ้มทข่ี ยายใน C:\\temp เป็นตน้ ) ดบั เบลิ คลกิ แฟ้ม Setup.exe เพ่อื ตดิ ตงั้ ArcGIS จะปรากฏหน้าต่างแสดง ลขิ สทิ ธิ ์(license agreement) ใหผ้ ใู้ ชเ้ ลอื ก accept เลอื กตดิ ตงั้ แบบ สมบรู ณ์ (Complete) คลกิ next เพอ่ื ยอมรบั การตดิ ตงั้ ไปเรอ่ื ยๆ จนกว่าจะ เสรจ็ สน้ิ การตดิ ตงั้ การลงทะเบยี นเพอ่ื ขอใชง้ าน ArcGIS เวอรช์ นั ทดลอง ห น้ า 377
โปรแกรมจะเปิด ArcGIS administrator ใหผ้ ใู้ ชเ้ ลอื ก ArcGIS for Desktop Advanced (Single Use) และคลกิ ท่ี Authorize Now เลอื ก I have installed my software and need to authorize it และคลกิ next เลอื ก Authorize with Esri now using the Internet (คอมพวิ เตอรต์ อ้ งเช่อื มต่อ อนิ เทอรเ์ น็ตดว้ ย แต่ถา้ ไมไ่ ดเ้ ช่อื มต่ออนิ เทอรเ์ น็ตใหเ้ ลอื ก authorize ผ่าน email แทนกไ็ ด)้ และคลกิ next ป้อนขอ้ มลู ผใู้ ชง้ านใหค้ รบและคลกิ next ป้อนขอ้ มลู Authorization number ซง่ึ ขน้ึ ตน้ ดว้ ย EVAxxxxxxxxx เชน่ EVA280525629 (ตวั เลขดงั กลา่ วไดจ้ ากขนั้ ตอนก่อนการดาวน์โหลด ซอฟตแ์ วร)์ เมอ่ื ลงทะเบยี นแบบ online เสรจ็ แลว้ (ใชเ้ วลาประมาณ 30 – 60 วนิ าท)ี ให้ เลอื ก finish ปิดโปรแกรม ArcGIS administrator, โปรแกรมพรอ้ มใชง้ านแลว้ สาหรบั โปรแกรม ArcGIS รุน่ ทดลองสามารถใชง้ านซอฟตแ์ วรไ์ ด้ครบทกุ ฟังชนั แต่ใชง้ านได้ เพยี ง 60 วนั เทา่ นนั้ 1. การเรยี กใชง้ านไพธอน ขณะทาการตดิ ตงั้ ArcGIS เวอรช์ นั 10.x โปรแกรมจะทาการตดิ ตงั้ ไพธอนเวอรช์ นั 2.7.x ใหโ้ ดยอตั โนมตั ิ ผใู้ ชง้ านสามารถเรยี กใชไ้ พธอนได้ 2 แบบคอื เรยี กใชง้ านจากภายใน ArcGIS เรยี กว่า Python shell window โดยคลกิ ทเ่ี มนู geoprocessing Python แต่วธิ นี ้จี ะเป็นแบบ Interactive mode คอื ไพธอน จะรบั คาสงั่ ทลี ะบรรทดั พรอ้ มกบั ประมวลผลคาสงั่ ทนั ที ดงั รปู ท่ี 16.2 รปู ท่ี 16.2 Python shell window ห น้ า 378
หรอื เรยี กจากภายนอกโปรแกรม ArcGIS โดยเลอื ก Start Programs Python 2.7.x IDLE (Python GUI) สาหรบั วนิ โดวส์ 7 และ 8 กดป่มุ windows บนแป้นพมิ พ์ IDLE (Python GUI) ดงั รปู ท่ี 16.3 รปู ที่ 16.3 IDLE (Python GUI) สาหรบั วธิ นี ้ผี เู้ ขยี นโปรแกรมสามารถเขยี นโปรแกรมในลกั ษณะเป็นสครปิ ตไ์ ด้ คอื เขยี น โปรแกรมหลายๆ คาสงั่ รวมเขา้ ไวด้ ว้ ยกนั เมอ่ื เขยี นโปรแกรมเสรจ็ แลว้ ตอ้ งทาการบนั ทกึ เป็นแฟ้มก่อน แลว้ จงึ สามารถสงั่ ใหโ้ ปรแกรมประมวลผลได้ 3. การเขียนโปรแกรมไพธอนกบั GIS (ArcGIS 10.x) การเขยี นสครปิ ตส์ าหรบั การประมวลผลขอ้ มลู ทางภูมศิ าสตร์ (Geoprocessing script) คอื การ ประมวลผลขอ้ มลู ทางภมู ศิ าสตรก์ บั ขอ้ มลู ทม่ี จี านวนมาก ใชเ้ วลานาน และตอ้ งทางานซ้าๆ ในรปู แบบเดมิ ๆ จนกว่าจะไดค้ าตอบทต่ี อ้ งการ การเขยี นสครปิ ตจ์ ะชว่ ยใหผ้ ูเ้ ขยี นโปรแกรมสามารถประหยดั ทรพั ยากรในการประมวลผลลงไดม้ าก สาหรบั ArcGIS ไดจ้ ดั เตรยี มชดุ คาสงั่ หรอื โมดลู สาหรบั งานดา้ น Geoprocessing ไว้ มชี ่อื ว่า Arcpy site package เพ่อื ช่วยอานวยความสะดวกในการจดั การควบคุมและ เขา้ ถงึ สภาพแวดลอ้ มของ ArcGIS 3.1 การเรยี กใชง้ าน Arcpy site package ดว้ ยไพธอน 1. เปิดแฟ้มเอกสารทใ่ี ชส้ าหรบั เกบ็ แผนทช่ี ่อื Crime1 (มสี ว่ นขยายเป็น .mxd) ในไดเรคท รอรี C:\\PythonData\\Ch16 (ใหท้ าการคดั ลอกไดเรคทรอรี PythonData จากแผ่น CD ลงใน C:\\PythonData) ดว้ ยโปรแกรม ArcMap ดงั น้ี เมนู File Open C:\\PythonData\\Ch16\\Crime1.mxd 2. เปิดโปรแกรม Python window เพอ่ื เขยี นสครปิ ต์จาก เมนู Geoprocessing Python ห น้ า 379
3. ในหน้าต่างของ Python window ใหท้ าการ import arcpy package ดงั น้ี >>> import arcpy ขณะทผ่ี เู้ ขยี นโปรแกรมกาลงั พมิ พค์ าสงั่ ใดๆ ใน Python window โปรแกรมจะแสดงคาสงั่ ท่ี สอดคลอ้ งกบั การพมิ พข์ องผเู้ ขยี นใหเ้ หน็ เพ่อื อานวยความสะดวกในการทางาน ดงั รปู ดา้ นบน Tips: เมอ่ื ผเู้ ขยี นโปรแกรมพมิ พค์ าสงั่ ใดๆ ใน Python window โปรแกรมจะแสดงคาสงั่ ท่ี เกย่ี วขอ้ งออกมาใหเ้ หน็ ผเู้ ขยี นโปรแกรมสามารถเลอื กใชค้ าสงั่ ใดๆ ดว้ ยการกดป่มุ Tab หรอื เลอ่ื นเมาสไ์ ปทค่ี าสงั่ ทต่ี อ้ งการแลว้ คลกิ เลอื กคาสงั่ นนั้ ๆ กไ็ ด้ 4. เมอ่ื ทาการ import arcpy package เขา้ มาในโปรแกรมแลว้ ผเู้ ขยี นโปรแกรมสามารถ เรยี กใชง้ านแอตทรบิ วิ , เมธอด, geoprocessing tools, ฟังชนั หรอื คลาสในโปรแกรม ArcGIS ไดท้ งั้ หมด โดยใชส้ ญั ลกั ษณ์ . เพราะ arcpy package ถูกเขยี นขน้ึ จากแนวคดิ การโปรแกรมเชงิ วตั ถุนนั่ เอง จากรปู ตวั อยา่ งดา้ นบน เมอ่ื พมิ พ์ arcpy. โปรแกรมจะแสดงแอตทรบิ วิ ต์, เมธอด, ฟังชนั , geoprocessing tools และคลาสทงั้ หมดใน ArcGIS (ขน้ึ อยกู่ บั license ทต่ี ดิ ตงั้ วา่ เป็นประเภท Basic, Standard หรอื Advanced) ใหผ้ เู้ ขยี นโปรแกรมเลอื กใชง้ าน โดยการกดป่มุ Tab หรอื เลอ่ื นเมาสไ์ ปยงั คาสงั่ ทต่ี อ้ งการกไ็ ด้ ดงั รปู ดา้ นลา่ ง ห น้ า 380
Search
Read the Text Version
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
- 193
- 194
- 195
- 196
- 197
- 198
- 199
- 200
- 201
- 202
- 203
- 204
- 205
- 206
- 207
- 208
- 209
- 210
- 211
- 212
- 213
- 214
- 215
- 216
- 217
- 218
- 219
- 220
- 221
- 222
- 223
- 224
- 225
- 226
- 227
- 228
- 229
- 230
- 231
- 232
- 233
- 234
- 235
- 236
- 237
- 238
- 239
- 240
- 241
- 242
- 243
- 244
- 245
- 246
- 247
- 248
- 249
- 250
- 251
- 252
- 253
- 254
- 255
- 256
- 257
- 258
- 259
- 260
- 261
- 262
- 263
- 264
- 265
- 266
- 267
- 268
- 269
- 270
- 271
- 272
- 273
- 274
- 275
- 276
- 277
- 278
- 279
- 280
- 281
- 282
- 283
- 284
- 285
- 286
- 287
- 288
- 289
- 290
- 291
- 292
- 293
- 294
- 295
- 296
- 297
- 298
- 299
- 300
- 301
- 302
- 303
- 304
- 305
- 306
- 307
- 308
- 309
- 310
- 311
- 312
- 313
- 314
- 315
- 316
- 317
- 318
- 319
- 320
- 321
- 322
- 323
- 324
- 325
- 326
- 327
- 328
- 329
- 330
- 331
- 332
- 333
- 334
- 335
- 336
- 337
- 338
- 339
- 340
- 341
- 342
- 343
- 344
- 345
- 346
- 347
- 348
- 349
- 350
- 351
- 352
- 353
- 354
- 355
- 356
- 357
- 358
- 359
- 360
- 361
- 362
- 363
- 364
- 365
- 366
- 367
- 368
- 369
- 370
- 371
- 372
- 373
- 374
- 375
- 376
- 377
- 378
- 379
- 380
- 381
- 382
- 383
- 384
- 385
- 386
- 387
- 388
- 389
- 390
- 391
- 392
- 393
- 394
- 395
- 396
- 397
- 398
- 399
- 400
- 401
- 402
- 403
- 404
- 405
- 406
- 407
- 408
- 409
- 410
- 411
- 412
- 413
- 414
- 415
- 416
- 417
- 418
- 419
- 420
- 421
- 422
- 423
- 424
- 425
- 426
- 427
- 428
- 429
- 430
- 431
- 432
- 433
- 434
- 435
- 436
- 437
- 438
- 439
- 440
- 441
- 442
- 443
- 444
- 445
- 446
- 447
- 448
- 449
- 450
- 451
- 452
- 453
- 454
- 455
- 456
- 457
- 458
- 459
- 460
- 461
- 462
- 463
- 464
- 465
- 466
- 467
- 468
- 469
- 470
- 471
- 472
- 473
- 474
- 475
- 476
- 477
- 478
- 479
- 480
- 481
- 482
- 483
- 484
- 485
- 486
- 487
- 488
- 489
- 490
- 491
- 492
- 493
- 494
- 495
- 496
- 497
- 498
- 499
- 500
- 501
- 502
- 503
- 504
- 505
- 506
- 507
- 508
- 509
- 510
- 511
- 512
- 513
- 514
- 515
- 516
- 517
- 518
- 519
- 520
- 521
- 522
- 523
- 524
- 525
- 526
- 527
- 528
- 529
- 530
- 531
- 532
- 533
- 534
- 535
- 536
- 537
- 538
- 539
- 540
- 541
- 542
- 543
- 544
- 545
- 546
- 547
- 548
- 549
- 550
- 551
- 552
- 553
- 554
- 555
- 556
- 557
- 558
- 559
- 560
- 561
- 562
- 563
- 564
- 565
- 566
- 567
- 568
- 569
- 570
- 571
- 572
- 573
- 574
- 575
- 576
- 577
- 578
- 579
- 580
- 581
- 582
- 583
- 584
- 585
- 586
- 587
- 588
- 589
- 590
- 591
- 592
- 593
- 594
- 595
- 596
- 597
- 598
- 599
- 600
- 601
- 602
- 603
- 604
- 605
- 606
- 607
- 608
- 609
- 610
- 611
- 612
- 613
- 614
- 615
- 616
- 617
- 618
- 619
- 620
- 621
- 622
- 623
- 624
- 625
- 626
- 627
- 628
- 629
- 630
- 631
- 632
- 633
- 634
- 635
- 636
- 637
- 638
- 639
- 640
- 641
- 642
- 643
- 644
- 645
- 646
- 647
- 648
- 649
- 650
- 651
- 652
- 653
- 654
- 655
- 656
- 657
- 658
- 659
- 660
- 661
- 1 - 50
- 51 - 100
- 101 - 150
- 151 - 200
- 201 - 250
- 251 - 300
- 301 - 350
- 351 - 400
- 401 - 450
- 451 - 500
- 501 - 550
- 551 - 600
- 601 - 650
- 651 - 661
Pages: