Trang 1 trên tổng số 6 123... Cuối cùngCuối cùng
Từ 1 tới 10 trên tổng số 59 kết quả

Đề tài: Lập Trình Mạng Với Thư Viện Winsock trên VC++

  1. #1
    Ngày gia nhập
    07 2006
    Nơi ở
    Hanoi, Vietnam
    Bài viết
    2,750

    Mặc định Lập Trình Mạng Với Thư Viện Winsock trên VC++

    I. KHỞI ĐỘNG WINSOCK

    Để lập trình được Winsock chúng ta sẽ khai báo thư viện winsock2.h (chứa các prototypes) và 1 file lib (chính là file .cpp đã được biên dịch thành .lib) có tên là ws2_2.lib.

    Bây giờ hãy tạo 1 project Windows32 Console Project.
    Lưu ý: Chúng ta không khai báo các thư viện trong file .cpp mà khai báo trong file stdafx.h. Đây là cách khai báo thư viện của Visual C++.
    Visual C++ Code:
    1. #include <stdio.h>
    2. #include <tchar.h>
    3. ...
    4. #include <winsock2.h>
    5. #pragma comment (lib,"ws2_32.lib")


    Và bây giờ sẽ là những hàm để khởi tạo Winsock:

    Visual C++ Code:
    1. int WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData);

    Trong đó:
    - wVersionRequested là phiên bản thư viện mà mình sử dụng. Ở đây sẽ là giá trị 0x0202 có nghĩa là phiên bản 2.2. Chúng ta có thể dùng macro MAKEWORD(2,2) để trả về giá trị 0x0202.
    - lpWSData là một số thông tin bổ sung sẽ được trả về sau khi gọi khởi tạo Winsock.:

    Visual C++ Code:
    1. typedef struct WSAData {
    2.     WORD            wVersion;         // Phiên bản hiện tại
    3.     WORD            wHighVersion;     // Phiên bản có thể hỗ trợ
    4.     char            szDescription[WSADESCRIPTION_LEN + 1]; // Ghi chú
    5.     char            szSystemStatus[WSASYS_STATUS_LEN + 1]; // Trạng thái hệ thống
    6.  
    7.     unsigned short  iMaxSockets;     // Không sử dụng từ Version 2 trở đi
    8.     unsigned short  iMaxUdpDg;         // Không sử dụng từ Version 2 trở đi
    9.     char FAR *      lpVendorInfo;    // Không sử dụng từ Version 2 trở đi
    10. } WSADATA, FAR * LPWSADATA;

    Và cuối cùng là hàm hủy Winsock khi kết thúc chương trình.
    Visual C++ Code:
    1. nt WSACleanup (void);

    Chương trình đầu tiên:
    Visual C++ Code:
    1. #include "stdafx.h"
    2.  
    3. using namespace std;
    4.  
    5. int _tmain(int argc, _TCHAR* argv[])
    6. {
    7.     WSADATA SData;
    8.     int iResult = WSAStartup(0x0202,&SData);
    9.     if (iResult!=0){
    10.         cout << "KHONG THE KHOI DONG WINSOCK";
    11.         return 1;
    12.     }
    13.  
    14.     cout << "KHOI TAO SOCKET THANH CONG: \n";
    15.     cout << "Phien ban: "<< SData.wVersion << "\n";  
    16.     cout << "Phien ban co the ho tro: "<< SData.wHighVersion << "\n";
    17.     cout << "Ghi chu: " << SData.szDescription << "\n";
    18.     cout << "Thong tin cau hinh: " << SData.szSystemStatus << "\n";
    19.    
    20.     WSACleanup();
    21.     return 0;
    22. }

    II. SOCKET

    1. Socket là gì?

    “Socket là một cổng logic mà một chương trình sử dụng để kết nối với một chương trình khác chạy trên một máy tính khác trên Internet. Chương trình mạng có thể sử dụng nhiều Socket cùng một lúc, nhờ đó nhiều chương trình có thể sử dụng Internet cùng một lúc.”

    Ở đây ta hiểu Socket trong Winsock như là một “phương tiện” để ứng dụng mạng có thể trao đổi dữ liệu. Nghĩa là 1 Server thì sẽ cần một Socket để lắng nghe, chờ đợi các kết nối từ clientClient thì phải cần có một Socket để kết nối tới Sever.

    2. Khởi tạo Socket
    Chúng ta sử dụng cấu trúc SOCKET để lưu giữ 1 Socket. Và có thể sử dụng hàm sau đây để tạo Socket.
    Visual C++ Code:
    1. SOCKET socket (
    2.     int af,
    3.     int type,
    4.     int protocol
    5. );

    Ví dụ:
    Visual C++ Code:
    1. SOCKET s = socket(AF_INET,SOCK_STREAM,IPPROTO_IP);

    Trong đó:
    * af: Là một con số ID để quyết định Socket của chúng ta sử dụng giao thức (protocol) để kết nối.
    - AF_INET : TCP/IP (Phổ biến nhất hiện nay -> dùng địa chỉ IP để truyền dữ liệu)
    - AF_NETBIOS: NetBIOS (Giao thức dùng tên máy để truyền dữ liệu)
    - AF_APPLETALK: AppleTalk
    - AF_ATM: ATM

    Và ở trong Tut này mình chỉ nghiên cứu tới TCP/IP.

    * type: Quy định giao thức vận chuyển dữ liệu
    Ví dụ với giao thức TCP/IP thì có 2 giao thức cốt lõi là UDP và TCP:
    - SOCK_DGRAM: Hay là giao thức UDP. Khi chương trình chúng ta dùng UDP để truyền dữ liệu thì chuyện gì sẽ xảy ra giữa bên gởi và bên nhận? Bên gửi cứ gửi và gửi và nó không hề quan tâm tới vấn đề bên nhận có nhận được nó hay không?
    => Ưu điểm: Tốc độ truyền dữ liệu nhanh.
    => Nhược điểm: Khả năng sai, mất dữ liệu sẽ rất lớn.

    Vậy dùng UDP khi nào? Những ứng dụng cần dữ liệu tức thời như:
    - Chương trình nghe nhạc trực tuyến. Vấn đề sai bit (vấp khi nghe nhạc) không quan trọng mấy vì yêu cầu của nó là đảm bảo tốc độ nhanh.
    - Chương trình Chat chẳn hạn.
    - Hoặc GameOnline (thỉnh thoảng bạn bị trường hợp LAG chính là do bị mất dữ liệu trên đường truyền đó)

    - SOCK_STREAM: Đây là giao thức TCP. Nó ngược với UDP vì nó đảm bảo giữa bên gửi và bên nhận dữ liệu phải chính xác. Vì vậy 2 bên sẽ phải bắt tay rất nhiều lần khi truyền được dữ liệu (ví dụ như bên gửi sẽ gửi n gói tin (packet), bên nhận sẽ kiểm tra có bị mất hay sai gói tin nào hay không, nếu đủ thì nó sẽ yêu cầu bên gửi gửi tiếp n gói tin tiếp theo, ngược lại thì nó sẽ yêu cầu gửi lại)
    => Ưu điểm: Chất lượng gởi tin cậy.
    => Nhược điểm: Chậm hơn UDP.
    Những ứng dụng như WEB, MAIL, FTP,…

    - SOCK_RAW:
    Là giao thức để kiểm soát mạng, kiểm tra kết nối…
    Ví dụ:
    Start -> Run -> CMD: “ping congdongcviet.com”.
    Nếu bạn nhận được Reply có nghĩa là giữa máy tính của bạn với máy chủ “congdongcviet.com” có “thông mạng” với nhau. Và gói tin mà bạn PING chính là SOCK_RAW (ICMP Packet)

    * protocol: Chỉ định rõ lại giao thức mà thôi. Vì SOCK_RAW có 2 protocol là ICMP và RAW nên nó cần điều này
    - SOCK_DGREAM -> protocol là: IPPROTO_UDP
    - SOCK_STREAM -> protocol là: IPPROTO_IP
    - SOCK_RAW -> protocol có thể là: IPPROTO_RAW hay IPPROTO_ICMP

    Các bạn có thể tham khảo thêm bảng thể hiện các thuộc tính của hàm SOCKET:
    Click vào hình ảnh để lấy hình ảnh lớn

