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...
40 trang |
Chia sẻ: Khủng Long | Lượt xem: 1106 | Lượt tải: 0
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:
- tailieu.pdf