ĐỊA CHỈ MẠNG Thread 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.. Ở Thread này m
Trang 1[VC++] Lập Trình Mạng Với Thư Viện Winsock
Trang 2[VC++] Lập Trình Mạng Với Thư Viện Winsock
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 trong file cpp có hàm main mà khai báo trong file stdafx.h Đây là cách khai báo thư viện của Visual C++
#include <tchar.h>
#include <winsock2.h>
#pragma comment (lib,"ws2_32.lib")
Và bây giờ sẽ là những hàm để khởi tạo Winsock:
int WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData);
typedef struct WSAData {
WORD wVersion; // Phiên bản hiện tại
WORD wHighVersion; // Phiên bản có thể hỗ trợ
char szDescription[WSADESCRIPTION_LEN + 1]; // Ghi chú
char szSystemStatus[WSASYS_STATUS_LEN + 1]; // Trạng thái hệ thống
unsigned short iMaxSockets; // Không sử dụng từ Version 2 trở đi
unsigned short iMaxUdpDg; // Không sử dụng từ Version 2 trở đi char FAR * lpVendorInfo; // Không sử dụng từ Version 2 trở đi
} WSADATA, FAR * LPWSADATA;
Và cuối cùng là hàm hủy Winsock khi kết thúc chương trình
nt WSACleanup (void);
Chương trình đầu tiên:
#include "stdafx.h"
Trang 3Ở đâ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ừ client và Client 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
Trang 4Và ở 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 diendantinhoc.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ủ
“diendantinhoc.com” có “thông mạng” với nhau Và gói tin mà bạn PING chính là SOCK_RAW (ICMP Packet)
Trang 5* 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:
Trang 63 Một số hàm lấy thông tin về mạng
Trang 7a Lấy thông tin Socket
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à
b Lấy tên máy tính của mình
int gethostname(char* name, int namelen);
Đị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_addr và inet_ntoa để chuyển đổi qua lại giữa u_long và char* u_long YahooAddr = inet_addr("216.109.112.135");
cout << "IP: " << inet_ntoa(*(in_addr*) &YahooAddr) << "\n";
d Lấy IP theo tên máy
struct hostent* FAR gethostbyname(const char* name);
Trang 8Trong đó
typedef struct hostent {
char FAR* h_name; // Tên máy tính
char FAR FAR** h_aliases; // Bí danh máy tính
short h_addrtype; // Kiểu IP (AF_INET)
short h_length; // Kích thước IP
char FAR FAR** h_addr_list; // Danh sách các địa chỉ IP // 1 host có thể có 1 hoặc nhiều IP
e Lấy tên máy theo địa chỉ IP
Tương tự nhưng ngược lại
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) hostent *Yahoo;
u_long YahooAddr = inet_addr("216.109.112.135");
Yahoo = gethostbyaddr((char*)&YahooAddr,4,AF_INET);
Trang 9Chương trình mẫu khởi tạo Socket:
III ĐỊA CHỈ MẠNG
Thread 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
Ở Thread 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 thread 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
Trang 10Hiệ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_id và Host_id
2 Các lớp địa chỉ IP
Trang 11Đị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)
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)
- 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)
Trang 12+ 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…
=> 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
Trang 133 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
-> 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
=> 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)
Trang 14b 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)
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”
Đị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)
Trang 15- DNS Servers: Là 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
Trang 16- 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ề
Trang 17- 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…
Tham khảo bài viết "Nguyên lý hoạt động của giao thức TCP/IP và Internet"
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 struct sockaddr_in{
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)
Trang 18lướ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)
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?
Trang 19a Hoạt động của Client – Server trong giao thức TCP (SOCK_STREAM)
Trang 20b Hoạt động của Client – Server trong giao thức UDP (SOCK_DGRAM)
Trang 212 Một số hàm liên quan tới gởi và nhận dữ liệu
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,
- 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
// Đưa IP và PORT vào SOCKET
bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr));
Trang 22- 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
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
SOCKET accept(
SOCKET s,
struct sockaddr FAR* addr,
int FAR* addrlen
);
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
Trang 23- 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);
- 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)