Tên:		SOCKETPROTOCOL.jpg
Lần xem:	2294
Size:		82.6 KB
ID:		4474

    3. Một số hàm lấy thông tin về mạng

    a. Lấy thông tin Socket

    Visual C++ Code:
    1. int WSAEnumProtocols (
    2.     LPINT lpiProtocols,
    3.     LPWSAPROTOCOL_INFO lpProtocolBuffer,
    4.     LPDWORD lpdwBufferLength
    5. );


    lpiProtocols: NULL
    lpProtocolBuffer: Kiểu dữ liệu trả về
    lpdwBufferLength: Kích thước của kiểu dữ liệu

    Tuy nhiên việc sử dụng hàm này còn hơi rườm rà.
    Ví dụ:


    Visual C++ Code:
    1. WSAEnumProtocols(NULL,NULL,&size); // -> Lấy kích thước kiểu dữ liệu
    2. WSAPROTOCOL_INFO *lpProtocolBuffer;
    3. lpProtocolBuffer = (WSAPROTOCOL_INFO*) malloc(size);
    4. WSAEnumProtocols(NULL, lpProtocolBuffer,&size);


    b. Lấy tên máy tính của mình

    Visual C++ Code:
    1. int gethostname(char* name, int namelen);

    Ví dụ:
    Visual C++ Code:
    1. char lpMyPCName[10];
    2. gethostbyname(lpMyPCName,10)
    3. cout<< lpMyPCName;


    c. Làm việc với IP
    “Mình sẽ đi nhanh và giới thiệu sơ qua về phần này. Ở phần Địa Chỉ Mạng sắp tới mình sẽ nói rõ hơn IP”.
    Địa chỉ IP là 1 con số 4 byte để xác định 1 host trên mạng.

    Ví dụ: “192.168.11.1” [Byte1: 192] [Byte2: 168][ [Byte3: 11][ [Byte4: 1]
    Có thể biểu diễn địa chỉ IP: unsigned long (4 bytes)
    Hoặc một char* lpIP;

    Sử dụng inet_addrinet_ntoa để chuyển đổi qua lại giữa u_long và char*

    Visual C++ Code:
    1. u_long YahooAddr = inet_addr("216.109.112.135");
    2. cout << "IP: " << inet_ntoa(*(in_addr*) &YahooAddr) << "\n";


    d. Lấy IP theo tên máy



    Visual C++ Code:
    1. struct hostent* FAR gethostbyname(const char* name);

    Trong đó
    Visual C++ Code:
    1. typedef struct hostent {
    2.   char FAR* h_name;            // Tên máy tính
    3.   char FAR  FAR** h_aliases;        // Bí danh máy tính
    4.   short h_addrtype;            // Kiểu IP (AF_INET)
    5.   short h_length;                // Kích thước IP
    6.   char FAR  FAR** h_addr_list;    // Danh sách các địa chỉ IP
    7.                         // 1 host có thể có 1 hoặc nhiều IP
    8. } HOSTENT,


    Ví dụ như:
    Visual C++ Code:
    1. char lpHostName[100];
    2. hostent *MyPC;    
    3. gethostname(lpHostName,100);
    4. MyPC = gethostbyname(lpHostName);


    e. Lấy tên máy theo địa chỉ IP

    Tương tự nhưng ngược lại.
    Visual C++ Code:
    1. hostent* FAR gethostbyaddr(const char* addr, int len, int type);


    Ví dụ lấy thông tin Yahoo (có địa chỉ IP: 216.109.112.135)
    Visual C++ Code:
    1. hostent *Yahoo;
    2. u_long YahooAddr = inet_addr("216.109.112.135");    
    3. Yahoo = gethostbyaddr((char*)&YahooAddr,4,AF_INET);


    Chương trình mẫu khởi tạo Socket:
    Click vào hình ảnh để lấy hình ảnh lớn

Tên:		Demo2.jpg
Lần xem:	2260
Size:		29.4 KB
ID:		4475

    Tài liệu được gửi bởi eXecutive

    Còn tiếp ...
    Email: admin[@]congdongcviet.com | CC to: info[@]congdongcviet.com
    Phone: 0972 89 7667 (Office: 04 6329 2380)
    Yahoo & Skype: dreaminess_world (Vui lòng chỉ rõ mục đích ngay khi liên hệ, cảm ơn!)

    Một người nào đó coi thường ý thức kỷ luật cũng có nghĩa là người đó đã coi thường tương lai số phận của chính bản thân người đó. Những người coi thường ý thức kỷ luật sẽ không bao giờ có được sự thành công trong sự nghiệp!

  2. #2
    Ngày gia nhập
    07 2006
    Nơi ở
    Hanoi, Vietnam
    Bài viết
    2,750

    III. ĐỊA CHỈ MẠNG

    Bài viết trước mình đã giới thiệu về SOCKET tuy nhiên mình chỉ nói khởi tạo, còn việc để gửi hay nhận gói tin cần phải thêm thông tin về địa chỉ IP cho nó nữa.

    Ở bài viết này mình sẽ tạm gác lại về SOCKET và C/C++ để đi sâu vào kiến thức cơ bản mạng cụ thể là địa chỉ IP. Bởi vì bạn muốn lập trình được chương trình truyền dữ liệu trên protocol TCP/IP mà không biết về IP thì hơi vô lý. Do đó cái bài viết này có phần đi ngoài lề về lập trình 1 xíu.

    1. Địa chỉ IP
    Một HOST muốn giao dịch trên mạng sử dụng giao thức TCP/IP thì cần phải có 1 địa IP duy nhất để xác địch chính nó (như địa chỉ nhà bạn) trên môi trường mạng.

    Hiện nay có 2 loại địa chỉ IP. Đó là IPv4 (4 bytes) và IPv6. Tuy nhiên IPv6 vẫn chưa thể thay thế cho IPv4 được bởi vì số thiết bị dùng IPv4 quá nhiều.

    Cài đặt địa chỉ IP ở đâu ?
    Click vào hình ảnh để lấy hình ảnh lớn

Tên:		SetupIP.jpg
Lần xem:	1651
Size:		67.2 KB
ID:		4476

    Hiện này IPv4 sử dụng rất nhiều và nó sẽ dùng 4bytes (32bit) để biểu diễn 1 địa chỉ
    Cấu trúc địa chỉ bao gồm 2 phần là: Network_idHost_id

    2. Các lớp địa chỉ IP
    Địa chỉ IP có thể phân tới 5 lớp (dựa trên Network_id) nhưng thực tế thì chỉ có 3 lớp A,B,C được sử dụng.

    a. Lớp A
    - Số byte làm Network_id: 1 byte (địa chỉ đầu tiên từ 0 – 127)
    - Số byte là Host_id: 3 bytes còn lại (trong 1 Network không được trùng)
    - Subnet Mask: 255.0.0.0
    - Địa chỉ BroadCast (đại diện cho tất cả các máy trong đường mạng -> Khi gởi tới địa chỉ này thì mọi host trong mạng sẽ nhận). x x x.255.255.255.
    - Địa chỉ Network (đai diện bất kỳ một máy trong mạng – thường dùng để kiểm soát, định tuyến,…) x x x.0.0.0
    Ví dụ: 10.1.1.1

    * Trong lớp A có đường mạng 127.0.0.0 là đường mạng không sử dụng, nó được sử dụng để kiểm tra giao thúc TCP/IP. Ví dụ bạn ping 127.0.0.1 thì luôn nhận reply vì đây là địa chỉ máy tính của bạn cho dù IP của bạn ở lớp nào đi chăng nữa địa chỉ này chỉ sử dụng tại máy tính của bạn mà thôi.

    b. Lớp B
    - Số byte làm Network_id: 2 byte (địa chỉ đầu tiên từ 128 – 191)
    - Số byte là Host_id: 2 bytes còn lại (trong 1 Network không được trùng)
    - Subnet Mask: 255.255.0.0
    - Địa chỉ BroadCast (đại diện cho tất cả các máy trong đường mạng -> Khi gởi tới địa chỉ này thì mọi host trong mạng sẽ nhận). x x x.x x x.255.255.
    - Địa chỉ Network (đai diện bất kỳ một máy trong mạng – thường dùng để kiểm soát, định tuyến,…) x x x.x x x.0.0
    Ví dụ: 172.11.22.3

    c. Lớp C
    - Số byte làm Network_id: 3 byte (địa chỉ đầu tiên từ 192 – 223)
    - Số byte là Host_id: 1 bytes còn lại (trong 1 Network không được trùng)
    - Subnet Mask: 255.255.255.0
    - Địa chỉ BroadCast (đại diện cho tất cả các máy trong đường mạng -> Khi gởi tới địa chỉ này thì mọi host trong mạng sẽ nhận). x x x.x x x.x x x.255.
    - Địa chỉ Network (đai diện bất kỳ một máy trong mạng – thường dùng để kiểm soát, định tuyến,…) x x x.x x x.x x x.0
    Ví dụ: 192.168.23.3

    Vấn đề về Subnet Mask bạn cứ chấp nhận như vậy vì mình cũng không muốn đi sâu vào chuyên môn của mạng, đơn giản là vì đây là BOX C/C++.

    d. Network_id và Host_id có ý nghĩa gì?
    Ví dụ địa chỉ IP lớp C:
    IP: 192.168.10.4
    Thì sẽ có:
    Mask: 255.2255.255.0
    BroadCast: 192.168.10.255
    Network: 192.168.10.0

    Ví dụ mình sẽ có thêm 2 HOST với 2 địa chỉ IP cùng lớp C nữa là: 192.168.10.3 và 192.168.11.2
    Như vậy:
    - Nếu mình ở tại máy tính có địa chỉ 192.168.10.4 (start -> run -> “cmd”)
    + ping 192.168.10.3 thì mình sẽ nhận được reply tức là 2 địa chỉ này “thông mạng” vì cùng Network là: 192.168.10.0
    + ping 192.168.11.2 thì kết quả sẽ là “request time out”. (Không liên lạc được) bởi vì 2 host này ở 2 mạng khác nhau (192.168.10.0 và 192.168.11.0).


    Như vậy:
    - Network_id sẽ quy định host trong đó thuộc “đường mạng” nào. Và các host chỉ thấy nhau khi cùng “đường mạng”
    - Host_id: dùng để phân biệt host với tất cả các host khác trong mạng


    Tiếp tục… Nếu để 2 đường mạng như trên 192.168.10.0 và 192.168.11.0 có thể “liên lạc” với nhau thì sao? Người ta cần định tuyến mạng với thiết bị mạng có tên là Router (Modem ADSL cũng là 1 router) hoặc một máy chủ Sever có dịch vụ RAS (routing), hoặc một Proxy….
    Click vào hình ảnh để lấy hình ảnh lớn

Tên:		Routing.jpg
Lần xem:	1651
Size:		14.6 KB
ID:		4477

    => Router là một thiết bị có 2 card mạng và sẽ có ít nhất 2 địa chỉ IP. Trong hình trên thì Card mạng thứ 2 của hai router phải thiết lập sao cho có cùng đường mạng để “liên lạc với nhau”. Ví dụ như là 192.168.12.0. Như vậy LAN 1 sẽ liên lạc được với LAN 2 thông qua 2 router trên hình.

    3. Tại sao lại phân loại địa chỉ IP
    - Là do nhu cầu của từng môi trường mạng.

    Ví dụ ở lớp A:
    - Network_id sẽ là 1 bytes (8 bits)
    -> Nó sẽ có 2^7 = 127 (đường mạng)
    + Tại sao là 8 bit nhưng lại là 2^7 ? -> Là vì số bit từ làm Subnet là 0 -> 7 (biểu diễn tới 2^7)
    - Host_id là (24 bits):
    -> 2^24 – 2 = 16.777.214 (Hosts) (trừ 2 địa chỉ Broadcast và Subnet)
    => Ở lớp A số đường mạng ít nhưng số host trên đường mạng nhiều -> thích hợp với các công ty lớn

    Ví dụ ở lớp B:
    - Network_id sẽ là 2 bytes (16 bits)
    -> Nó sẽ có 2^15 = 16.384 (đường mạng)
    - Host_id là (16 bits):
    -> 2^16 – 2 = 65.534 (hosts)
    => Số đường mạng lớp B và số host vừa nhau

    Ví dụ ở lớp C:
    - Network_id sẽ là 3 bytes (24 bits)
    -> Nó sẽ có 2^23 = 2.097152 (đường mạng)
    - Host_id là (8 bits):
    -> 2^8 – 2 = 254 (Hosts)
    => Số đường mạng lớp C rất nhiều những host mỗi đường mạng lại rất ít. Và có lẽ lớp C là lớp được sử dụng nhiều trong mạng LAN bởi lẽ với con số PC dưới 100 trên phòng máy thì lớp C là lựa chọn hay nhất.

    Hy vọng tới đây các bạn sẽ hình dung 1 chút ít gì đó về địa chỉ IP.

    4. IP LAN và IP WAN

    a. IP LAN (PRIVATE IP):
    Trong dãy các IP đó thì người ta trích mỗi lớp một khoảng làm IP LAN và nó chỉ có tác dụng giao dịch trong mạng LAN. Chúng ta có thể đặt IP cho card mạng với những khoảng dưới đây.
    Lớp A: 10.0.0.0 đến 10.255.255.255 (số lượng đường mạng 1)
    Lớp B: 172.16.0.0 đến 172.31.255.255 (số lượng đường mạng 16)
    Lớp C: 192.168.0.0 đến 192.168.255.255 (số lượng đường mạng 256)

    b. IP WAN (PUBLIC IP):
    Là các IP còn lại không thuộc IP LAN. Đây là IP mà cả thế giới thấy được, và muốn có bạn phải mua hoặc được ISP cung cấp (về bản chất thì ISP cũng mua các khoảng IP này và cung cấp các dịch vụ Internet như ADSL, mỗi IP là 1 thuê bao ADSL).

    Ví dụ máy tính của bạn có IP là: 172.16.10.2
    - Tại một nơi nào đó trên thế giới bạn truy cập tới địa chỉ này -> Không thể được vì nó là IP LAN, một IP chỉ có giá trị trong mạng LAN )
    - Chúng ta muốn thế giới thấy được chúng ta thì chúng ta phải có IP WAN. Ở gia đình mạng ADSL của có thể có rất nhiều máy tính bên trong mang IP LAN. Nhưng có 1 thiết bị có IP WAN là ADSL Router và mọi người trên thế giới thấy được nó, tuy nhiên đây là địa chỉ động và bị thay đổi liên tục… do đó việc truy cập cũng rất khó.
    Bạn có thể nghiên cứu thêm vấn đề này bằng cách vào google và gõ từ khóa “NAT” ("Network Address Translation")

    5. Địa chỉ IP trong thực tế

    a. Một số địa chỉ IP cần thiết

    Hãy xem mô hình mạng của mình.
    Bạn vào start->run->”cmd” gõ lệnh “ipconfig /all”

    Code:
      IP Address. . . . . . . . . . . . : 192.168.1.254
      Subnet Mask . . . . . . . . . . . : 255.255.255.0
      Default Gateway . . . . . . . . . : 192.168.1.1
      DNS Servers . . . . . . . . . . . : 203.113.131.1
                                          203.113.131.2

    - Default Gateway: Cổng ra của gói tin mà ta muốn gửi đi nếu địa chỉ đến của gói tin đó khác đường mạng. Và đây thường là địa chỉ của những thiết bị làm nhiệm vụ định tuyến (Routing) như ADSL Modem

    Địa chỉ IP 192.168.1.1 (IP LAN) chính là địa chỉ của cái ADSL Moderm. Rõ ràng nó phải cùng đường mạng với máy tính của mình (đường mạng: 192.168.1.0)

    - DNS Servers: dịch vụ phân giải tên miền. Khi ta truy cập internet thì nó sẽ chuyển đổi giữa tên miền -> IP:

    Ví dụ như mình gõ: http://www.google.com --> DNS SERVER-> http://72.14.207.99

    DNS SERVER như là một “cơ sở dữ liệu khổng lồ” mà mỗi record sẽ là một tên miền và 1 IP. Do được phân cấp theo dạng cây “vn -> com -> google” nên khả năng tìm kiếm của DNS rất nhanh.

    DNS của mạng FTP là 203.113.131.1 (IP WAN) và 1 cái dự phòng là 203.113.131.2. Dịch vụ thì VNN (203.162.4.190, 203.162.4.191, 203.162.0.181,… ) và của Viettel thì khác, cũng có thể DNS là GateWay nhưng thực chất trong ADSL Router nhà cung cấp ISP đã cài đặt DNS truy vấn tới các địa chỉ trên rồi.

    b. Truy cập WEB trên Internet

    - Để minh họa hơn về IP,mình sẽ lấy 1 ví dụ về truy cập WEB trên Internet.

    - Bắt đầu khi ta gõ: http://www.google.com/ từ PC có địa chỉ 192.168.1.254
    Click vào hình ảnh để lấy hình ảnh lớn

Tên:		IP.jpg
Lần xem:	1638
Size:		31.0 KB
ID:		4478

    - Host 192.168.1.254 sẽ nhờ DNS SERVER (203.113.131.1) phân giải dùm google.com có địa chỉ IP là bao nhiêu??? (72.14.207.99)

    - Và lúc này có kết quả tương đương: http://72.14.207.99

    - Sau đó host sẽ truy cập tới 72.14.207.99 nhưng thực sự là nó nhờ Gateway (ADSL Router 192.168.1.1) vì khác đường mạng.

    - ADSL Router hiểu như là 1 PC có 2 card mạng
    + 1 cái mang địa chỉ IP LAN để giao dịch bên trong đường mạng 192.168.1.0.
    + 1 cái mang địa chỉ IP WAN thay đổi mỗi lần khởi động do ISP cung cấp (ví dụ như VNN hay FTP, Viettel…) để giao dịch bên ngoài Internet.
    + Và nhiệm vụ của ADSL Router là sẽ định tuyến và xác định đường đi từ Việt Nam qua Singapo (Trung quốc) -> … -> Mỹ để truy cập tới địa chỉ 72.14.207.99. Và lấy nội dung trang WEB về.

    - Cuối cùng nó sẽ trả lại cho host mang địa chỉ 192.168.1.254 và trang WEB của google sẽ hiện ra trên web browser…

    6. Biểu diễn địa chỉ IP trong Winsock
    Quay lại về vấn đề WINSOCK
    Tại sao IP quan trọng trong lập trình mạng như vậy, bởi vì IP rất quan trọng trong mô hình TCP/IP.
    - 1 Server phải có 1 IP để lắng nghe kết nối trên địa chỉ IP của mình.
    - 1 Client muốn kết nối với Server thì phải có IP của Server…

    Trong Winsock người ta sử dụng cấu trúc sockaddr_in để biểu diễn địa chỉ IP.
    C++ Code:
    1. struct sockaddr_in{
    2.     short           sin_family;
    3.     u_short         sin_port;
    4.     struct in_addr  sin_addr;
    5.     char            sin_zero[8];
    6. };


    Trong đó:
    - sin_family: AF_INET
    - sin_port:
    Là port (cổng logic) để lắng nghe kết nối.
    -> 1 ứng dụng Server hay Client rất cần 1 port để liên lạc trên mạng. Bởi vì card mạng sẽ thông qua số hiệu port này để nhận biết được gói tin đang nhận (hay gửi đi) của ứng dụng nào?

    - Các port dưới 1024 là những port dành riêng cho các dịch vụ như:
    Một số port như: 80 (Web), 20,21 (FTP), Mail (25 SMTP, 993 POP3), 53 (DNS), 22 (SSH), 23 (Telnet)...

    Ví dụ như ta gõ http://www.google.com
    - http là giao thức WEB => Port 80.
    - www.google.com => IP: 72.14.207.99
    Như vậy tại Client sẽ khởi tạo 1 port (lớn hơn 1024) kết nối tới địa chỉ 72.14.207.99 qua port 80.

    Dựa vào số port người quản trị có thể kiểm soát được mạng ví dụ như chỉ có lướt WEB (mở port 80, 53) nhưng chặn hết các port khác => Bạn không thể chat và gameonline mặc dù có Internet.

    Với số nguyên u_short (16bit) có nghĩa có thể chọn tới 2^16 = 65535 port sử dụng giao dịch trên mạng. à ứng dụng của chúng ta phải chọn port lớn hơn 1024 nếu hông muốn viết 1 Sever như WEB hay FTP,…

    - sin_addr: là một struct để xác định địa chỉ IP (có thể là IP của Server hoặc IP của chính mình,…)
    - Nếu sin_addr.s_addr = INADDR_ANY; Thì IP sẽ chính là IP của mình. Các Server thường dùng để chọn địa chỉ IP của mình và lắng nghe kết nối..
    - Nếu sin_addr.s_addr = inet_addr(“x x x.x x x.x x x.xx”); Thì IP sẽ là IP chỉ định theo chuỗi. Client phải chỉ định được IP của Server mới kế nối được.

    - sin_zero Để dành 8 byte (không sử dụng).

    Ví dụ:
    C++ Code:
    1. sockaddr_in ip;
    2. ip.sin_family = AF_INET;
    3. ip.sin_port = htons(99999);
    4. ip.sin_addr.s_addr = inet_addr("10.0.0.2");

    (eXcutive)

    Còn tiếp ...
    Email: admin[@]congdongcviet.com | CC to: info[@]congdongcviet.com
    Phone: 0972 89 7667 (Office: 04 6329 2380)
    Yahoo & Skype: dreaminess_world (Vui lòng chỉ rõ mục đích ngay khi liên hệ, cảm ơn!)

    Một người nào đó coi thường ý thức kỷ luật cũng có nghĩa là người đó đã coi thường tương lai số phận của chính bản thân người đó. Những người coi thường ý thức kỷ luật sẽ không bao giờ có được sự thành công trong sự nghiệp!

  3. #3
    Ngày gia nhập
    07 2006
    Nơi ở
    Hanoi, Vietnam
    Bài viết
    2,750

    IV. ỨNG DỤNG CLIENT - SERVER

    1. Ứng dụng Client - Server là gì?
    Trước tới giờ, các bạn lập trình với mục đích là tạo ra được một ứng dụng. Nhưng ứng dụng đó chỉ hoạt động độc lập 1 mình riêng lẽ.

    Mục tiêu lập trình mạng sẽ đưa ra những ứng dụng dạng Client – Server. Tức là sẽ có 2 loại ứng dụng chính đó là Client và Server.

    Quy trình hoạt động của ứng dụng Server – Client như sau:
    - Server có nhiệm vụ của là lắng nghe, chờ đợi kết nối từ Client trên địa chỉ IP của mình với PORT được quy định sẵn. Khi client gởi dữ liệu tới Server thì nó phải giải quyết một công việc là nhận dữ liệu đó -> xử lý -> trả kết quả lại cho Client.
    - Client là ứng dụng được phục vụ, nó chỉ gởi truy vấn và chờ đợi kết quả từ Server.

    Trong mô hình TCP/IP có 2 giao thức là TCP và UDP và 2 giao thức này sẽ quyết định cách thức hoạt động của Client - Server như thế nào?

    a. Hoạt động của Client – Server trong giao thức TCP (SOCK_STREAM)
    Click vào hình ảnh để lấy hình ảnh lớn

Tên:		TCP_Client_Server.jpg
Lần xem:	2148
Size:		51.4 KB
ID:		4479

    b. Hoạt động của Client – Server trong giao thức UDP (SOCK_DGRAM)
    Click vào hình ảnh để lấy hình ảnh lớn

Tên:		UDP_Client_Server.jpg
Lần xem:	1908
Size:		40.1 KB
ID:		4480

    2. Một số hàm liên quan tới gởi và nhận dữ liệu

    a. BIND

    C++ Code:
    1. int bind(
    2.      SOCKET                     s,
    3.      const struct sockaddr FAR* name,
    4.      int                        namelen
    5. );


    Tác dụng dụng của BIND là sẽ giúp cho SOCKET của SERVER biết rằng nó sẽ chờ đợi kết nối và nhận dữ liệu trên IP nào và PORT bao nhiêu?


    PORT nên chọn ở đây nên ở trong khoảng nào:
    - 0 -1023: Là những PORT đã được sử dụng bởi các dịch vụ như WEB, FTP, …
    - 1024-49151: Là PORT dành cho SERVER lắng nghe. SERVER nên chọn trong khoảng này.
    - 49152-65535: Là PORT khởi tạo ngẫu nhiên dành cho CLIENT kết nối tới Server.


    Hàm Bind gồm có 3 thông số:
    - SOCKET s: Socket được thiết lập
    - sockaddr name: Cấu trúc ADDR bao gồm địa chỉ IP và PORT
    - int namelen: Kích thước của cấu trúc sockaddr

    Ví dụ:
    C++ Code:
    1. #define MYPORT 12345
    2.  
    3. // Chọn giao thức TCP
    4. SOCKET sockfd = socket(AF_INET, SOCK_STREAM, 0);            
    5.  
    6. // Thiết lập IP
    7. sockaddr_in my_addr;
    8.  
    9. my_addr.sin_family = AF_INET;
    10. my_addr.sin_port = htons(MYPORT);
    11. my_addr.sin_addr.s_addr = INADDR_ANY;                
    12.  
    13. // Đưa IP và PORT vào SOCKET
    14. bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr));


    b. LISTEN

    C++ Code:
    1. int listen(
    2.     SOCKET s,
    3.     int    backlog
    4. );


    Kể từ khi gọi hàm này thì SERVER sẽ bắt đầu lắng nghe kết nối của mình.

    Hàm LISTEN gồm có 2 thông số:
    - SOCKET s: Socket đã được thiết lập IP và PORT.
    - int backlog: Số kết nối cho phép chờ trong hàng đợi khi Server chưa chấp nhận kết nối. (vì đôi lúc có thể có tới 2 hay 3 client kết nối tới cùng 1 lúc). Giá trị tốt nhất là khoảng từ 5 – 10.

    c. CONNECT

    C++ Code:
    1. int connect(
    2.        SOCKET s,
    3.        struct sockaddr *serv_addr,
    4.        int addrlen );


    Hàm được gọi từ CLIENT nếu nó muốn kết nối tới SERVER
    - SOCKET s: Socket đã được khởi tạo.
    - sockaddr *serv_addr: IP và PORT của Server.
    - int addrlen: Sizeof của cấu trúc sockaddr.


    d. ACCEPT



    C++ Code:
    1. SOCKET accept(
    2.      SOCKET  s,
    3.      struct sockaddr FAR*  addr,
    4.      int FAR*  addrlen
    5. );


    Khi Client kết nối tới Server. Nó phải chờ Server chấp nhận kết nối (nếu ở giao thức TCP) bằng hàm accept trên.

    Hàm ACCEPT gồm có 2 thông số:
    - SOCKET s: Socket lắng nghe của SERVER.
    - sockaddr addr: Là cấu trúc sockaddr lưu địa chỉ IP và PORT của CLIENT kết nối tới SERVER.
    - int addrlen: Kích thước cấu trúc địa chỉ IP này.

    Hàm ACCPET trả về 1 SOCKET mới
    Socket mới được tạo này đại diện cho 1 Connection (kết nối) mới giữa Server và Client. Sau khi đã truyền dữ liệu thì ta phải đóng SOCKET này lại bằng hàm close như closesocket(connect);

    e. REVC/SEND

    * Nhận dữ liệu trên giao thức TCP


    C++ Code:
    1. int recv(
    2.     SOCKET s,
    3.     char FAR* buf,
    4.     int len,
    5.     int flags
    6. );


    * Gửi dữ liệu giao thức TCP

    C++ Code:
    1. int send(
    2.     SOCKET s,
    3.     const char FAR * buf,
    4.     int len,
    5.     int flags
    6. );


    - SOCKET s: Là SOCKET được tạo ra khi Server chấp nhận kết nối từ CLIENT
    - char FAR* buf: Là dữ liệu (dạng BYTE – char) nhận hay gửi.
    - int len: Kích thước của dữ liệu.
    - int flags: Một số cờ hiệu đi kèm (thông thường là 0).

    f. REVCFROM/SENDTO

    Được sử dụng trên giao thức UDP

    C++ Code:
    1. int recvfrom(
    2.      SOCKET s,
    3.      char FAR* buf,
    4.      int len,
    5.      int flags,
    6.      struct sockaddr *from,
    7.      int *fromlen);


    Các thông số:
    - SOCKET s: Là SOCKET được tạo ra ban đầu.
    - char FAR* buf: Là dữ liệu (dạng BYTE – char) nhận
    - int len: Kích thước của dữ liệu nhận.
    - int flags: Một số cờ hiệu đi kèm (thông thường là 0).
    - sockaddr *from: IP và PORT từ bên gửi.
    - int *fromlen: Sizeof cấu trúc addr.



    C++ Code:
    1. int sendto(
    2.           SOCKET s,
    3.  
    4.           const char FAR *buf,
    5.           int len,
    6.  
    7.           int flags,
    8.           const struct sockaddr* to,
    9.           int tolen
    10. );

    - SOCKET s: Là SOCKET được tạo ra ban đầu.
    - char FAR* buf: Là dữ liệu (dạng BYTE – char) gửi
    - int len: Kích thước của dữ liệu gửi.
    - int flags: Một số cờ hiệu đi kèm (thông thường là 0).
    - sockaddr *to: IP và PORT tới bên nhận.
    - int tolen: Sizeof cấu trúc addr.

    g. CLOSE/SHUTDOWN

    C++ Code:
    1. int shutdown(
    2.     SOCKET s,
    3.     int how
    4. );


    C++ Code:
    1.  int closesocket (SOCKET s);


    Hủy SOCKET sau một kết nối hoặc kết thúc chương trình.
    Tham số how của shutdown:
    - SD_RECEIVE: Đóng SOCKET, không cho phép NHẬN nhưng cho phép GỬI.
    - SD_SEND: Đóng SOCKET, không cho phép GỬI nhưng cho phép NHẬN.
    - SD_BOTH: Không cho GỬI và NHẬN (giống gọi hàm closesocket).

    Tại sao có chuyện đóng nhưng cho phép gửi và nhận, là do Winsock hỗ trợ rất nhiều giao thức như ATM, IPX,… do đó nó có những cái phức tạp, mình cũng không hiểu rõ bởi vì khả năng dịch vẫn hạn chế. Vì vậy có lẽ vẫn nên sử dụng SD_BOTH hoặc closesocket).

    Còn tiếp ...
    Email: admin[@]congdongcviet.com | CC to: info[@]congdongcviet.com
    Phone: 0972 89 7667 (Office: 04 6329 2380)
    Yahoo & Skype: dreaminess_world (Vui lòng chỉ rõ mục đích ngay khi liên hệ, cảm ơn!)

    Một người nào đó coi thường ý thức kỷ luật cũng có nghĩa là người đó đã coi thường tương lai số phận của chính bản thân người đó. Những người coi thường ý thức kỷ luật sẽ không bao giờ có được sự thành công trong sự nghiệp!

  4. #4
    Ngày gia nhập
    07 2006
    Nơi ở
    Hanoi, Vietnam
    Bài viết
    2,750

    V. DEMO MINI WEBSERVER

    Thật ra ở đây mình không có tham vọng làm 1 Web Server thực sự như IIS hay Apache đâu? Mình chỉ muốn dùng Winsock để làm 1 chương trình có thể biến máy tính bạn thành 1 máy chủ Web. Nghĩa là có ai đó truy cập vào thì nó sẽ trả lại 1 Webpage.

    Trước hết mình xin giới thiệu giao thức HTTP.

    1. Giao thức HTTP

    Bước 1: Giả sử người sử dụng gõ địa chỉ URL là: “http://webserver.com/Page?ID=1"

    Lúc này thì Client tại người sử dụng sẽ Connect tới Webserver và gởi một nội dung Request như sau:
    Click vào hình ảnh để lấy hình ảnh lớn

