Lập trình mạng - Chương 1: Tổng quan về lập trình mạng

Tài liệu Lập trình mạng - Chương 1: Tổng quan về lập trình mạng: Chương 1: TỔNG QUAN VỀ LẬP TRÌNH MẠNG I. Họ giao thức TCP/IP 1. Mục tiêu _ Cung cấp dịch vụ truyền thông liên mạng, che dấu chi tiết kiến trúc liên mạng, che dấu chi tiết phần cứng. _ Kiến trúc phân lớp. 2. Địa chỉ IP a) Khái niệm _ Địa chỉ luận lý (địa chỉ cấp phát động hoặc tĩnh): xác định duy nhất một máy trên mạng, khác với địa chỉ card mạng. _ Địa chỉ vật lý (địa chỉ card mạng): do nhà sản xuất cấp phát. _ Phiên bản: • Ipv4: 32 bit, dạng biểu diễn số chấm thập phân (ví dụ: 192.168.10.1). • Ipv6: 128 bit. b) Phân lớp địa chỉ _ Xác định bởi những bit nhận dạng (Class ID): – Sự tương quan giữa lớp và kích thước mạng: _ Các địa chỉ IP đặc biệt: _ Các vùng địa chỉ IP dành riêng (Private Network): • 10.0.0.0 – 10.255.255.255.255 • 172.16.0.0 – 172.31.255.255 • 192.168.0.0 – 192.168.255.255 3. Một số giao thức a) Lớp Internet (Internet Layer) _ Giao thức Internet (IP – Internet Protocol). _ Giao thức Kiềm soát Thông điệp Internet (Interne...

pdf40 trang | Chia sẻ: Khủng Long | Lượt xem: 998 | Lượt tải: 0download
Bạn đang xem trước 20 trang mẫu tài liệu Lập trình mạng - Chương 1: Tổng quan về lập trình mạng, để tải tài liệu gốc về máy bạn click vào nút DOWNLOAD ở trên
Chương 1: TỔNG QUAN VỀ LẬP TRÌNH MẠNG I. Họ giao thức TCP/IP 1. Mục tiêu _ Cung cấp dịch vụ truyền thông liên mạng, che dấu chi tiết kiến trúc liên mạng, che dấu chi tiết phần cứng. _ Kiến trúc phân lớp. 2. Địa chỉ IP a) Khái niệm _ Địa chỉ luận lý (địa chỉ cấp phát động hoặc tĩnh): xác định duy nhất một máy trên mạng, khác với địa chỉ card mạng. _ Địa chỉ vật lý (địa chỉ card mạng): do nhà sản xuất cấp phát. _ Phiên bản: • Ipv4: 32 bit, dạng biểu diễn số chấm thập phân (ví dụ: 192.168.10.1). • Ipv6: 128 bit. b) Phân lớp địa chỉ _ Xác định bởi những bit nhận dạng (Class ID): – Sự tương quan giữa lớp và kích thước mạng: _ Các địa chỉ IP đặc biệt: _ Các vùng địa chỉ IP dành riêng (Private Network): • 10.0.0.0 – 10.255.255.255.255 • 172.16.0.0 – 172.31.255.255 • 192.168.0.0 – 192.168.255.255 3. Một số giao thức a) Lớp Internet (Internet Layer) _ Giao thức Internet (IP – Internet Protocol). _ Giao thức Kiềm soát Thông điệp Internet (Internet Control Message Protocol – ICMP). _ Giao thức Nhóm Thông điệp Internet (Internet Group Message Protocol – IGMP). _ Giao thức Phân giải Địa chỉ (Address Resolution Protocol – ARP), giao thức Chuyển đổi Địa chỉ (Address Reverse Protocol – ARP): chuyển đổi địa chỉ vật lý, luận lý. b) Lớp Giao vận (Transport Layer) _ Cung cấp dịch vụ truyền thông giữa các tiến trình. _ Thông tin xác định tiến trình: • Địa chỉ IP. • Cổng (16 bit):  0 – 1023: well-know port, IANA (Internet Assigned Numbers Authority).  1024 – 49151: registered port.  49152 – 65535: dynamic port. _ Giao thức Kiểm soát Truyền thông (Transmission Control Protocol – TCP): • Có thiết lập cầu nối: full duplex. • Tin cậy: đúng trình tự, không thất thoát, không trùng lấp. • Byte stream: đệm dữ liệu (nơi lưu trữ dữ liệu trước khi gửi; ví dụ: nếu đệm là 1 KB, gói dữ liệu là 2 KB thì chỉ có 1KB được chuyển phải gửi lại thêm 1 KB nữa). _ Giao thức Dữ liệu Người dùng (User Datagram Protocol – UDP): • Không thiết lập cầu nối. • Không tin cậy. • Dạng truyền thông (broadcast). • Dữ liệu Người dùng (datagram). c) Lớp Ứng dụng (Application Layer) _ Cung cấp việc vận chuyển dữ liệu trong suốt giữa các hệ thống đầu cuối (end systems). II. Ứng dụng mạng _ Truyền tải tập tin (File Transfer). _ Trình duyệt web / server (Web Browser / Server). _ Thư điện tử (Electric Mail). _ Truyền tải giọng nói (Voice over IP). _ Xem phim, nghe nhạc trực tuyến (Audio / Video Online). _ Hội họp từ xa (Remote Conferencing). _ Trò chơi trực tuyến (Game Online). _ III. Mô hình ứng dụng _ Mô hình 2 tầng (2-tiers): Client / Server. _ Mô hình đa tầng (N-tiers). _ Mô hình hàng ngang (Peer to Peer). IV. Giao diện lập trình socket 1. Window Socket API _ Khái quát: • Phát triển theo đặc tả giao diện Phân phối Phần mềm Berkeley (Berkeley Soft ware Distribution – BSD Socket). • Bổ sung các tính năng hoạt động của môi trường Windows. • Hiện thực ở dạng thư viện liên kết động: wsock32.dll (winsock.h), ws2_32.del (winsock.h). • Thư viện lập trình giao tiếp với giao thức mạng. _ Dữ liệu: socket, địa chỉ IP, thông tin máy. _ Các hàm: liên kết thư viện truy xuất thông tin, chuyển đổi dữ liệu, làm việc với socket. 2. Tiếp cận hướng đối tượng a) MFC _ Thư viện hỗ trợ của Microsoft: • Che giấu chi tiết sử dụng các hàm Winsock API. • Hỗ trợ xây dựng ứng dụng Internet. _ Windows Sockets: CasyncSocket, Csocket, CsocketFile. _ Mở rộng: Win32 Internet Extensions Winlnet: • ElnternetSession. • ElnternetConnection: CftpConnection, CgopherConection, ChttConnection. • CgopherLocation. • ElnternetFile. • EfileFmd. b) .NET _ System.Net: • DNS. • IPAddress. • EndPoint. • IPHostEntry. • Socket Address. • WebRequest, WebResponse. • WebClient. • _ System.Net.Socket: • Socket. • SocketException. • TcpClient, TcpListener. • UdpClient. • _ System.Net.Mail: • Enet. • c) Java _ Java.Net: • InetAddress. • Socket, ServerSocket. • SocketException. • URI+, URL. _ Javamail. Chương 2: CĂN BẢN LẬP TRÌNH WINSOCK I. Socket 1. Khái niệm _ Cơ chế trừu tượng dùng cho quá trình truyền thông giữa các tiến trình. _ Tương ứng với cấu trúc chứa các thông tin cần cho quá trình truyền thông giữa các tiến trình (IP, port). 2. Quản lý socket _ Cấu trúc dữ liệu do hệ điều hành quản lý. _ Ứng dụng sử dụng thông qua handle. 3. Phân loại _ Stream Socket: TCP Socket. _ Datagram Socket: UDP Socket. _ Raw Socket. II. Phân nhóm hàm thư viện _ Liên kết thư viện, kết thúc. _ Truy xuất thông tin. _ Chuyển đổi dạng dữ liệu. _ Các hàm thao tác trên socket: • Tạo socket, đóng socket. • Thiết lập cầu nối. • Gửi, nhận dữ liệu. III. Liên kết thư viện _ Liên kết thư viện: int WSAStartup (WORD wVersionRequested, LPWSADATA lpwaData); _ Kết thúc: int WSACleanup(); _ Truy xuất mã lỗi sai: int WSAGetLastError(); _ Lưu ý: • File StdAfx.h: #include • Lớp ứng dụng: bool CDDemoApp :: InitInstance() { ... if (!AfxSocketInit()) { AfxMessageBox(IDP_SOCKETS_INIT_FAILED); return false; } ... return false; } IV. Truy xuất thông tin 1. Thông tin máy _ Các phương thức: • int gethost name (char FAR* name, int len); • PHOSTENT gethostbyname (const char FAR* hostname); • PHOSTENT gethostbyaddr (const char FAR* addr, int len, int af); _ Ví dụ: char sethostName [MAX_LEN]; if (gethostname (sethostName, MAX_LEN) != SOCKET_ERROR) { // } else { // } _ Cấu trúc thông tin máy: • struct hostent { char FAR* h_name; char FAR* FAR* h_aliases; short h_addtype; short h_lenght; char FAR* FAR* h_addr_list; #define h_addr h_addr_list[0]; }; • PHOSTENT pHostEnt = gethostbyname (set); if (pHostEnt != NULL) { // } 2. Thông tin dịch vụ _ Cú pháp: PSERVENT getservbyname (constchar char FAR* name, const FAR* proto); V. Chuyển đổi thông tin dữ liệu 1. Chuyển đổi trật tự byte _ Trật tự byte: • Lưu trữ số nguyên trên máy tính: Host Byte Order Little-Endian Big-Endian short 12AB AB12 12AB long 12AB34CD CS34AB12 12AB34CD • Quy ước lưu trữ số nguyên trên mạng (Network Byte Order): Big-Endian. _ Các hàm chuyển đổi: • u_short ntohs (u_short); • u_long ntohl (u_long); • u_short htons (u_short); • u_long htonl (u_long); 2. Chuyển đổi dạng địa chỉ _ Dạng biểu diễn địa chỉ IPv4: • Số nguyên 4 byte. • Chuỗi dấu chấm thập phân (Dotted Decimal). _ Các hàm chuyển đổi: • unsigned long inet_addr (const char FAR* CD); • char FAR* inet_ntoa (struct in_addr in); _ Cấu trúc địa chỉ: struct in_addr { union { struct { u_char s_b1, s_b2, s_b3, s_b4; } S_un_b; struct { u_short s_w1,s_w2; } S_un_w; u_long S_addr; } S_un; #define s_addr S_un.S_addr // can be used for most tcp & ip code #define s_host S_un.S_un_b.s_b2 // host on imp #define s_net S_un.S_un_b.s_b1 // network #define s_imp S_un.S_un_w.s_w2 // imp #define s_impno S_un.S_un_b.s_b4 // imp # #define s_lh S_un.S_un_b.s_b3 // logical host }; _ Ví dụ: // PHOSTENT pHost = gethostbyname (“”); if (pHost != NULL) { IN_ADDR inAddr; memcpy (&inAddr, pHost->h_addr, 4); // inAddr.s_addr = pHost->h_addr; Cstring sAddress = inet_ntoa (inAddr); // } memcpy (&inAddr, pHost->h_addr, 4); VI. Các hàm socket 1. Quy trình sử dụng a) Có thiết lập cầu nối b) Không thiết lập cầu nối 2. Chi tiết sử dụng a) Tạo socket _ Cú pháp: SOCKET socket (int af, int type, int protocol); _ Thông số: • af: họ địa chỉ AF_INET. • type: loại địa chỉ – SOCK_STREAM (có thiết lập cầu nối – TCP), SOCK_DGRAM (không thiết lập cầu nối – UDP). • protocol: loại giao thức – 0. _ Kết quả trả về: • Thành công: handle của socket vừa tạo. • Thất bại: INVALID_SOCKET. _ Ví dụ: SOCKET s; // socket descriptor char lpszMessage[100]; // informational message s = socket(AF_INET, SOCK_STREAM, 0); if (s == INVALID_SOCKET) wsprintf (lpszMessage, “socket() generated error %d”, WSAGetLastError()); else lstrcpy(lpszMessage, “socket() succeeded”); MessageBox(NULL, lpszMessage, “Info”, MB_OK); b) Đóng socket _ Cú pháp: int closesocket(SOCKET s); _ Thông số: s: handle máy muốn đóng. _ Kết quả trả về: Thất bại: SOCKET_ERROR. c) Gán thông tin socket _ Cú pháp: int bind (SOCKET s, const struct sockaddr FAR* addr, int addrlen); _ Thông số: • s: handle của socket chờ gán thông tin. • addr: địa chỉ cấu trúc dùng để chứa thông tin socket phía kết nối đến. • addrlen: địa chỉ biến chứa kích thước cấu trúc bởi addr. _ Cấu trúc thông tin socket: struct sockaddr { u_short sa_family; // address family char sa_data[14]; // up to 14 bytes of direct address }; struct sockaddr_in { short sin_family; // address family u_short sin_port; // service port struct in_addr sin_addr; // Internet address char sin_zero[8]; // filler }; _ Ví dụ: SOCKET s; // socket descriptor char lpszMessage[100]; // informational message SOCKADDR_IN addr; // Internet address // create a stream socket s = socket(AF_INET, SOCK_STREAM, 0); if (s != INVALID_SOCKET) { // fill out the socket’s address information addr.sin_family = AF_INET; addr.sin_port = htons(1050); addr.sin_addr.s_addr = htonl(INADDR_ANY); // bind the socket to its address if (bind(s, (LPSOCKADDR)&addr, sizeof(addr)) == SOCKET_ERROR) { wsprintf(lpszMessage, “ bind() generated error %d”, WSAGetLastError()); MessageBox(NULL, lpszMessage, “Info”, MB_OK); } else { //... } } • Trường hợp không chỉ định port: sin_port = 0; • Lấy thông tin socket: int getsockname (SOCKET s, struct sockAddr* addr, int* addrlen); d) Lắng nghe _ Cú pháp: int listen (SOCKET s, int backlog); _ Thông số: • s: handle máy muốn đóng. • backlog: kích thước hàng đợi kết nối. _ Kết quả trả về: Thất bại: SOCKET_ERROR. _ Ví dụ: SOCKET s; // socket descriptor char lpszMessage[100]; // informational message SOCKADDR_IN addr; // Internet address // create a stream socket s = socket(AF_INET, SOCK_STREAM, 0); if (s != INVALID_SOCKET) { // fill out the socket’s address information addr.sin_family = AF_INET; addr.sin_port = htons(1050); addr.sin_addr.s_addr = htonl(INADDR_ANY); // bind the socket to its address if (bind(s, (LPSOCKADDR)&addr, sizeof(addr)) != SOCKET_ERROR) { // listen for connections (queueing up to three) if (listen(s, 3) == SOCKET_ERROR) { wsprintf(lpszMessage, “listen() generated error %d”, WSAGetLastError()); MessageBox(lpszMessage, “Info”); } else { //... } } } e) Tiếp nhận _ Cú pháp: SOCKET socket (SOCKET s, struct sockAddr FAR* addr, int FAR* addrlen); _ Thông số: • s: handle của socket chờ tiếp nhận “nói” (“lắng nghe”). • addr: địa chỉ cấu trúc dùng để chứa thông tin socket phía kết nối đến. • addrlen: địa chỉ biến chứa kích thước cấu trúc bởi addr. _ Kết quả trả về: • Thành công: handle của socket giao tiếp với phía kết nối đến • Thất bại: INVALID_SOCKET. _ Ví dụ: SOCKET s; // socket descriptor SOCKET clientS; // client socket descriptor char lpszMessage[100]; // informational message SOCKADDR_IN addr; // Internet address SOCKADDR_IN clientAddr; // Internet address IN_ADDR clientIn; // IP address int nClientAddrLen; // create a stream socket s = socket(AF_INET, SOCK_STREAM, 0); if (s != INVALID_SOCKET) { // fill out the socket’s address information addr.sin_family = AF_INET; addr.sin_port = htons(1050); addr.sin_addr.s_addr = htonl(INADDR_ANY); // bind the socket to its address if (bind(s, (LPSOCKADDR)&addr, sizeof(addr)) != SOCKET_ERROR) { // listen for connections (queueing up to three) if (listen(s, 3) != SOCKET_ERROR) { // set the size of the client address structure nClientAddrLen = sizeof(clientAddr); // accept a connection clientS = accept(s, (LPSOCKADDR)&clientAddr, &nClientAddrLen); if (clientS == INVALID_SOCKET) { wsprintf(lpszMessage, “ accept() generated error %d”, WSAGetLastError()); MessageBox(lpszMessage, “Info”); } else { // copy the four byte IP address into an IP address structure memcpy(&clientIn, &clientAddr.sin_addr.s_addr, 4); // print an informational message wsprintf(lpszMessage, “accept() ok: client IP address is %s, port is %d”, inet_ntoa(clientIn), ntohs(clientAddr.sin_port)); 114 Part II n Basics of WinSock Programming MessageBox(lpszMessage, “Info”); ... } } } } _ Trường hợp lấy thông tin phía kết nối sau khi tiếp nhận kết nối: int getpeername (SOCKET s, struct sockAddr* addr, int* addrlen); _ Ví dụ: // accept a connection clientS = accept(s, NULL, NULL); if (clientS == INVALID_SOCKET) { wsprintf(lpszMessage, “accept() generated error %d”, WSAGetLastError()); MessageBox(lpszMessage, “Info”); } else { if (getpeername(clientS, (LPSOCKADDR)&clientAddr, &nClientAddrLen)) == SOCKET_ERROR) { wsprintf(lpszMessage, “getpeername() generated error %d”, WSAGetLastError()); MessageBox(lpszMessage, “Info”); } else { // copy the four byte IP address into an IP address structure memcpy(&clientIn, &clientAddr.sin_addr.s_addr, 4); // print an informational message wsprintf(lpszMessage, “client IP address is %s, port is %d”, inet_ntoa(clientIn), ntohs(clientAddr.sin_port)); MessageBox(lpszMessage, “Info”); //... } } f) Kết nối _ Cú pháp: int connect (SOCKET s, const struct sockAddr FAR* addr, int addrlen); _ Thông số: • s: handle của socket thực hiện kết nối. • addr: địa chỉ cấu trúc dùng để chứa thông tin socket phía chờ kết nối. • addrlen: địa chỉ biến chứa kích thước cấu trúc bởi addr. _ Kết quả trả về: Thất bại: SOCKET_ERROR. _ Ví dụ: bool ConnectToServer (const Cstring &sServerAddress, short nServerPort) { bool bSuccess = true; m_hSocket = socket (AP_INET, SOCK_STREAM, 0); if (m_hSocket == INVALID_SOCKET) bSuccess = false; else { SOCKADD_IN sockAddr; addrServer.sin_family = AF_INET; addrServer.sin_port = htons(nServerPort); addrServer.sin_addr.s_addr = inet_addr(sServerAddress); if (connect(s, (LPSOCKADDR) &addrServer, sizeof(addrServer)) == SOCKET_ERROR) { bSuccess = false; closesock(m_hSocket); } return bSuccess; } } g) Gửi nhận _ Có thiết lập cầu nối: • Cú pháp: int send (SOCKET s, const char FAR* buf, int len, int flags); int recv (SOCKET s, const char FAR* buf, int len, int flags); • Thông số:  s: handle của socket bên gửi.  buf: địa chỉ vùng đệm chứa dữ liệu cần gửi.  len: kích thước dữ liệu gửi (tính theo byte).  flags: 0, MSG_DONTROUTR, MSG, OOB. • Kết quả trả về:  Thành công: số byte dữ liệu đã gửi.  Thất bại: SOCKET_ERROR. • Ví dụ:  Gửi: SOCKET s; // socket to communicate over char pszBuf[100]; // buffer to send int nBufLen; // number of bytes in buffer to send int nBytesSent; // bytes sent int nError; // error status // create, bind, and connect socket s ... lstrcpy(pszBuf, “Hello, World!”); nBufLen = lstrlen(pszBuf); nBytesSent = send(s, pszBuf, nBufLen, 0); if (nBytesSent == SOCKET_ERROR) r = WSAGetLastError(); else { //... }  Nhận: SOCKET s; // socket to communicate over #define BUFSIZE (100) // receive buffer size char pszBuf[BUFSIZE]; // buffer to receive data int nBytesRecv; // number of bytes received int nError; // error status // create, bind, and connect socket s ... nBytesRecv = recv(s, pszBuf, BUFSIZE, 0); if (nBytesRecv == SOCKET_ERROR) nError = WSAGetLastError(); else { //... } _ Không thiết lập cầu nối: • Cú pháp: int sendto (SOCKET s, const char * buf, int size, int flags, const struct sockAddr FAR* addrTo); int recvfrom (SOCKET s, char* buf, int size, int flags, struct sockAddr FAR* addrFrom); • Thông số:  s: handle của socket bên gửi.  buf: địa chỉ vùng đệm chứa dữ liệu cần gửi.  len: kích thước dữ liệu gửi (tính theo byte).  flags: 0, MSG_DONTROUTR, MSG, OOB. • Kết quả trả về:  Thành công: số byte dữ liệu đã gửi.  Thất bại: SOCKET_ERROR. • Ví dụ:  Gửi: SOCKET s; SOCKADDR_IN addr; #define BUFSIZE (100) char pszBuf[BUFSIZE]; int nBufLen; int nBytesSent; int nError; s = socket(AF_INET, SOCK_DGRAM, 0); if (s == INVALID_SOCKET) { nError = WSAGetLastError(); // ... } else { // fill out the address of the recipient addr.sin_family = AF_INET; addr.sin_port = htons(2050); addr.sin_addr.s_addr = inet_addr(“166.78.16.150”); // assign some data to send lstrcpy(pszBuf, “Hello, World!”); nBufLen = lstrlen(pszBuf); // send the datagram nBytesSent = sendto(s, pszBuf, nBufLen, 0, (LPSOCKADDR)&addr, sizeof(addr)); if (nBytesSent == SOCKET_ERROR) { //... } else { //... } closesocket(s); }  Nhận: char pszMessage[100]; // informational message SOCKET s; // socket to receive data on SOCKADDR_IN addr; // address of the socket #define BUFSIZE (100) // receive buffer size char pszBuf[BUFSIZE]; // receive buffer int nBytesRecv; // number of bytes received int nError; // error code SOCKADDR_IN addrFrom; // address of sender int nAddrFromLen = sizeof(addrFrom); // lengh of sender structure IN_ADDR inFrom; // IP address of sender s = socket(AF_INET, SOCK_DGRAM, 0); if (s == INVALID_SOCKET) { nError = WSAGetLastError(); // ... } else { // fill out the name this server will read data from addr.sin_family = AF_INET; addr.sin_port = htons(2050); addr.sin_addr.s_addr = htonl(INADDR_ANY); // bind the name to the socket if (bind(s, (LPSOCKADDR)&addr, sizeof(addr)) == SOCKET_ERROR) { nError = WSAGetLastError(); // ... } else { nBytesRecv = recvfrom(s, pszBuf, 100, 0, (LPSOCKADDR)&addrFrom, &nAddrFromLen); if (nBytesRecv == SOCKET_ERROR) { nError = WSAGetLastError(); // ... } else { // got some data ... // copy the four byte IP address into an IP address structure memcpy(&inFrom, &addrFrom.sin_addr.s_addr, 4); // print an informational message wsprintf(pszMessage, “server received %d bytes from %s, port is %d”, nBytesRecv, inet_ntoa(inFrom), ntohs(addrFrom.sin_port)); } closesocket(s); } } 3. Hai chế độ hoạt động của socket a) Blocking _ Các hàm thực hiện hoạt động nhập / xuất trên socket chỉ trả về khi tác vụ hoàn tất → Tiến trình bị chặn nếu tác vụ chưa hoàn tất (sự kiện mong đợi chưa xảy ra). b) Non-blocking _ Các hàm thực hiện hoạt động nhập / xuất trên socket trở vền gay sau khi được gởi. _ Phát sinh lỗi WSAEWOULDBLOCK nếu tác vụ yêu cầu chưa hoàn tất → Tiến trình không bị chặn. 4. Mô hình xử lý a) Blocking _ Sử dụng các hàm socket theo chế độ hoạt động mặc định (blocking). _ Thường sử dụng giải pháp xử lý đa luồng (multithread). b) WSAAsyncSelect _ Mô hình xử lý bất đồng bộ: • Ứng dụng đăng ký sự kiện mong đợi xảy ra trên socket. • Hệ thống giám sát và gửi thông điệp báo hiệu đến ứng dụng khi sự kiện xảy ra. • Ứng dụng xử lý khi nhận được thông điệp. _ Giải pháp phù hợp với mô hình hoạt động hướng thông điệp của Windows. _ Chọn chế độ xử lý bất đồng bộ: int WSAAsyncSelect (SOCKET s, HWND hWnd, UINT message, long IEvent), trong đó sự kiện mong đợi (IEvent) bao gồm: • FD_ACCEPT, FD_CONNECT. • FD_READ, FD_WRITE. • FD_CLOSE. • FD_OOB. _ Đăng ký nhiều sự kiện: • Đúng: WSAAsyncSelect (s, hWnd, USER_MESSAGE, FD_READ | FD_CLOSE); • Sai: Thông điệp báo hiệu do người lập trình định nghĩa #define (WM_USERtn) const UINT = WM_USERtn; c) Tạo trình xử lý thông điệp (MFC) _ Hàm thành phần của lớp cửa sổ nhận thông điệp: • Khai báo: LRESULT (WPARAM wParam, LPARAM lParam); trong đó:  wParam: handle socket xảy ra sự kiện mong đợi.  lParam: mã sự kiện xảy ra và lỗi sai. • Định nghĩa: LRESULT ::(WPARAM wParam, LPARAM lParam) { if (WSAGETSELECTERROR (lParam == 0) { switch (WSAGETSELECTEVENT (lParam)) { case FD_ACCEPT: //...; break; case FD_READ: //...; break; case FD_CLOSE: //...; break; //... } } else { //... } return LRESULT(); } • Ví dụ: const UINT MSG_ASYNC = WM_USER + 1; bool ::ConnectToServer (const Cstring &sIpAddr, short nServerPort) { bool bSuccess = true; m_hSocket = ...; if (m_hSocket != INVALID_SOCKET) { SOCKADD_IN sockAddr; if (connect (m_hSocket, ..., ... ) == SOCKET_ERROR || WSAAsynSelect (m_hSocket, m_h Wnd, MSG_ASYNC, FD_READ | FD_CLOSE) == SOCKET_ERROR) { closesocket (m_hSocket); bSuccess = false; } } else bSuccess = false; return bSuccess; } LRESULT ::(WPARAM wParam, LPARAM lParam) { if (WSAGETSELECTERROR (lParam) == 0) { switch (WSAGETSELECTEVENT) { case FD_READ: ReceiveData(); break; case FD_CLOSE: closesocket (m_hSocket); break; } } else { //... } return 0L; } _ Đăng ký trình xử lý: BEGIN_MESSAGE_MAP(, ) //... ON_MESSAGE (, ) END_MESSAGE_MAP trong đó: • MSG_ASYNC: thông điệm do người lập trình định nghĩa. • OnAsyncSelect tên trình xử lý thông điệp. VII. Ứng dụng MiniChat 1. Mô tả _ Hai thành phần: • Server:  Quản lý người dùng theo nhóm.  Tổ chức đăng nhập theo nhóm.  Tổ chức trao đổi trong nhóm. • Client:  Giao tiếp với người dùng.  Cho phép đăng nhập theo nhóm.  Cho phép “trò chuyện” trong nhóm. _ Giao thức ứng dụng: • Server port: 2013: có thiết lập cầu nối. • Tập lệnh (Client  Server):  GLIST: yêu cầu danh sách nhóm : LOGIN ,  ULIST: yêu cầu danh sách người dùng trong nhóm:  Gửi nội dung trò chuyện: TALKS  LGOUT: đăng xuất.  Ký hiệu đặc biệt để ngăn cắt: CRLF. • Dạng hồi đáp (Server  Client):  Khi thiết lập cầu nối: xuất lời chào.  Khi nhận các lệnh: trong đó:  : 0 là thành công, 1 là thất bại.  :  Lệnh GLIST (trường hợp thành công): message = , ,  Lệnh ULIST (trường hợp thành công): message = , ,  Lệnh LOGIN, LGOUT, TALKS (hoặc trường hợp thất bại): message = 2. Client a) Giao diện _ Hộp thoại chính. _ Hộp thoại phụ trợ. b) Trạng thái hoạt động c) Cài đặt _ Ứng dụng dạng hộp thoại. _ Lớp hộp thoại ứng dụng: • Dữ liệu:  socket handle: SOCKET m_hSocket.  Trạng thái hiện tại: enum FSTATE {FS_BEGIN, FS_CONNECT, FS_GLIST, FS_LOGIN, FS_ULIST, FS_TALKS, FS_LOGOUT}; FSTATE m_fState  Tên đăng nhập: Cstring m_sUserName  Thành phần liên kết với điều khiển: CString m_sMessage CListBox m_lbGroups CListBox m_lbUsers CListBox m_lbContent • Thao tác:  Trình xử lý sự kiện:  Tác động trên các nút:  Kết nối (Connect):  Kiểm tra trạng thái chưa kết nối.  Mở hộp thoại kết nối (Connect Dialog).  Thực hiện kết nối – gọi thao tác phụ trợ (ConnectToServer).  Đặt trạng thái: đang kết nối.  Hoạt động bất đồng bộ:  Lấy Danh sách Nhóm (Get Groups):  Kiểm tra trạng thái kết nối.  Gởi lệnh GLIST.  Đặt trạng thái: đang lấy danh sách nhóm.  Đăng nhập (Login):  Kiểm tra trạng thái đã kết nối và chưa đăng nhập.  Mở hộp thoại đăng nhập (Login Dialog).  Gởi lệnh LOGIN.  Đặt trạng thái: đang đăng nhập.  Lấy Danh sách Người dùng (Get Users):  Kiểm tra trạng thái đã đăng nhập.  Gởi lệnh ULIST.  Đặt trạng thái: đang lấy danh sách người dùng.  Gửi (Send):  Kiểm tra trạng thái đã đăng nhập.  Gởi lệnh TALKS.  Đặt trạng thái: đang trao đổi.  Đăng xuất (Logout):  Kiểm tra trạng thái đã đăng nhập.  Gởi lệnh LGOUT.  Đặt trạng thái: đang thoát ra khỏi nhóm.  Hoạt động bất đồng bộ:  Kiểm tra mã báo lỗi.  Thực hiện / gọi tác vụ xử lý sự kiện tương ứng:  FD_CONNECT: báo kết nối thành công.  FD_READ: nhận và xử lý hồi đáp từ server.  FD_CLOSE: đóng socket  mfState = FS_BEGIN  Thông báo đã đóng kết nối  Xử lý hồi đáp:  Kết nối (Connect):  Hiển thị lời chào nhận từ server.  Đặt trạng thái: đã kết nối.  Lấy Danh sách Nhóm (Get Groups):  Kiểm tra mã hồi đáp: nếu thành công thì hiển thị danh sách nhóm  tách các tên nhóm và đưa vào listbox; nếu thất bại thì hiển thị thông điệp báo lỗi nhận từ server.  Hiển thị thông báo nhận từ server.  Đăng nhập (Login):  Kiểm tra mã hồi đáp: nếu thành công thì đặt trạng thái đã đăng nhập (sẵn sàng đón nhận thông điệp); nếu thất bại thì đặt trạng thái: đã kết nối (chưa đăng nhập).  Hiển thị thông báo nhận từ server.  Lấy Danh sách Người dùng (Get Users):  Kiểm tra mã hồi đáp: nếu thành công thì hiển thị danh sách người dùng; nếu thất bại thì hiển thị thông điệp báo lỗi nhận từ server.  Đặt trạng thái: sẵn sàng nhận thông điệp.  Gửi (Send):  Kiểm tra mã hồi đáp: Thành công: đặt trạng thái: đã kết nối (chưa đăng nhập).  Hiển thị thông báo nhận từ server.  Thao tác phụ trợ:  Tạo cầu nối đến server (ConnectToServer):  Thông số: [vào] Địa chỉ server (dạng chuỗi), [vào] số hiệu cổng.  Kết quả trả về: nếu thành công là true; nếu thất bại là false.  Thực hiện: Kết nối (hoạt động theo chế độ blocking), chọn chế độ hoạt động bất đồng bộ: FD_READ | FD_CLOSE.  Nhận dữ liệu (ReceiveData):  Xử lý nhận dữ liệu: nội dung hồi đáp từ server.  Gọi thao tác xử lý hồi đáp tương ứng, tùy thuộc trạng thái hiện tại của client.  Gởi dữ liệu (SendData). 3. Server a) Giao diện _ Hộp thoại chính. _ Hộp thoại phụ trợ. b) Cài đặt _ Ứng dụng dạng hộp thoại. _ Các lớp phụ trợ: • CUserSocket:  Kế thừa từ CObject.  Dữ liệu:  socket handle: SOCKET m_hSocket.  Tên đăng nhập: CString m_sName.  Địa chỉ, cổng phía kết nối: int m_nPeerPort. CString m_sPeerAddress.  Thao tác:  Truy xuất thông tin.  Cập nhật thông tin. • CUserGroup:  Kế thừa từ CObject.  Dữ liệu:  Tên nhóm: CString m_sName.  Danh sách các usersocket: CTypedPtrList m_userList.  Thao tác:  Thêm người dùng.  Lấy người dùng ra khỏi nhóm.  Tìm người dùng.  Truy xuất tên nhóm.  Lấy danh sách tên nhóm. • CGroupList:  Dữ liệu: Danh sách các nhóm: CArray m_groupList.  Thao tác:  Tìm nhóm.  Theo tên nhóm.  Theo socket handle.  Tìm nhóm và người dùng: theo socket handle.  Lấy danh sách tên nhóm. _ Lớp hộp thoại ứng dụng: • Dữ liệu:  socket handle – “lắng nghe”.  Danh sách nhóm: CGroupList m_groupList.  Danh sách người dùng mới kết nối: CUserList m_groupNewUsers.  Thành phần liên kết với các điều khiển. • Thao tác:  Khởi tạo:  Thông số: port lắng nghe.  Kết quả trả về: nếu thành công là socket handle; nếu thất bại là INVALID_SOCKET.  Dữ liệu nhóm: gồm tập tin dữ liệu hoặc cơ sở dữ liệu.  Socket “lắng nghe” – InitListenSocket: Socket() Bind() Listen() WSAAsyncSelect() - FD_ACCEPT  Trình xử lý sự kiện:  Tác động trên phần tử giao diện: ListBox: Nhóm. Button: Xóa, ẩn.  Hoạt động bất đồng bộ.  Xử lý đáp ứng yêu cầu từ client:  GLIST:  Tạo nội dung hồi đáp theo quy định: sử dụng thao tác lấy danh sách tên nhóm của CGroupList.  Gởi hồi đáp.  LOGIN:  Kiểm tra người dùng chưa đăng nhập. Kiểm tra thông tin đăng nhập:  Tên nhóm có thật:  Tên người dùng không trùng lắp.  Chuyển người dùng sang nhóm đăng nhập (trường hợp thành công):  Xóa khỏi danh sách người dùng mới kết nối.  Thêm vào danh sách người dùng của nhóm đăng nhập.  Cập nhật tên đăng nhập.  Tạo hồi đáp theo quy định gửi hồi đáp.  ULIST:  Kiểm tra là người dùng đã đăng nhập (hiện diện trong danh sách của một nhóm).  Tạo nội dung hồi đáp theo quy định (sử dụng thao tác lấy danh sách tên người dùng của CUserGroup).  Gửi hồi đáp.  LGOUT:  Kiểm tra là người dùng đã đăng nhập (hiện diện trong danh sách của một nhóm).  Chuyển người dùng sang danh sách mới kết nối (trường hợp thành công).  Xóa khỏi danh sách người dùng mới của nhóm.  Thêm vào danh sách người dùng mới kết nối.  Tạo hồi đáp theo quy định.  Gửi hồi đáp.  “Broadcast”:  Kiểm tra là người dùng đã đăng nhập (hiện diện trong danh sách của một nhóm).  Sử dụng thao tác tìm nhóm và người dùng theo socket handle của lớp CGroupList.  Tạo thông điệp “broadcast” (tên người dùng gửi + “:” + nội dung trò chuyện).  Gửi thông điệp “broadcast” cho các người dùng còn lại trong nhóm.  Thao tác phụ trợ:  Tiếp nhận kết nối (AcceptConnection):  Nhận dữ liệu (ReceiveData).  Phân tích yêu cầu (ParseRequest):  Xác định mã lệnh, gọi tác vụ.  Xừ lý đáp ứng yêu cầu.  Gửi dữ liệu (SendData). 4. Minh họa _ Thiết lập kết nối: bool CChatClDlg::ConnectToServer (const CString &sServerAddress, short nServerPort) { bSuccess = true; m_hSocket = socket (, , 0); if (m_hSocket != INVALID_SOCKET) { SOCKETADD_IN sockAddr; sockAddr.sin_family = sockAddr.sin_port = htons (nServerPort); if (isalpha (sServerAddress[0]) { PHOSTENT pHost = gethostbyname (sServerAddress); if (pHost) memcpy (&sockAddr.sin_addr, pHost->h_addr, 4); } else sockAddr.sin_addr = inet_addr (sServerAddress); if (connect (m_hSocket, (LPSOCKADDR) & sockAddr, sizeof(sockAddr)) == SOCKET_ERROR || WSAAsyncSelect, FDREAD | FDCLOSE) == SOCKET_ERROR) { closesocket (m_hSocket); bSuccess = false; } else bSuccess = false; return bSuccess; } } void CChatClDlg::OnBnClickedConnect() { if (m_fState < FSCONNECT) // chưa kết nối { ConnectDlg dlg; // hộp thoại để nhập thông tin server if (dlg_DoModal() == IDOK) { if (ConnectToServer (dlg.m_nServerAddress) == dlg.m_nServerPort) { mlbContent.AddString (_T(“Đã kết nối”)); m_fState = FS_CONNECT; } else mlbContent.AddString (_T(“Kết nối thất bại”)); } } } _ Trình xử lý cho hoạt động xử lý bất đồng bộ: • Hàm thành phần của CDlg (lớp hộp thoại chính của ứng dụng). LRESULT CDlg::OnAsyncSelect (WPARAM wParam, LPARAM lParam) { if (WSAGETSELECTERROR (lParam) == 0) { switch (WSAGETSELECT (lParam)) { case FD_READ: ReceiveData(); break; case FD_CLOSE: closesocket (m_hSocket); m_fState = FS_BEGIN; m_lbContent.AddString(_T(“...”); break; } } else { //} return LRESULT(0); } • Đăng ký trình xử lý: BEGIN_MESSAGE_MAP (CDlg:Cdialog) ... ON_MESSAGE (MSG_ASYNC, OnAsyncSelect); // tự định nghĩa END_MESSAGE_MAP const UNIT MSG_ASYNC = WM_USER + 1; • Hàm thành phần thực hiện nhận dữ liệu: const int BUF_LEN = 255; void CDlg::ReceiveData() { char szBuffer[BUF_LEN + 1]; int nRecvBytes = recv (m_hSocket, szBuffer, BUF_LEN , 0); if (nRecvBytes > 0) { szBuffer[nRecvBytes] = ‘\0’; switch (m_fState): { case FS_CONNECT: m_lbContent.AddString (szBuffer); break; case FS_GLIST: ProcessGListReply(nRecvBytes); break; } } } void CDlg::SendData(const CString &sData) { return send(m_hSocket, sData, sData.GetLength (), 0) != SOCKET_ERROR); } _ Xử lý yêu cầu lấy danh sách nhóm: • Gởi yêu cầu (lệnh GLIST): khi người dùng nhấn nút GetGroup  Cài đặt trình xử lý sự kiện nhấn nút GetGroup. • Xử lý hồi đáp: khi có nhận dữ liệu từ server và client đang ở trạng thái “yêu cầu danh sách nhóm” (FS_GLIST). • Khi nhấn nút GetGroup: void CDlg::OnBnClickedGetGroup() { if (m_fState >= FS_CONNECT) //đã kết nối { m_fState = FS_GLIST; } } • Hàm thành phần xử lý hồi đáp cho yêu cầu lấy danh sách nhóm: void CDlg::ProcessGListReply (const CString &sReply) { if (sReply[0] == “MaHoiDap”) { int end, begin = HEADER_LEN // tự định nghĩa m_lbGroups.ResetContent(); while ((end = sReply.Find (‘,’, begin)) >= 0) { m_lbGroups.AddString (sReply.Mid (begin, end – begin)); begin = end + 1; } m_lbGroups.AddString (sReply.Mid(begin)); } else } _ Xử lý đăng nhập: • Xử lý gởi yêu cầu (lệnh LOGIN): cần 2 thông số là tên nhóm và tên người dùng. void C...Dlg::OnBnClickedLogin() { if (m_fState >= FS_CONNECT && m_fState < FS_LOGIN) { CLoginDlg dlg; int index = m_lbGroups.GetCurSel(); if (index != LB_ERR) m_lbGroups.GetText (index, dlg.m_sGroupsName) ; if (dlg.DoModal() == IDOK) { if (dlg.m_sGroupName.IsEmpty() || dlg.m_sUserName.IsEmpty()) MessageBox(_T(“Thiếu thông tni”), _T(“Lỗi”), MB_OK | MB_ICON ERROR) else { CString sLogin Cmd; sLoginCmd.Format (_T(“LOGIN %S, %S”), dlg.m_GroupName, dlg.m_sUserName); if (SendData (sLoginCmd)) { m_fState = FS_LOGIN; m_UserName = dlg.m_UserName; } } } } } • Xử lý hồi đáp. void CDlg::ProcessLoginReply (const CString &sReply) { if (sReply[0] == ‘0’) m_fState = ...;// sẵn sàng nhận nội dung trò chuyện trong nhóm else { m_lbContent.AddString (sReply.Mid(HEADER_LEN)); m_fState = FS_CONNECT; } } _ Xử lý yêu cầu lấy danh sách người dùng: • Xử lý gởi yêu cầu (lệnh ULIST): tương tự trường hợp yêu cầu danh sách nhóm nhưng m_fState >= FS_LOGIN. • Xử lý hồi đáp: tương tự trường hợp xử lý hồi đáp cho yêu cầu danh sách nhóm:  Hiển thị danh sách trên listbox m_lbUsers.  m_fState = ...;// sẵn sàng nhận nội dung trò chuyện. _ Xử lý yêu cầu trò chuyện: • Xử lý gởi yêu cầu (lệnh TALKS): có một thông số là nội dung trò chuyện. • Xử lý hồi đáp: hiển thị nội dung trò chuyện. void CDlg::OnBnClickedSend() { if (m_fState >= FS_LOGIN) { UpdataData(true); if (SendData (_T(“TALKS “) + m_sMessage)) { m_lbContent.AddString (m_sUserName + _T(“: “) + m_sMessage); m_fState = FS_TALKS; m_sMessage.Empty(); UpdataData(false); } } } _ Xử lý yêu cầu đăng xuất: • Xử lý gởi yêu cầu (lệnh LOGOUT): void CDlg::OnBnClickedLogout() { if (m_fState >= FS_LOGIN) { UpdataData(true); if (SendData (_T(“LGOUT“)) m_fState = FS_LOGOUT; } } • Xử lý hồi đáp: void CDlg::ProcessLooutReply () { if (sReply[0] == ‘0’) m_fState = FS_CONNECT; m_lbContent.AddString (sReply.Mid(HEADER_LEN)); } _ Xử lý khi người dùng đóng ứng dụng: • Đóng socket. • Trình xử lý thông điệp: .WM_CLOSE. void CDlg::OnClose() { if (m_fState > FS_BEGIN) closesocket (m_hSocket); } VIII. Thư viện lớp MFC 1. Giới thiệu _ MFC: • Microsoft Foundation Class Library. • Application Framework. _ MFC Socket Classes: • CAsyncSocket • CSocket. • 2. Lớp CASynSocket _ Đặc tả khái niệm socket. _ Kế thừa từ CObject: • Cung cấp giao diện lập trình mạng: Giao diện lập trình mạng mức thấp (che giấu chi tiết sử dụng Windows API):  Các hàm thành phần: tương ứng với các hàm Winsock API.  Các hàm callback: được tự động gọi khi có sự kiện xảy ra với socket.  Người lập trình tự giải quyết các vấn đề:  Hoạt động blocking.  Sự khác biệt: Trật tự byte và mã ký tự. • Thành phần:  Dữ liệu: SOCKET m_hSocket.  Tác vụ:  Tạo lập:  CASyncSocket().  Create().  Truy xuất thông tin:  GetSockName().  GetSockNameEx() (cho IPv6).  Tạo, đóng socket:  Socket().  Close().  Thiết lập cầu nối:  Bind().  Listen().  Accept().  Connect().  Gởi nhận dữ liệu:  Send().  Receive().  SendTo().  ReceiveFrom().  SendToEx().  ReceiveFromEx().  Khác:  Attach().  Detach().  Callback:  OnAccept(): FD_ACCEPT.  OnConnect(): FD_CONNECT.  OnSend(): FD_WRITE.  OnReceive(): FD_READ.  Chi tiết sử dụng:  Tạo đối tượng socket:  Quá trình hai bước:  Thiết lập.  Gọi hàm Create: bool Create ( UINT nSocketPort = 0; int nSocketType = SOCK_STREAM, long lEvent = FD_READ | FD_WRITE | FD_OOB | FD|ACCEPT | FD_CONNECT | FD_CLOSE LPCTSTR lpszSocketAddress = NULL );  Minh họa:  Cấp phát tĩnh: CasyncSocket sock; Sock.Create();  Cấp phát động: CAsyncSocket *pSock = new CAsyncSocket; pSocke->Create();  Thiết lập cầu nối:  Lắng nghe: bool Listen (int nConnectionBacklog = 5); sock.Listen(); pSock->Listen();  Tiếp nhận: Không gọi hàm Create cho đối tượng rConnectedSocket: CAsyncSocket connectedSocket; sock.Accept (connectedSocket, ...); CAsyncSocket *pConnectedSocket = new CAsyncSocket; pSock->Accept (**pConnectedSocket, ...);  Kết nối: bool Connect (LPCTSTR lpszHostAddress, UINT nHostPort); bool Connect (const SOCKADDR* lpSockAddr, int nSockAddrLen); sock.Connect (“127.0.0.1”, 2012); pSock->Connect (“127.0.0.1”, 2012);  Gởi nhận dữ liệu:  Có cầu nối: virtual int Send (const void *lpBuf, int nBufLen, int nFlags = 0); virtual int Receive (void *lpBuf, int nBufLen, int nFlags = 0);  Không cầu nối.  Hủy đối tượng socket:  Cấp phát tĩnh: tự hủy khi thực thi khỏi tầm vực khai báo.  Cấp phát động: delete pSock.  Phương thức hủy tự động gọi thao tác Close.  Xử lý sự kiện xảy ra với socket:  Định nghĩa lại thao tác xử lý cho các hàm callback. virtual void OnAccept (int nErrorCode); virtual void OnClose (int nErrorCode); virtual void OnConnect (int nErrorCode); virtual void OnReceive (int nErrorCode); virtual void OnSend (int nErrorCode); virtual void OnOutOfBandDate (int nErrorCode);  Các hàm callback được tự động gọi khi có sự kiện xảy ra với socket. 3. CSocket a) Khái quát _ Kế từ từ lớp CasyncSocket: mức trừu tượng cao. _ Trừu tượng hóa họat động gửi nhận dữ liệu: • CsocketFile. • CArchive. _ Chế độ hoạt động: blocking (phù hợp với CArchive). b) Thành phần _ Tạo đối tượng: CSocket(); bool Create { UNIT nSocketPort = 0; int nSocketyType = SOCK_STREAM; LPCTSTR lpszSocketAddress = NULL; } _ Liên quan đến blocking mode: bool IsBlocking(); void CancelBlockingCall(); virtual bool OnMessagPending(); static CSocket * PASCAL FromHandle (SOCKET hSocket); c) Chi tiết sử dụng _ Tạo đối tượng socket. _ Thiết lập cầu nối: • Server:  Socket.Listen();  Sock.Accept();  pSock->Listen();  pSock->Accept(); • Client:  sock.Connect();  pSock->Connect(); _ Gửi nhận dữ liệu: • Tạo đối tượng CSocketFile (liên kết với đối tượng CSocketFile): CSocketFile sockFile (&sock, ); CSocketFile sockFile (psock, ); • Để nhận: CArchive arIn (&sockFile, CArchive::load, ...); • Để gởi: CArchive arOut (&sockFile, CArchive::store, ...); • CArchive không làm việc với Datagram Socket. _ Kết thúc: Hủy đối tượng CArchive, CSocketFile, CSocket. 4. Vấn đề thiết kế a) Xây dựng lớp socket _ Kế thừa từ CAsyncSelect, CSocket. _ Bổ sung thành phần mới. _ Định nghĩa lại các thao tác: CAsyncSelect::OnReceive(...). CAsyncSelect::OnSend(...). CAsyncSelect::OnAccept(...). CAsyncSelect::OnClose(...). ... CSocket::OnMessagePeding(...). b) Gởi nhận dữ liệu cấu trúc _ Cấu trúc thông điệp: struct Message { long m_lMagicNumber; short m_nCommand; short m_nParam1; short m_nParam1; } void Message::Serialize(CArchive ar) { if (ar.IsStoring()); { ar << (DWORD) htonl(m_lMagicNumber); ar << (DWORD) htons(m_lMagicNumber); ar << (WORD) htonl(m_lMagicNumber); ar << (WORD) htons(m_lMagicNumber); } else { WORD w; DWORD dw; ar >> dw; m_lMagicNumber = ntohl((long) dw); ar >> w; m_nCommand = ntohs((short) w); ar >> w; m_nParam1 = ntohns((short) w); ar >> dw; m_nParam2 = ntohnl((long) w); } } _ VÍ dụ: CString SinhVien::toString()const { CString kq; kq.Format(_T("%s, %s, %d"), m_sMaso, m_sHoTen, m_nNamSinh); } c) Mô hình xử lý _ Bất đồng bộ: dùng CAsyncSocket _ Đồng bộ: thường dùng CSocket (đa luồng). Chương 3: ỨNG DỤNG INTERNET I. Hệ thống FTP 1. Giới thiệu _ Giao thức cấp ứng dụng: • Hỗ trợ dùng chung tập tin, sử dụng tập tin trên máy ở xa. • Che dấu sự khác biệt giữa các hệ thống lưu trữ tập tin. • Cung cấp dịch vụ chuyển tập tin tin cậy và hiệu quả giữa các máy. _ Đặc điểm: • Mô hình client – server:  Server:  Quản lý hệ thống tập tin trên máy ở xa  Đáp ứng yêu cầu làm việc trên tập tin từ phía lcient  Client:  Giao tiếp với server: làm việc với tập tin trên máy ở xa.  Giao tiếp người dùng. • Sử dụng giao thức TCP:  Trao đổi dữ liệu tin cậy.  Có thiết lập cầu nối:  Cầu nối điều khiển (control connection).  Cầu nối dữ liệu (data connection). • Yêu cầu chứng thực người dùng:  Người dùng đã đăng ký tài khoản: tên và mật khẩu.  Người dùng chưa đăng ký: bí danh. _ Mô hình hoạt động: • Client:  Giao diện người dùng (User Interface – UI):  Giao diện dạng dòng lệnh.  Giao diện đồ họa.  Giao thức phiên dịch (Protocol Interpreter – PI):  Diễn dịch yêu cầu từ UI  Giao tiếp với server PI:  Thiết lập cầu nối điều khiển.  Gởi yêu cầu (FTP command).  Xử lý hồi đáp.  Điều khiên họa động của client DTP.  Tiến trình Vận chuyển Dữ liệu (Data Transfer Process – DTP). • Server:  Giao thức phiên dịch (Protocol Interpreter – PI):  Chờ yêu cầu kết nối từ client.  Giao tiếp với client PI:  Nhận yêu cầu (FTP commmand).  Giụ hồi đáp.  Điều khiển hoạt động của server DTP.  Tiến trình Vận chuyển Dữ liệu (Data Transfer Process – DTP). _ FTP và NVT: • FTP client thiết lập cầu nối • FTP client dàn xếp việc thiết lập cầu nối giữa hai FTP server. _ Hai loại cầu nối: • Cầu nối điều khiển:  Client PI / server PI trao đổi yêu cầu / hồi đáp.  Đặc điểm:  Thiết lập khi bắt đầu phiên làm việc.  Duy trì cho đến khi kết thúc phiên làm việc.  Thiết lập:  Server PI lắng nghe tại cổng dành riêng (port 21).  Client PI chủ động kết nối đến server PI.  Server PI gởi hối đáp sẵn sàng khi kết nối thành công. • Cầu nối dữ liệu:  Client DTP / server DTP gởi nhận dữ liệu.  Đặc điểm:  Thiết lập trước khi bắt đầu truyền dữ liệu  Duy trì trong suốt quá trình truyền dữ liệu, có thể đóng sau khi kết thúc quá trình truyền dữ liệu.  Thiết lập:  Server mở cầu nối chủ động:  Client DTP lắng nghe tại cổng tự chọn (mở cầu nối thụ động).  Client PI gởi thông tin về địa chỉ và cổng “lắng nghe” cho server PI.  Server DTP chủ động kết nối theo địa chỉ và cổng đã nhận.  Server mở cầu nối thụ động:  Client PI báo cho server DTP mở cầu nối thụ động (gởi lệnh PASV).  Server PI gởi hồi đáp thông tin về địa chỉ và cổng “lắng nghe” cho client PI.  Client DTP chủ động kết nối đến server DTP. _ Vấn đề truyền dữ liệu: • Tùy chọn:  Dạng biểu diễn của dữ liệu.  Ký tự định dạng.  Cấu trúc.  Chế độ truyền. • Tổng kết:  Ứng dụng FTP chỉ hỗ trợ:  Điều khiển không định dạng in (Nonprint format control).  Cấu trúc tập tin (File structure).  Chế độ dòng (Stream mode).  Người dùng lựa chọn loại tập tin:  ASCII.  Image (binary). _ Tập lệnh: • Các lệnh kiểm soát truy cập (access control commands):  USER (USER ):  Cung cấp tên người dùng cho FTP server.  Lệnh đầu tiên được gởi sau khi thiết lập cầu nối điều khiển.  Mã hồi đáp.  PASS (PASS):  Cung cấp mật khẩu người dùng cho FTP Server.  Thực hiện sau lệnh USER, bổ sung thông tin xác nhận người dùng.  Mã hồi đáp.  ACCT, CWD, CDUP, SMNT, REIN, QUIT. • Các lệnh truyền tải biến (transfer-parameter commands):  PORT (PORT ):  Cung cấp thông tin để thiết lập cầu nối dữ liệu.  Thông tin cung cấp theo định dạng byte, bao gồm :  Địa chỉ máy: h1, h2, h3, h4.  Cổng chờ kết nối: p1, p2.  Mã hồi đáp.  PASV (PASV):  Báo cho tiến trình truyền dữ liệu phía server lắng nghe trên port dữ liệu (không phải port dữ liệu mặc định) và chờ kết nối đến.  Phía server đáp ứng bằng cách gởi hồi đáp kèm thông tin về địa chỉ server và port dữ liệu cùng với định dạng như trong lệnh PORT.  Mã hồi đáp.  TYPE, STRU, MODE. • Các lệnh dịch vụ (service commands):  RETR (RETR ):  Yêu cầu server gởi tập tin (tải xuống).  Mã hồi đáp.  STOR (RETR ):  Yêu cầu server nhận tập tin (tải lên).  Mã hồi đáp.  LIST (LIST ):  Yêu cầu danh sách tập tin trong thư mục hay thông tin về tập tin chỉ định.  Dữ liệu được truyền qua cầu nối dữ liệu.  Mã hồi đáp.  STAT (STAT ):  Xác định trạng thái của FTP server.  Server gởi thông tin trạng thái thông qua hồi đáp (qua cầu nối điều khiển).  Khi thông số là đường dẫn, lệnh STAT có tác dụng như lệnh LIST nhưng dữ liệu được gửi qua cầu nối điều khiển.  Mã hồi đáp.  NOOP (NOOP):  Thường dùng để kiểm tra cầu nối.  Khi nhận được, server gởi hồi đáp OK.  Mã hồi đáp.  STOU, APPE, ALLO, REST, ABOR, RNFR, RNTO, DELE, RMD, MKD, PWD, NLIST, HELP. _ Mã hồi đáp (xyz): • x:  1yz:  Positive Preliminary reply.  Server chờ hồi đáp khác trước khi xử lý lệnh mới.  2yz:  Positive Completion reply.  Server đã đáp ứng hoàn tất yêu cầu.  3yz:  Positive Intermediate reply.  Server chấp nhận yêu cầu nhưng cần thên thông tin.  4yz:  Transient Negative Completion reply.  Yêu cầu không được chấp nhận, có thể gởi yêu cầu lại sau.  5yz:  Permanent Negative Completion reply.  Server không chấp nhận yêu cầu. • y:  x0z:  Syntax.  Liên quan đến lỗi sai cú pháp.  x1z:  Information.  Hồi đáp cho những yêu cầu về thông tin (STAT, HELP, ).  x2z:  Connection  Liên quan đến các cầu nối.  x3z:  Authentication, accounting.  Liên quan đến quá trình chứng thực, đăng ký.  x4z: chưa dùng.  x5z:  File system.  Liên quan đến trạng thái hệ thống tập tin. _ Minh họa: • Dạng tương tác dòng lệnh:  ftp> open nic.ddn.mil .  Name (nic.ddn.mil:happy):anonymous.  ftp> get index.txt .  ftp> get index.txt .  ftp> close . • Phiên làm việc điển hình. II. Giao diện lập trình WinInet 1. Giới thiệu _ WinInet – Win32 Internet Extensions. • Giao diện lập trình cấp cao: Hỗ trợ ây dựng các ứng dụng như FTP client, HTTP client, Gopher client. • Hai cách sử dụng:  Các hàm WinInet.  Các lớp MFC WinInet (afxinet.h). 2. Các hàm WinInet 3. Các lớp MFC WinInet 4. Các bước xây dựng FTP Client a) Khởi đầu phiên làm việc _ Tạo đối tượng CInternetSession: Duy trì đối tượng CInternetSession đến cuối phiên làm việc. CInternetSession *m_pSession; m_pSession = new CinternetSession(); _ Tạo cầu nối: GetFtpConnection(). GetHttpConnection(). GetGopherConnection(). b) Kết nối đến FTP server _ Kết nối: • CFtpConnection *m_pConnection = m_pSession->GetFtpConnection (sServerName, sUserName, sPassword); • CInternetSession::GetFtpConnection. • CFtpConnection:  GetCurrentDirector(...).  SetCurrentDirectory(...).  CreateDirectory(...).  RemoveDirectory(...).  GetFile(...).  PutFile(...).  Remove(...).  Rename(...).  Command(...). c) Chọn thư mục làm việc _ Chọn thư mục lảm việc: • m_pConnection->SetCurrentDirectory(sDirName). • CftpConnection::GetCurrentDirectory(). • CftpConnection::SetCurrentDirectory(). d) Duyệt nội dung thư mục trên FTP server _ Thực hiện: • Tạo đối tượng duyệt tập tin. • Lập duyệt tập tin. • Ví dụ: try { CftpFileFind ftpFind(m_pConnection); Bool bWorking = ftpFind.FindFile(_T(“*”)); while(bWorking) { bWorking = ftpFind.FindNextFile(); lbox.AddString(ftpFind.GetFileURL()); } } catch (CinternetException *pEx) { ... } _ CftpFileFind: • Phương thức thiết lập. • Phương thức duyệt tập tin. • Phương thức khác:  GetFileName, GetFileTitle, GetFilePath, GetRoot.  GetFileURL.  GetCreationTime, GetLastAccessTime, GetLastWrithTime.  GetLength. e) Làm việc trên tập tin _ Tải xuống: • m_pConnection->GetFile(sRemoteFileName, sLocalFileName). • CFtp::GetFile(...). _ Tải lên: • m_pConnection->PutFile(sLocalFileName, sRemoteFileName). • CFtp::PutFile(...). _ Đọc / ghi: • CFtpConnection::OpenFile(...). • CInternetFile::Read(...). • CInternetFile::Write(...). f) Thao tác khác _ m_pConnection->Remove(sFileName). _ m_pConnection->Rename(sExsistingName, sNewName). _ m_pConnection->RemoveDirectory(sDirName). _ m_pConnection->CreateDirectory(sDirName). g) Kết thúc phiên làm việc _ Các cầu nối, tập tin tự động đóng: delete m_pSession. III. Lập trình mạng với .NET (.NET Network Programming) 1. Thư viện hỗ trợ lập trình mạng a) System.Net _ Dns. _ EndPoint, IPEndPoint. _ IPAddress. _ IPHostEntry. _ SocketAddress. _ WebClient. _ WebRequest, WebResponse. _ FtpWebRequest, FtpWebResponse. _ HttpWebRequest, HttpWebResponse. _ HttpListener. b) System.Net.Sockets _ Socket. _ SocketException. _ TcpClient. _ TcpListener. _ UdpClient. _ NetworkStream. c) System.Net.Mail _ Attachment. _ MailAddress. _ MailAddressCollection. _ MailMessage _ SmtpClent _ MailPriority _ SmtpAccess. _ SmtpDeliveryMethod. _ SmtpStatusCode. 2. Xác định thông tin máy a) DNS _ Phương thức: • static String GetHostName(). • static IPHostEntry GetHostByName(String name). • static IPHostEntry GetHostByAddress(IPAddress addr). • static IPHostEntry Resolve(String name). _ Hoạt động bất đồng bộ • BeginGetHostByName, EndGetHostByName. • BeginGetHostByAddress, EndGetHostByAddress. • BeginRessolve, EndRessolve. b) IPHostEntry _ Đặc tả thông tin máy: tên máy, tên địa chỉ. _ Thuộc tính: • Danh sách địa chỉ: AddressList. • Danh sách các tên máy (bí danh): Aliases. • Tên máy: Host. IV. Lập trình Socket 1. Các lớp liên qnan a) System.Net _ IP Address: • Hằng định nghĩa sẵn:  IPAddress.Any.  IPAddress.Broadcast.  IPAddress.Loopback.  IPAddress.None. • Thuộc tính:  Địa chỉ IP: Address.  Họ địa chỉ: AddressFamily. • Phương thức:  Chuyển đổi trật tự byte: short, int, long:  static T HostToNetworkOrder (T num).  static T NetworkToHostOrder (T num).  static bool IsLoopback (IPAddress addr) .  static IPAddress Parse (String ipString) .  static bool TryParse (String ipString, ...).  string ToString().  byte[] GetAddressByte(). _ IPEndPoing: • Thuộc tính:  Địa chỉ IP: Address.  Họ địa chỉ: AddressFamily.  Số hiệu cổng: Port. • Phương thức:  IPEndPoint (long addr, int port).  IPEndPoint (IPAddress addr, int port).  EndPoint Create (SocketAddress sockAddr).  SocketAddress Serialize(). b) System.Net.Sockets _ Hằng liệt kê: • AddressFamily:  InterNetwork.  InterNetworkV6. • ProtocolType:  Tcp.  Udp.  • SocketType:  Stream.  Dgram.  Raw.  • SocketFlags:  None.  DontRoute.  OutOfBand.  _ Socket: • Thuộc tính:  AddressFamily, SocketType, ProtocolType.  Available.  Blocking.  Connected.  Handle.  LocalEndPoint, RemoteEndPoint.  • Phương thức:  Tạo lập:  Socket(AddressFamily family, SocketType sockType, ProtocolType protocol).  void Bind(EndPoint localEP).  Thiết lập cầu nối:  void Listen(int backlog).  Socket Accept().  void Connect (EndPoint remoteEP).  Gởi / nhận:  int Receive(byte[] data).  int Eeceive(byte[] data, SocketFlags flag).  int Send(byte[] data).  int Send(byte[] data, SocketFlags flag).  int ReceiveFrom(byte[] data, ref Endpoint ep).  int ReceiveFrom(byte[] data, SocketFlags flag, ref Endpoint ep).  int SendTo(byte[] data, ref Endpoint ep).  int SendTo(byte[] data, SocketFlags flag, ref Endpoint ep).  Khác:  void Shutdow (SocketShutdown how).  void Close().  GetSocketOption.  SetSocketOption.  bool Pool (int microSeconds, SelectMode mode).  static void Select(IList read, IList write, ILst err, int microSeconds).  Hoạt động bất đồng bộ:  BeginAccept, EndAccept.  BeginConnect, EndConnect.  BeginReceive, EndReceive.  BeginSend, EndSend.  BeginReceiveFrom, EndReceiveFrom.  BeginSendTo, EndSendTo. • Minh họa:  Client: IPAddress host = IPAddress.Parse(“192.168.1.1”); IPEndPoint hostep = new IPEndPoint (host, 8000); Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); Client.Connect (hostep);  Server: IPHostEntry local = Dns.GetHostByName(Dns.GetHostName()); IPEndPoint iep = new IPEndPoint(local.AddressList[0], 8000); Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); Server.Bind (iep); Server.Listen (5); Socket client = server.Accept(); _ Helper Class: • Khái quát: • Xây dựng dựa trên Socket Class. • Che dấu chi tiết sử dụng socket:  TCP:  TcpClient:  TcpListener:  UDP: UdpClient: • TcpClient:  Thuộc tính:  Avaliable.  Connected:  ReceiveBufferSize và SendBufferSize: mặc định là 8192 byte.  ReceiveTimeout và SendTimeout: mặc định là 0 ms.  Phương thức thiết lập: Tạo đối tượng TcpClient và kết nối đến địa chỉ và cổng mặc định:  TcpClient().  TcpClient(AddressFamily family).  TcpClient(IPEndPoint localEndPoint).  TcpClient(String addr, int port).  Kết nối:  Đồng bộ:  void Connect(IPEndPoint ep).  void Conenct(IPAddress addr, int port).  void Connect(String addr, int port).  IAsyncResult BeginConnect(IPAddress address, int port, AsyncCallback requestCallback, Object state).  Bất đồng bộ: void RndConnect (IasyncResult asyncResult()).  Gửi / nhận:  Thông qua NetworkStream:  Truy xuất NetworkStream: NetworkStream GetStream().  Thuộc tính: CanRead, CanWrite, DataAvailable, ReadTimeout, WriteTimeout.  Đọc / ghi:  Đồng bộ: int Read(byte[] buffer, int offset, int size). int ReadByte(). int Write(byte[] buffer, int offset, int size). void WriteByte (byte data).  Bất đồng bộ: IAsyncResult BeginRead (byte[] buffer, int offset, int size, AsyncCallback callback, Object state).  Đóng.  Dữ liệu ký tự: System.Text gồm 2 phương thức GetBytes() và GetString():  ASCIIEncoding.  UnicodeEncoding.  UTF7Encoding.  UTF8Encoding.  Kết hợp các stream:  TcpClient client = new TcpClient (server, port).  BinaryWrite out = new BinaryWriter(new BufferedStream(client.GetStream).  Đóng socket.  Minh họa: TcpClient newclient = new TcpClient (www.isp.net, 8000); NetworkStram ns = newclient.GetStream(); byte[] outbytes = ASCIIEncoding.GetBytes(“Testing”); ns.Write(outbytes, 0, outbytes.Length); byte[] inbytes = new byte[1024]; ns.Read(inbytes, 0, inbytes.Length); String instring = ASCIIEncoding.GetString (inbytes); Console.WriteLine(instring); ns.Close(); newclient.Close(); • TcpListener:  Phương thức thiết lâp:  TcpListener (int port).  TcpListener (IPEndPoint ep).  TcpListener (IPAddress addr, int port).  Lắng nghe / Đóng:  void Start().  void Stop().  Kiểm tra có yêu cầu kết nối: bool Pending().  Tiếp nhận:  Đồng bộ:  Socket AcceptSocket().  TcpClient AcceptTcpClient().  Bất đồng bộ:  BeginAcceptSocket.  EndAcceptSocket.  BeginAcceptTcpClient.  EndAcceptTcpClient.  Minh họa: TcpListener newserver = new TcpListener(9050); newserver.Start(); TcpClient newclient = new server.AcceptTcpClient(); NetworkStream ns = newclient.GetStream(); Byte[] outbytes = Encoding.ASCII.GetBytes(“Testing”); ns.Write(outbytes, 0, oubytes.Length); byte[] inbytes = new byte[1024]; ns.Read(inbytes, 0, inbytes.Length); String instring = Encoding.ASCII.GetString(inbytes); console.WriteLine(instring); ns.Close(); newclient.Close(); newserver.Stop(); 2. Kỹ thuật tránh blocking a) Non-blocking socket _ Sử dụng thuộc tính Blocking. Đặt Blocking = false để đặt socket vào chế độ hoạt động non-blocking. _ Ở chế độ non-blocking: • Khi phương thức được gọi không thể hoàn tất Trả về mã báo sai, chương trình không bị chặn. • Ví dụ: Phương thức Receive trả về 0 khi chưa có dữ liệu để nhận. b) Phương thức Poll _ Dùng phương thức Poll trước khi gọi các hàm có thể gây blocking: bool Poll(int microseconds, SelectMode mode) _ Sử dụng phương thức Select để đưa các socket và danh sách giám sát: Sau khi Select hoàn tất, các IList chỉ chứa những socket thỏa điều kiện kiểm tra: void Select(IList read IList write, IList err, int microseconds) _ Minh họa: ArrayList socketList = new ArrayList(5); socketList.Add(sock1); socketList.Add(sock2); Socket.Select(socketList, null, null, 5000000); byte[ buffer = new byte[1024]; for(i = 0; I < socket.List.Count; i++) { ((Socket)socketLst[i]).Receive(buffer); Console.WriteLine(Encoding.ASCII.GetString(buffer)); } c) Tổng kết _ Tiếp nhận kết nối: • Dùng Socket: Trước khi gọi Accept():  Đặt vào chế độ non-blocking.  Gọi Poll() hoặc Select(). • Dùng TcpListener: Chỉ gọi AcceptSocket() / AcceptTcpClient() khi Peding() = true. _ Kết nối: Dùng Socket trước khi gọi Connect: • Đặt vào chế độ non-blocking. • Gọi Poll() hoặc Select(). _ Gởi dữ liệu: • Dùng Socket: Trước khi gọi Send() / SendTo():  Đặt vào chế độ non-blocking.  Gọi Poll() hoặc Select().  Đặt giá trị thuộc tính SendTimeout. • Dùng TcpClient: Đặt giá trị thuộc tính SendTimeout trước khi gọi Write() trên network stream. _ Nhận dữ liệu: • Dùng Socket: Trước khi gọi Receive() / Receive To():  Đặt vào chế độ non-blocking.  Gọi Poll() hoặc Select().  Đặt giá trị thuộc tính SendTimeout.  Chỉ gọi Receive() / Receive To() khi giá trị thuộc tính Available > 0. • Dùng TcpClient:  Đặt giá trị thuộc tính ReceiveTimeout trước khi gọi Write() trên network stream.  Chi gọi Read() khi giá trị thuộc tính Available = true; 3. Xử lý bất đồng bộ _ Sử dụng các phương thức hoạt động theo chế độ hoạt động bất đồng bộ. • Gọi các phương thức bắt đầu bằng Begin để đăng ký trình xử lý cho sự kiện mong đợi. • Gọi các phương thức bắt đầu bằng End trong trình xử lý để hoàn tất xử lý bất đồng bộ cho sự kiện mong đợi. _ Minh họa: • Client: void ButtonConnectOnClick(object ob), EventArgs ea) { conStatus.Text = “Connecting”; m_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); // Socket newSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream); IPEndPoint iep = new IPEndPoint(IPAddress.Parse(“127.0.0.1”), 9050); m_socket.BeginConnect(iep, new AsyncCallback(Connected), null); // newSocket.BeginConnect(iep, new AsyncCallback(Connected), newSocket); } void Connected(IAsyncResult iar) { //Socket client = (Socket)iar.AsyncState; try { m_socket.EndConnect(iar); // client.EndConnect(iar); conStatus.Text = “Connect to: “ + m_socket.RemoteEndPoint.ToString(); //conStatus.Text = “Connect to: “ + client.RemoteEndPoint.ToString(); m_socket.BeginReceive(data, 0, size, SocketFlags.None, new AsyncCallback(ReceiveData), null); // client. BeginReceive(data, 0, size, SocketFlags.None, new AsyncCallback(ReceiveData), null); } catch (SocketException) { conStatus.Text = “Error connecting”; } } void ReceiveData(IAsyncResult iar) { // Socket client = (Socket)iar.AsyncState; int recv = m_socket.EndReceive(iar); // int recv = client.EndReceive(iar); string stringData = Encoding.ASCII.GetString(data, 0, recv); results.Items.Add(stringData); } • Server: void ButtonListenOnClick(object ob), EventArgs ea) { m_server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); IPEndPoint iap = new IPEndPoint(IPAddress.Any, 9050); m_server.Bind(iap); m_server.Listen(5); m_server.BeginAccept(new AsyncCallback(AcceptConnection), null); } void AcceptConnection(IAsyncResult iar) { Socket client = m_server.EndAccept(iar); conStatus.Text = “Connect to: “ + client.RemoteEndPoint.ToString(); string stringData = “Welcome to my server”; byte[] message1 = Encoding.ASCII.GetBytes(stringData); client.BeginSend(message1, 0, message1.Length, SocketFlags.None, new AsyncCallback(SendData), client); } void SendData(IAsyncResult iar) { Socket client = (Socket)iar.AsyncState; int sent = client.EndSend(iar); client.BeginReceive(data, 0, size, SocketFlags.None, new AsyncCallback(SendData), client); } void ReceiveData(IAsyncResult iar) { Socket client = (Socket)iar.AsyncState; int recv = client.EndReceive(iar); if(recv == 0) { client.Close(); conStatus.Text = “Waiting for client...”; m_server.BeginAccept(new AsyncCallback(AcceptConnection), null); return; } string receiveData = Encoding.ASCII.GetString(data, 0, recv); results.Items.Add(receivedData); byte[] message = Encoding.ASCII.GetBytes(receiveData); client.BeginSend(message, 0, message.Length, SocketFlags.None, new AsyncCallback(SendData), client); } 4. Sử dụng thread _ Server: class ThreadedTcpSrvr { private TcpListener listener; public ThreadedTcpSrvr() { listener = new new TcpListener(9050); listener.Start(); Console.WriteLine(“Waiting for clients”); while(true) { while (!listener.Pending()) Thread.Sleep(1000); ConnectionThread newclient = new ConnectionThread(); Newclient.threadListener = this.listener; Thread newthread = new Thread(new ThreadStart(newclient.HandleConnection)); newthread.Start(); } } public static void Main() { ThreadedTcpSrvr server = new ThreadedTcpSrvr(); } } class ConnectionThread { private static int connections = 0; public TcpListener threadListener; public void HandleConnection() { int recv; byte[] data = new byte[1024]; TcpClient client = threadListener.AcceptTcpClient(); NetworkStream ns = client.GetStream(); connections++; Console.WriteLine(“New client accepted: [0] active connections”, connections); string welcome = “Welcome to my server”; data = Encoding.ASCII.GetBytes(welcome); ns.Write(data, 0, data.Length); while(true) { data = new byte[1024]; recv = ns.Read(data, 0, data.Length); if (recv == 0) break; ns.Write(data, 0, recv); } ns.Close(); client.Close(); connections--; Console.WriteLine(“Client disconnected: [0] active connections”, connections); } }

Các file đính kèm theo tài liệu này:

  • pdftailieu.pdf
Tài liệu liên quan