Tên:		ClientRequest.jpg
Lần xem:	1525
Size:		11.2 KB
ID:		4483


    GET /Page?ID=1 HTTP/1.1
    Host: webserver.com
    User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.9) Gecko/20071025 Firefox/2.0.0.9
    Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
    Accept-Language: en-us,en;q=0.5
    Accept-Encoding: gzip,deflate
    Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
    Keep-Alive: 300
    Connection: keep-alive


    Nội dung trên bao gồm:
    - GET (lấy trang web). Ngoài ra còn có POST
    - Host: Tên SERVER muốn kết nối!
    - User-Agent: Tên chương trình Web Browser
    - Ngoài ra còn một số thông tin về ngôn ngữ.

    Nếu khi đã nhận được trang WEB mà trong Web còn có hình ảnh hay âm thanh thì tiếp tục gởi Request tới Server
    Như:
    GET /samples/images/IMAGE.jpg HTTP/1.1


    Bước 2: Xử lý tại Server

    Lúc này tại Web Server sẽ làm gì?
    Nó sẽ gửi lại 1 Header như sau:
    HTTP/1.0 200 OK
    Server: <TÊN SERVER>
    Date:
    Content-Type: text/html
    Accept-Ranges: bytes
    Last-Modified:
    Content-Length: <kich thuoc trang WEB>


    Và sau đó nó sẽ căn cứ vào yêu cầu của Client: GET /Page?ID=1 HTTP/1.1 để gửi trả lại 1 trang HTML hoặc file hình ảnh, âm thanh… tương ứng.
    Ví dụ như:


    <html>
    <head><title>DEMO WEBSERVER - WINSOCKC++</title></head>
    <body>
    <h1>Hello eXecutve!</h1>
    <br>
    CopyRight 2007 by eXecutive;
    stairwaytoheaven187@yahoo.com
    </body>
    </html>


    2. Chương trình DEMO

    Như vậy đã đủ. Chúng ta sẽ viết chương trình.

    Hàm Thiết lập lắng nghe trên PORT 80
    C++ Code:
    1. #define MY_PORT 80
    2. ..
    3. ..
    4. int SetupIPWS(SOCKET& ListenSock,sockaddr_in& MyListenIP){    
    5.  
    6.     MyListenIP.sin_family = AF_INET;
    7.     MyListenIP.sin_port = htons( MY_PORT );       // PORT 80
    8.     MyListenIP.sin_addr.s_addr = INADDR_ANY; // IP của mình
    9.    
    10.     int nResult = bind(ListenSock, (sockaddr*) &MyListenIP,sizeof(MyListenIP));  // Gán IP PORT vào SOCKET
    11.     if (nResult == -1){
    12.         cout << "Loi khi thiet lap IP va PORT\n";
    13.         WSAGetLastError();
    14.         ShutdownWS(ListenSock);
    15.         return 1;
    16.     }
    17.    
    18.     return 0;
    19. }


    Hàm lắng nghe phục vụ kết nối.
    C++ Code:
    1. void LoopListen(SOCKET& ListenSock,sockaddr_in& MyListenIP){
    2.     SOCKET NewConnection;
    3.     sockaddr_in ConnectClient;
    4.     int nSizeAddr;
    5.    
    6.     int nResult = listen(ListenSock,10);           // Lắng nghe kết nối
    7.     if (nResult == -1){
    8.         cout << "Khong the lang nghe ket noi\n";
    9.         WSAGetLastError();
    10.         return;
    11.     }
    12.    
    13.     cout << "Dang lang nghe ket noi... tai IP: " << inet_ntoa(MyListenIP.sin_addr)
    14.         << " PORT: " << MyListenIP.sin_port << "\nCTRL + C de STOP SERVER\n\n";
    15.    
    16.  
    17.     char lpBuffRevc[1024]={0}; // Nội dung nhận từ  CLIENT (REQUEST)
    18.  
    19.         // Nội dung gửi trả lại  CLIENT
    20.     char headers[] =        "HTTP/1.0 200 OK\r\n"
    21.                     "Server: eXecutive Winsock Server\r\n"
    22.                     "Date: Sat, 3 November 2007\r\n"
    23.                     "Content-Type: text/html\r\n"
    24.                     "Accept-Ranges: bytes\r\n"
    25.                     "Content-Length: 187\r\n"
    26.                     "\r\n";           // Kết thức Header \n
    27.    
    28.     char html[] =              "<html>"
    29.                     "<head><title>DEMO WEBSERVER - WINSOCK C++ - CopyRight eXecutive</title></head> \r\n"
    30.                     "<body>\r\n"
    31.                     "<h1>Hello eXecutve!</h1>\r\n"
    32.                     "<br>\r\n"
    33.                     "CopyRight 2007 by eXecutive<br> \r\n"
    34.                     "stairwaytoheaven187@yahoo.com\r\n"
    35.                     "</body>\r\n"
    36.                     "</html>\r\n\n"; // Kết thúc trang web bằng \n\n
    37.  
    38.     while (1){        
    39.         nSizeAddr = sizeof(sockaddr);        // Quan trọng! Xác định kích thước sockaddr
    40.  
    41.         NewConnection = accept(ListenSock, (sockaddr*) &ConnectClient, &nSizeAddr);    // Chấp nhận 1 kết nối
    42.  
    43.         if (NewConnection == -1){
    44.             cout << "Loi ket noi tu Client\n";
    45.             continue;
    46.         }
    47.        
    48.         cout << "Nhan ket noi tu: "<< inet_ntoa(ConnectClient.sin_addr) << "\n";
    49.         cout << "\nNoi dung nhan:\n\n";
    50.        
    51.                 // Xóa nội dung REQUEST trước và nhận  REQUEST mới.
    52.         memset(lpBuffRevc,0,sizeof(lpBuffRevc));
    53.         recv(NewConnection,lpBuffRevc, sizeof(lpBuffRevc) ,0);
    54.         /*
    55.  
    56.                         XỬ  LÝ REQUEST => Ở ĐÂY MÌNH 1 KHÔNG 1 WEBSERVER CHUYỆN NGHIỆP NÊN BỎ QUA.
    57.         */
    58.                 // Gửi HEADER của SERVER và trang HTML
    59.         send(NewConnection,headers,sizeof(headers),0);
    60.         send(NewConnection,html,sizeof(html),0);
    61.  
    62.         cout << lpBuffRevc << "\n";
    63.                
    64.                 // Đóng kết nối!
    65.         closesocket(NewConnection);
    66.     }
    67. }


    Có lẽ 1 chương trình kỳ cục. While(1) không có điều kiện kết thúc. Nhưng đó là cách hoat động của SERVER, 1 chương trình có thể chạy quanh năm suốt tháng. Do đó nên không có vấn đề gì?

    Địa chỉ mà mình gõ là "http://localhost" tức là địa chỉ cục bộ = "http://127.0.0.1" = "http://192.168.1.254" (nếu máy tính của mình đặt IP này)
    Click vào hình ảnh để lấy hình ảnh lớn

Tên:		Webserver.jpg
Lần xem:	1814
Size:		83.9 KB
ID:		4484


    Tiếp tục ...
    Email: admin[@]congdongcviet.com | CC to: info[@]congdongcviet.com
    Phone: 0972 89 7667 (Office: 04 6329 2380)
    Yahoo & Skype: dreaminess_world (Vui lòng chỉ rõ mục đích ngay khi liên hệ, cảm ơn!)

    Một người nào đó coi thường ý thức kỷ luật cũng có nghĩa là người đó đã coi thường tương lai số phận của chính bản thân người đó. Những người coi thường ý thức kỷ luật sẽ không bao giờ có được sự thành công trong sự nghiệp!

  5. #5
    Ngày gia nhập
    07 2006
    Nơi ở
    Hanoi, Vietnam
    Bài viết
    2,750

    VI. DEMO MINI WEB BROWSER

    Lần này mình sẽ DEMO 1 Web Browser.

    Chương trình như sau:
    - Người dùng nhập vào địa chỉ Website
    - Kết nối và lấy nội dung HTML về lưu lại thành file RESPONSE.htm
    Click vào hình ảnh để lấy hình ảnh lớn

Tên:		WebClient.jpg
Lần xem:	1687
Size:		57.7 KB
ID:		4481

    1. Các thao tác tại WEB BROWSER

    a. Nhận địa chỉ Website và phân giải tên miền thành địa chỉ IP.

    C++ Code:
    1. char lpDomain[100];
    2. cout << "\n\nNhap ten mien can truy cap: http://";
    3. cin.getline(lpDomain,100);
    4.  
    5. hostent *ConnectPC=NULL;
    6. ConnectPC = gethostbyname(lpDomain);  // Lay PC theo Tên Domain
    7.    
    8. if (!ConnectPC){
    9.     cout << "DNS khong the phan giai duoc ten mien nay ...\n";
    10.     return 1;
    11. }
    12.    
    13. ConnectIP.sin_family = AF_INET;
    14. ConnectIP.sin_port = htons(  MY_PORT );
    15.  
    16. ConnectIP.sin_addr.s_addr = (*(DWORD*)ConnectPC->h_addr_list[0]); // Lay IP
    17.    
    18. cout << "\nMay chu:";
    19. cout << ConnectPC->h_name << "\n";
    20. cout << "IP: " << inet_ntoa(ConnectIP.sin_addr) << "\n";


    b. Gửi REQUEST tới máy chủ WEB

    Ví dụ người sử dụng nhập là "http://www.google.com" thì phải có Request tối thiểu để máy chủ xử lý là:


    GET / HTTP/1.1
    Host: google.com

    User-Agent: eXecutive DEMO Client

    -> "Host: google.com" rất quan trọng vì có nhiều WEB thuê HOSTING và lúc này có nhiều WEBSITE cùng trên 1 WEBSERVER. Do đó phải xác định miền truy cập

    C++ Code:
    1. // NOI DUNG REQUEST
    2. char *GetHTML = "GET / HTTP/1.1\r\n";
    3. char HostRequest[100];
    4. sprintf(HostRequest,"Host: %s\r\n",lpDomain);
    5. char *UserAgent = "User-Agent: eXecutive DEMO Client \r\n\n";
    6.        
    7. // GUI REQUEST
    8. send(Sock,GetHTML,strlen(GetHTML),0);
    9. send(Sock,HostRequest,strlen(HostRequest),0);
    10. send(Sock,UserAgent,strlen(UserAgent),0);


    c. Nhận RESPONSE từ máy chủ WEB

    Nhận HEADER từ SERVER:
    Nguyên tắc là chúng ta sẽ nhận từng BYTE . Khi thấy ký tự "\n\n" có nghĩa là kết thúc Header.

    C++ Code:
    1. char HeaderResponse;
    2. while(!done){
    3.     nByteRevc = recv(Sock,&HeaderResponse,1,0);
    4.     if(nByteRevc<0)    // Loi
    5.         done=TRUE;
    6.  
    7.     switch(HeaderResponse)    {            
    8.         case '\r':
    9.             break;
    10.         case '\n': // Neu gap "\n\n" hay "\n\r\n" thi ket thuc Header
    11.             if (EndHeader == true){                        
    12.                 done = TRUE;
    13.             }
    14.             EndHeader=true;
    15.             break;
    16.         default:
    17.             EndHeader=false;
    18.             break;
    19.     }      
    20.     cout << HeaderResponse;
    21. }


    Nhận BODY từ SERVER:
    Chúng ta sẽ nhận từng lúc 512 Bytes từ SERVER cho đến khi không được SERVER hồi âm nữa (nByteRevc <= 0)
    Đoạn CODE như sau:
    C++ Code:
    1. char HTMLResponse[513]={0};
    2.  
    3. do{
    4.     nByteRevc = recv(Sock,HTMLResponse,sizeof(HTMLResponse)-1,0);        // Đọc 512 Bytes    
    5.  
    6.     // Loai bo 1 ky tu bi thua tu HEADER
    7.     if (HTMLResponse[0] == 0)
    8.         HTMLResponse[0] = ' ';
    9.  
    10.     if(nByteRevc < 0)    // Ket thuc HTML
    11.         break;            
    12.            
    13.     HTMLResponse[nByteRevc] = 0; // Ngắt chuỗi nếu RESPONSE nhận < 512 BYTES hoặc cuối cùng để in chuỗi.
    14.     cout << HTMLResponse;
    15.        
    16. }while (nByteRevc > 0);



    Rõ ràng giữa sendrecv không phải tuân theo quy tắc là gửi ba nhiêu bytes thì phải nhận bấy nhiêu bytes. Và chúng ta có thể gửi hay nhận nhiều lần, vấn đề này không quan trọng trong WINSOCK!


    Còn tiếp ...
    Email: admin[@]congdongcviet.com | CC to: info[@]congdongcviet.com
    Phone: 0972 89 7667 (Office: 04 6329 2380)
    Yahoo & Skype: dreaminess_world (Vui lòng chỉ rõ mục đích ngay khi liên hệ, cảm ơn!)

    Một người nào đó coi thường ý thức kỷ luật cũng có nghĩa là người đó đã coi thường tương lai số phận của chính bản thân người đó. Những người coi thường ý thức kỷ luật sẽ không bao giờ có được sự thành công trong sự nghiệp!

  6. #6
    Ngày gia nhập
    07 2006
    Nơi ở
    Hanoi, Vietnam
    Bài viết
    2,750

    Mặc định Lập Trình Mạng Với Thư Viện Winsock trên VC++

    VII. DEMO CLIENT - SERVER VỚI GIAO THỨC UDP

    1. UDP là gì?

    - Thật ra mình đã giới thiệu về UDP ở phần tạo SOCKET rồi nhưng mình nói lại 1 xíu cho nhanh. UDP là một trong 2 giao thức chính của mô hình TCP/IP để truyền tải dữ liệu. CLIENT - SERVER sử dụng UDP sẽ không bao giờ quan tâm đến về đề dữ liệu có chính xác hay không? Bên gửi cứ gửi và bên nhận cứ nhận, nhận không được thì mặc kệ...

    - Người ta sử dụng UDP trong những ứng dụng cần truyền dữ liệu nhanh như CHAT, GAME ONLINE, ....

    - Và lần này mình sẽ DEMO 1 bộ ứng dụng Client - Server liên lạc với nhau mà Client không cần biết IP Server sử dụng cơ chế Multicast hay Broadcast

    2. BROADCAST hay MULTICAST là gì?

    a. Broadcast

    Chúng ta biết IP chia thành 3 lớp là A,B,C.
    Trong cấu trúc địa chỉ IP bao gồm NET_IDHOST_ID

    -> Các host chỉ thấy nhau (liên lạc được) khi chúng có cùng NET_ID.
    -> Muốn host liên lạc khi khác NET_ID thì cần phải có cơ chế định tuyến (sử dụng thiết bị router, Server có cấu hình Routing ...)

    => Như vậy trong 1 NET_ID phải có 1 địa chỉ nào đó để đại diện cho tất cả các HOST đường mạng đó và đó chính là địa chỉ Broadcast

    Ví dụ:
    Lớp A:
    Có các IP sau : 10.0.0.100, 10.12.3.1 hay 10.252.252.252
    -> Tất cả đều có chung 1 đường mạng 10.0.0.0
    -> Và IP đại điện là IP Broadcast: 10.255.255.255.
    Nghĩa là khi có 1 gói tin UDP gởi tới địa chỉ này thì tất cả các host trên đều nhận được

    b. Multicast

    - Địa chỉ Broadcast đại diện cho tất cả các Host nhưng trên cùng 1 đường mạng.
    - Địa chỉ Multicast đại diện cho tất cả các HOST trên tất cả các đường mạng khác nhau

    Hay chính xác hơn thì địa chỉ "255.255.255.255" chính là địa chỉ Multicast.
    Khi gởi 1 gói tin UDP Packet tới địa chỉ này thì tất cả các host có thể liên lạc được với host gửi (khác đường mạng phải có cơ chế định tuyến) đèu nhận được gói tin này.

    Tuy nhiên lưu ý 1 điều là Multicast hay Broadcast chỉ có giá trị trong IPLAN

    2. Chương trình DEMO

    Ý tưởng: Bạn đã chơi Game Haftlife chưa???

    - Rõ ràng máy Server trong mạng LAN có thể là bất cứ máy tính nào mà Gamer chọn Create Game?
    => Vậy làm sao Client có thể biết được IP máy chủ để Connect.
    Và nó phải sử dụng cơ chế tìm IP SERVER sử dụng UDP Broadcast.

    Và chương trình DEMO của mình sẽ như sau:
    Click vào hình ảnh để lấy hình ảnh lớn

Tên:		BroadcastServer_Client.jpg
Lần xem:	1646
Size:		42.8 KB
ID:		4482

    a. SERVER

    Cũng như TCP. Nhiệm vụ chính của nó là lắng nghe, tuy nhiên chỉ khác chức năng là không cần phải chấp nhận kết nối từ Client.


    C++ Code:
    1. // Thiết lập IP PORT
    2.     pAddr->sin_family = AF_INET;
    3.     pAddr->sin_port = htons(MY_PORT);
    4.     pAddr->sin_addr.S_un.S_addr = ADDR_ANY;
    5.    
    6.    
    7.     // Tìm địa chỉ IP của SERVER <sau này trả lại cho CLIENT>
    8.     char lpName[100];
    9.     char lpMyIP[100]={0};
    10.  
    11.     gethostname(lpName,sizeof(lpName));
    12.     hostent* pMyServer = gethostbyname(lpName);
    13.     u_long myIP = *(u_long*)pMyServer->h_addr_list[0];
    14.     strcpy(lpMyIP,inet_ntoa(*(in_addr*)&myIP));
    15.     // ----------------------------------------------------------------------------
    16.  
    17.     // Đưa thông tin IP và PORT cho SOCKET
    18.     nResult = bind(*sock,(sockaddr*)pAddr,sizeof(sockaddr));
    19.  
    20.     if (nResult == -1){
    21.         cout << "Loi thiet lap IP va PORT\n";
    22.         HuyWinsock(sock);
    23.         return 1;
    24.     }
    25.     cout << "Dang lang nghe ket noi tren IP: " << lpMyIP << " port: " << MY_PORT << "\n\n";
    26.  
    27.     int nAddrLen;
    28.     int nRevc;
    29.     int nSend;
    30.     sockaddr_in IPClient;
    31.    
    32.     char buff[512]={0};    
    33.  
    34.     while (1){
    35.  
    36.         // Nhận kết nối từ CLIENT
    37.         nAddrLen = sizeof(sockaddr_in);                
    38.         nRevc = recvfrom(*sock,buff,sizeof(buff),0, (sockaddr*)&IPClient,&nAddrLen);
    39.        
    40.         buff[nRevc-1] = 0;
    41.        
    42.         cout << "Nhan ket toi tu CLIENT IP: " << inet_ntoa(IPClient.sin_addr) << "\n";
    43.         cout << "Noi dung: \"" << buff << "\"\n";
    44.  
    45.         // Kiểm tra yêu cầu của CLIENT        
    46.         if (strcmpi("IP may chu dau???",buff)==0){
    47.             cout << "Hieu yeu cau tu Client!\n";            
    48.             // Gửi trả lại cho CLIENT địa chỉ IP của mình
    49.             nSend = sendto(*sock,lpMyIP,strlen(lpMyIP),0,(sockaddr*)&IPClient,sizeof(IPClient));
    50.             cout << "Gui lai client: " << nSend << " Bytes\n";
    51.         }
    52.         else{
    53.             cout << "Khong hieu yeu cau tu Client!\n";
    54.         }
    55.  
    56.         cout << "\n";
    57.     }


    b. CLIENT

    -> Nhiệm vụ chính của CLIENT là gửi 1 gói tin BROADCAST với PORT quy định sẵn

    Trong chương trình của CLIENT có 1 hàm rất quan trọng để cho phép SOCKET gởi tới địa chỉ BROADCAST

    setsockopt(*sock,SOL_SOCKET,SO_BROADCAST,(char*)&b SockBroadcast,sizeof(BOOL));

    - Op tùy chọn ở đây là : SO_BROADCAST (gởi gói tin Broadcast);
    - bSockBroadcast: TRUE (cho phép), FALSE (không cho phép).

    Hàm này có rất nhiều Option. Các bạn có thể tham khảo thêm MSDN.

    C++ Code:
    1. int FindServer(SOCKET* sock,sockaddr_in* pServerAddr){        
    2.     BOOL bSockBroadcast=true;
    3.    
    4.     setsockopt(*sock,SOL_SOCKET,SO_BROADCAST,(char*)&bSockBroadcast,sizeof(BOOL));
    5.  
    6.     pServerAddr->sin_family = AF_INET;
    7.     pServerAddr->sin_port = htons(MY_PORT);
    8.     pServerAddr->sin_addr.S_un.S_addr = inet_addr("255.255.255.255"); // Địa chỉ đích là MULTICAST
    9.    
    10.     int nSend;
    11.     int nRevc;
    12.     int nAddrLen;    
    13.     char buff[512]="IP may chu dau???"; // Thông tin gửi tới máy chủ
    14.        
    15.     // Gửi tới máy chủ    
    16.     nSend = sendto(*sock,buff,sizeof(buff),0,(sockaddr*)pServerAddr,sizeof(sockaddr_in));
    17.     cout << "Da gui " << nSend << " bytes\n";
    18.    
    19.     // Nhận hồi âm của máy chủ
    20.     sockaddr_in ServerIP;
    21.     nAddrLen = sizeof(sockaddr_in);
    22.     nRevc = recvfrom(*sock,buff,sizeof(buff),0, (sockaddr*)&ServerIP, &nAddrLen);
    23.     buff[nRevc] = 0;
    24.     cout << "Da nhan " << nRevc << " bytes\n";
    25.     cout << "Noi dung nhan <IP SERVER>: " << buff << "\n";
    26.  
    27.     return 0;
    28. }

    Còn tiếp ...
    Email: admin[@]congdongcviet.com | CC to: info[@]congdongcviet.com
    Phone: 0972 89 7667 (Office: 04 6329 2380)
    Yahoo & Skype: dreaminess_world (Vui lòng chỉ rõ mục đích ngay khi liên hệ, cảm ơn!)

    Một người nào đó coi thường ý thức kỷ luật cũng có nghĩa là người đó đã coi thường tương lai số phận của chính bản thân người đó. Những người coi thường ý thức kỷ luật sẽ không bao giờ có được sự thành công trong sự nghiệp!

  7. #7
    Ngày gia nhập
    02 2008
    Bài viết
    2

    Bài viết hay quá. Xin đưa thêm một ví dụ nhỏ về ứng dụng Server - Client sử dụng UDP.
    Server chờ để nhận yêu cầu kết nối từ client. Client cần chỉ địa chỉ IP mà nó cần kết nối, sau đó nó sẽ gởi 10 dòng dữ liệu theo kiểu UDP đến địa chỉ đó mà không cần quan tâm đến việc server có nhận được hay không.
    Server :
    Visual C++ Code:
    1. #include<iostream>
    2. #include<Winsock2.h>
    3. #include<stdio.h>
    4. #pragma comment(lib,"Ws2_32")
    5. using namespace std;
    6. const unsigned NPACK=10;
    7. const unsigned BUF_LENGTH=512;
    8. int main(){
    9.     WORD wVersionRequested=MAKEWORD(1,1);
    10.     WSADATA wsaData;
    11.     SOCKET serverSock;
    12.     sockaddr_in sockAddr,clientAddr;
    13.     int clientAddrLeng=sizeof(clientAddr);
    14.     char buf[BUF_LENGTH];
    15.     int port=2512;
    16.     if(WSAStartup(wVersionRequested,&wsaData)==0){
    17.         serverSock=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
    18.         memset(&sockAddr,0,sizeof(sockAddr));
    19.         sockAddr.sin_family=AF_INET;
    20.         sockAddr.sin_port=htons(port);
    21.         sockAddr.sin_addr.s_addr=htonl(INADDR_ANY);
    22.         bind(serverSock,(sockaddr*)&sockAddr,sizeof(sockAddr));
    23.         printf("Wait for UDP clients ...\n\n");
    24.         //for(int i=1;i<=NPACK;i++){
    25.         while(true){
    26.             if(recvfrom(serverSock,buf,BUF_LENGTH,0,(sockaddr*)&clientAddr,&clientAddrLeng)!=0){
    27.                 printf("Received packet from %s:%d. Data : %s\n\n",inet_ntoa(clientAddr.sin_addr),
    28.                     htons(clientAddr.sin_port),buf);
    29.             }
    30.             else
    31.                 printf("Missed \n\n");
    32.         }
    33.         closesocket(serverSock);
    34.         WSACleanup();
    35.         setsockopt
    36.     }
    37.     else
    38.     {
    39.         cout<<"Can not start winsock"<<endl;
    40.     }
    41.     return 0;
    42. }

    Client

    Visual C++ Code:
    1. #include<iostream>
    2. #include<Winsock2.h>
    3. #include<stdio.h>
    4. #pragma comment(lib,"Ws2_32")
    5. using namespace std;
    6. const unsigned NPACK=10;
    7. const unsigned BUF_LENGTH=512;
    8. int main(){
    9.     WORD wVersionRequested=MAKEWORD(1,1);
    10.     WSADATA wsaData;
    11.     SOCKET clientSock;
    12.     sockaddr_in sockAddr,serverAddr;
    13.     struct hostent* serverHostent;
    14.     char buf[BUF_LENGTH];
    15.     char hostName[256];
    16.     int port=2512;
    17.     if(WSAStartup(wVersionRequested,&wsaData)==0){
    18.         clientSock=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
    19.         cout<<"Enter host name : ";
    20.         cin.getline(hostName,256);
    21.         serverHostent=gethostbyname(hostName);
    22.         if(serverHostent==NULL){
    23.             printf("Can not solve host \n");
    24.         }
    25.         memset(&sockAddr,0,sizeof(sockAddr));
    26.         memcpy(&sockAddr.sin_addr,serverHostent->h_addr,serverHostent->h_length);
    27.         sockAddr.sin_family=serverHostent->h_addrtype;
    28.         sockAddr.sin_port=htons(port);     
    29.  
    30.         for(int i=1;i<=NPACK;i++){
    31.             printf("Send packet %d \n",i);
    32.             sprintf(buf,"This is packets %d",i);
    33.             if(sendto(clientSock,buf,sizeof(buf),0,(sockaddr*)&sockAddr,sizeof(sockAddr))==SOCKET_ERROR){
    34.                 printf("Error send\n");
    35.             }
    36.         }
    37.         closesocket(clientSock);
    38.         WSACleanup();
    39.     }
    40.     else
    41.     {
    42.         cout<<"Can not start winsock"<<endl;
    43.     }
    44.     return 0;
    45. }
    Đã được chỉnh sửa lần cuối bởi hi0hiq : 16-05-2008 lúc 02:51 AM.

  8. #8
    Ngày gia nhập
    09 2008
    Bài viết
    188

    Dr và ZCoder87 cho mình hỏi một tí.
    Giả sử mình có 2 máy client A,B cùng kết nối với một Server S.
    Mình có 2 vấn đề muốn hỏi lại các bạn:
    1. Số Port của Client và Server có phái trùng với nhau không ???
    (mình cũgn đã thử viết, nó chỉ chạy khi Port của CLient và Ser giống nhau ??)
    Vì trong bài viết mình có thấy giải thích là số Port theo quy định củ Client và Server khác nhau ???

    2. 2 máy Client A,B sử dụng cùng Port 4000 và kết nối tới Server. vậy khi Server gởi Dữ liệu lại thì làm sao phân biệt được gởi cho máy A hay B ???

    Mong các bạn giải thích giùm để hiểu cho rõ hơn mà.

    Cảm ơn vì bài viết cũng rất hay !!!
    Mỗi lần học hỏi được gì sao mà vui thế, ... haaa !
    Phải đập cáng nhiều mới hiểu được ...

  9. #9
    Ngày gia nhập
    01 2008
    Nơi ở
    Gameloft Studio
    Bài viết
    294

    Trước tiên thì xin lỗi bạn vì đến hôm nay mình mới thấy câu hỏi?

    Trích dẫn Nguyên bản được gửi bởi nam_dkn
    1. Số Port của Client và Server có phái trùng với nhau không ???
    (mình cũgn đã thử viết, nó chỉ chạy khi Port của CLient và Ser giống nhau ??)
    Vì trong bài viết mình có thấy giải thích là số Port theo quy định củ Client và Server khác nhau ???
    Thứ nhất là port Client và Server sẽ không trùng nhau.
    - Port của server thì luôn luôn cố định.
    - Trong khi port của client thì sẽ tự động phát sinh một cách ngẫu nhiên nhưng khi kết nối tới server thì bạn phải chỉ đúng port đang lắng nghe từ server!


    Trích dẫn Nguyên bản được gửi bởi nam_dkn
    2. 2 máy Client A,B sử dụng cùng Port 4000 và kết nối tới Server. vậy khi Server gởi Dữ liệu lại thì làm sao phân biệt được gởi cho máy A hay B ???
    Ở trường hợp này người ta sử dụng 1 khái niệm là session, tức là bạn phải làm một chuỗi số nào đó mà khi kết nối tới server thì server sẽ cấp cho client. Session này sẽ có 1 thời gian sống nhất định (nếu khoảng 10p ko cập nhật thì tự động hủy hay ngắt kết nối) và cũng là duy nhất cho 1 kết nối.

    Nói cụ thể hơn là bạn sẽ dùng 1 Hashtable[session] để lưu trữ các socket được tạo ra khi server accept.

    Vì vậy trong mỗi lần gửi thông tin tới thì client sẽ gửi kèm theo session id này. Do đó server hoàn toàn phân biệt được 2 máy A. B và tìm ra được socket cần thiết để reply cho client.

    Mình hy vọng sẽ trả lời được thắc mắc của bạn. Các thắc mắc về socket các bạn có thể hỏi ở đây cũng được. Nếu cái gì có thể trả lời được thì mình sẽ trả lời.

    Cám ơn bạn vì đã quan tâm!
    Đã được chỉnh sửa lần cuối bởi ZCoder87 : 08-06-2009 lúc 11:55 PM.

  10. #10
    Ngày gia nhập
    09 2008
    Bài viết
    188

    Cảm ơn Zcoder87 nhiều, bạn giải thích mình cũng hiểu ra nhiều điều. có đièu mình chưa dùng HashTable nên chưa thể làm thử dc ngay.
    cảm ơn bạn
    Mỗi lần học hỏi được gì sao mà vui thế, ... haaa !
    Phải đập cáng nhiều mới hiểu được ...

Các đề tài tương tự

  1. Lập trình Winsock trong C# | Winsock programing with C#
    Gửi bởi ZCoder87 trong diễn đàn Tutorials và Thủ thuật lập trình C#, ASP.NET
    Trả lời: 27
    Bài viết cuối: 07-04-2013, 11:55 AM
  2. Cách tạo voice chat bằng winsock viết trên MFC Dialog?
    Gửi bởi phamduyhung trong diễn đàn Thắc mắc lập trình Visual C++
    Trả lời: 0
    Bài viết cuối: 26-06-2012, 04:39 PM
  3. Sách MFC Winsock tiếng việt tìm ở đâu?
    Gửi bởi aptx1987 trong diễn đàn Công cụ, ebooks VC++
    Trả lời: 1
    Bài viết cuối: 23-08-2011, 06:39 PM
  4. Borland C++ Lỗi Thư viện winsock,winsock2
    Gửi bởi tauit_dnmd trong diễn đàn Thắc mắc lập trình C/C++/C++0x
    Trả lời: 4
    Bài viết cuối: 05-04-2010, 08:49 PM
  5. Lập trình winsock trên VC++. Lỗi do đâu?
    Gửi bởi vietwow trong diễn đàn Thắc mắc lập trình Visual C++
    Trả lời: 2
    Bài viết cuối: 02-06-2009, 04:26 PM

Quyền hạn của bạn

  • Bạn không thể gửi đề tài mới
  • Bạn không thể gửi bài trả lời
  • Bạn không thể gửi các đính kèm
  • Bạn không thể chỉnh sửa bài viết của bạn