Trước đây tôi chủ yếu sử dụng UDP để giao tiếp với máy chủ, nhưng dự án mới này yêu cầu sử dụng TCP để thực hiện kết nối.
Tôi đã tạo một lớp TCP client và cũng xây dựng một server để kiểm thử. Dưới đây là mã nguồn của lớp TCP client:
Header file:
1 // CeTcpClient.h: interface for the CCeTcpClient class.
2 //
3 //////////////////////////////////////////////////////////////////////
4
5 #if !defined(AFX_CETCPCLIENT_H__B7856B99_69E7_4868_9BA3_96152245C65E__INCLUDED_)
6 #define AFX_CETCPCLIENT_H__B7856B99_69E7_4868_9BA3_96152245C65E__INCLUDED_
7
8 #if _MSC_VER > 1000
9 #pragma once
10 #endif // _MSC_VER > 1000
11
12 #include <winsock.h>
13
14 // Kết nối ngắt kết nối
15 typedef void (CALLBACK *ONDISCONNECT)(CWnd *);
16 // Nhận dữ liệu
17 typedef void (CALLBACK *ONREAD)(CWnd *,const char *,int);
18 // Lỗi socket
19 typedef void (CALLBACK *ONERROR)(CWnd *,int);
20
21 class CCeTcpClient
22 {
23 public:
24 CCeTcpClient();
25 virtual ~CCeTcpClient();
26
27 public:
28 // Địa chỉ IP của máy chủ
29 CString m_csRemoteHost;
30 // Cổng máy chủ
31 int m_iPort;
32
33 // Ngắt kết nối
34 ONDISCONNECT OnDisConnect;
35 // Nhận dữ liệu
36 ONREAD OnRead;
37 // Lỗi xảy ra
38 ONERROR OnError;
39
40 public:
41 // Mở socket
42 bool Open(CWnd *pWnd);
43 // Đóng socket
44 bool Close();
45 // Kết nối đến máy chủ
46 bool Connect();
47 // Gửi dữ liệu đến máy chủ
48 bool SendData(const char *pcBuffer,int iLen);
49
50 private:
51 // Handle socket
52 SOCKET m_socket;
53 // Sự kiện thoát luồng giao tiếp
54 HANDLE m_hExitThreadEvent;
55 // Luồng giao tiếp
56 HANDLE m_hTcpThreadHandle;
57 // Handle cửa sổ cha
58 CWnd *m_pOwnerWnd;
59
60 private:
61 // Luồng xử lý socket
62 static DWORD SocketControlThread(LPVOID lparam);
63 };
64
65 #endif // !defined(AFX_TCPCLIENT_CE_H__B7856B99_69E7_4868_9BA3_96152245C65E__INCLUDED_)
Source file:
1 // CeTcpClient.cpp: implementation of the CCeTcpClient class.
2 // Leo.Zheng 2012.04.09
3 //////////////////////////////////////////////////////////////////////
4
5 #include "stdafx.h"
6 #include "TCPClient.h"
7 #include "CeTcpClient.h"
8
9 #ifdef _DEBUG
10 #undef THIS_FILE
11 static char THIS_FILE[]=__FILE__;
12 #define new DEBUG_NEW
13 #endif
14
15 //////////////////////////////////////////////////////////////////////
16 // Construction/Destruction
17 //////////////////////////////////////////////////////////////////////
18 CCeTcpClient::CCeTcpClient()
19 {
20 int iInitWSA = 0;
21 // Khởi tạo Socket, phiên bản: 1.1
22 WSADATA wsd;
23
24 iInitWSA = WSAStartup(MAKEWORD(1,1),&wsd);
25 if(0 != iInitWSA)
26 {
27 RETAILMSG(1,(L"[CCeTcpClient::CCeTcpClient]WSA start up failed: %d\r\n",iInitWSA));
28 }
29 // Tạo sự kiện thoát luồng
30 m_hExitThreadEvent = CreateEvent(NULL,FALSE,FALSE,NULL);
31 }
32
33 CCeTcpClient::~CCeTcpClient()
34 {
35 // Giải phóng Socket
36 WSACleanup();
37 // Đóng sự kiện thoát
38 CloseHandle(m_hExitThreadEvent);
39 }
40
41 /*
42 * Chức năng: Mở Socket
43 * Tham số: pWnd dùng để xác định handle cửa sổ cha
44 * Trả về: TRUE thành công; FALSE thất bại
45 */
46 bool CCeTcpClient::Open(CWnd *pWnd)
47 {
48 ResetEvent(m_hExitThreadEvent);
49 m_pOwnerWnd = pWnd;
50
51 // Tạo socket TCP
52 m_socket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
53 if(SOCKET_ERROR == m_socket)
54 {
55 return FALSE;
56 }
57
58 // Tạo luồng giao tiếp
59 m_hTcpThreadHandle = CreateThread(NULL,0,SocketControlThread,this,0,NULL);
60 if(NULL == m_hTcpThreadHandle)
61 {
62 closesocket(m_socket);
63 return FALSE;
64 }
65
66 return TRUE;
67 }
68
69 /*
70 * Chức năng: Đóng Socket
71 * Tham số: Không có
72 * Trả về: TRUE thành công; FALSE thất bại
73 */
74 bool CCeTcpClient::Close()
75 {
76 // Thiết lập sự kiện kết thúc luồng
77 SetEvent(m_hExitThreadEvent);
78 Sleep(1000);
79
80 // Đóng Socket
81 int iError = closesocket(m_socket);
82 if(SOCKET_ERROR == iError)
83 {
84 return FALSE;
85 }
86
87 return TRUE;
88 }
89
90 /*
91 * Chức năng: Kết nối đến máy chủ TCP
92 * Tham số: Không có
93 * Trả về: TRUE thành công; FALSE thất bại
94 */
95 bool CCeTcpClient::Connect()
96 {
97 struct sockaddr_in addr;
98 int iError = 0;
99 char cAnsiRemoteHost[255 + 1];
100
101 addr.sin_family = AF_INET;
102 addr.sin_port = htons(m_iPort);
103 ZeroMemory(cAnsiRemoteHost,sizeof(char) * (255 + 1));
104 WideCharToMultiByte(CP_ACP,WC_COMPOSITECHECK,m_csRemoteHost,wcslen(m_csRemoteHost),cAnsiRemoteHost,wcslen(m_csRemoteHost),NULL,NULL);
105
106 addr.sin_addr.s_addr = inet_addr(cAnsiRemoteHost);
107 // Sử dụng phương thức kết nối đồng bộ, connect trả về thành công hoặc thất bại
108 iError = connect(m_socket,(struct sockaddr *)&addr,sizeof(addr));
109 if(SOCKET_ERROR == iError)
110 {
111 return FALSE;
112 }
113 // Thiết lập chế độ không đồng bộ
114 DWORD dwMode = 1;
115 ioctlsocket(m_socket,FIONBIO,&dwMode);
116
117 return TRUE;
118 }
119
120 /*
121 * Chức năng: Gửi dữ liệu đến máy chủ
122 * Tham số: buf dữ liệu cần gửi
123 len độ dài dữ liệu cần gửi
124 * Trả về: TRUE thành công; FALSE thất bại
125 */
126 bool CCeTcpClient::SendData(const char *pcBuffer,int iLen)
127 {
128 int iBytes = 0;
129 int iSendBytes = 0;
130
131 while(iSendBytes < iLen)
132 {
133 iBytes = send(m_socket,pcBuffer + iSendBytes,iLen - iSendBytes,0);
134 if(SOCKET_ERROR == iBytes)
135 {
136 int iErrorCode = WSAGetLastError();
137 OnError(m_pOwnerWnd,iErrorCode);
138 OnDisConnect(m_pOwnerWnd);
139 // Đóng Socket
140 Close();
141 return FALSE;
142 }
143
144 iSendBytes = iSendBytes + iBytes;
145
146 if(iSendBytes < iLen)
147 {
148 Sleep(1000);
149 }
150 }
151 return TRUE;
152 }
153
154 /*
155 * Chức năng: Luồng này dùng để giám sát các sự kiện giao tiếp TCP
156 ví dụ: khi nhận được dữ liệu, kết nối bị ngắt hoặc lỗi trong quá trình giao tiếp
157 * Tham số: lparam: có thể truyền tài nguyên cần thiết vào luồng thông qua tham số này
158 ở đây chúng ta truyền con trỏ đến đối tượng lớp CCeTcpClient
159 * Trả về: Không có ý nghĩa, trả về 0.
160 */
161 DWORD CCeTcpClient::SocketControlThread(LPVOID lparam)
162 {
163 CCeTcpClient *pSocket = NULL;
164 fd_set fdRead;
165 TIMEVAL aTime;
166 int iRet = 0;
167
168 // Lấy con trỏ đến đối tượng CCeTcpClient
169 pSocket = (CCeTcpClient *)lparam;
170 // Thiết lập thời gian chờ sự kiện
171 aTime.tv_sec = 1;
172 aTime.tv_usec = 0;
173
174 while(TRUE)
175 {
176 // Sự kiện thoát, kết thúc luồng
177 if(WAIT_OBJECT_0 == WaitForSingleObject(pSocket->m_hExitThreadEvent,0))
178 {
179 break;
180 }
181 // Đặt fdRead rỗng
182 FD_ZERO(&fdRead);
183 // Thiết lập sự kiện đọc cho socket
184 FD_SET(pSocket->m_socket,&fdRead);
185
186 // Kiểm tra xem có sự kiện đọc không
187 iRet = select(0,&fdRead,NULL,NULL,&aTime);
188 if(SOCKET_ERROR == iRet)
189 {
190 pSocket->OnError(pSocket->m_pOwnerWnd,1);
191 pSocket->OnDisConnect(pSocket->m_pOwnerWnd);
192 // Đóng socket client
193 closesocket(pSocket->m_socket);
194 break;
195 }
196
197 if(iRet > 0)
198 {
199 if(FD_ISSET(pSocket->m_socket,&fdRead))
200 {
201 char cRecvBuffer[1024 + 1];
202 int iRecvLength = 0;
203
204 ZeroMemory(cRecvBuffer,sizeof(char) * (1024 + 1));
205 // Nhận dữ liệu
206 iRecvLength = recv(pSocket->m_socket,cRecvBuffer,1024,0);
207 if(SOCKET_ERROR == iRecvLength)
208 {
209 int iError = WSAGetLastError();
210 pSocket->OnError(pSocket->m_pOwnerWnd,iError);
211 pSocket->OnDisConnect(pSocket->m_pOwnerWnd);
212 // Đóng socket client
213 closesocket(pSocket->m_socket);
214 break;
215 }
216 else if(0 == iRecvLength)
217 {
218 pSocket->OnDisConnect(pSocket->m_pOwnerWnd);
219 // Đóng socket client
220 closesocket(pSocket->m_socket);
221 break;
222 }
223 else
224 {
225 // Kích hoạt sự kiện nhận dữ liệu
226 pSocket->OnRead(pSocket->m_pOwnerWnd,cRecvBuffer,iRecvLength);
227 }
228 }
229 }
230 }
231
232 return 0;
233 }
Code kiểm thử:
1 BOOL CTCPClientDlg::OnInitDialog()
2 {
3 //m_bFullScreen = FALSE;
4 CDialog::OnInitDialog();
5
6 // Set the icon for this dialog. The framework does this automatically
7 // when the application's main window is not a dialog
8 SetIcon(m_hIcon, TRUE); // Set big icon
9 SetIcon(m_hIcon, FALSE); // Set small icon
10
11 CenterWindow(GetDesktopWindow()); // center to the hpc screen
12
13 // Khởi tạo giá trị đầu vào
14 m_csRemoteHost = GetLocalIP(); // Trong môi trường mô phỏng: client và server chạy trên cùng máy ảo
15 m_iRemotePort = 5000;
16 UpdateData(FALSE);
17 return TRUE; // return TRUE unless you set the focus to a control
18 }
19
20
21 // Ngắt kết nối
22 void CALLBACK CTCPClientDlg::OnDisConnect(CWnd *pWnd)
23 {
24 CTCPClientDlg *pDlg = (CTCPClientDlg *)pWnd;
25
26 CStatic *pStatus = (CStatic *)pDlg->GetDlgItem(IDC_LBLCONNSTATUS);
27 ASSERT(NULL != pStatus);
28 pStatus->SetWindowText(L"Kết nối bị ngắt");
29
30 CButton *pBtnConn = (CButton *)pDlg->GetDlgItem(IDC_BTNCONN);
31 CButton *pBtnDisConn = (CButton *)pDlg->GetDlgItem(IDC_BTNDISCONN);
32 CButton *pBtnSendData = (CButton *)pDlg->GetDlgItem(IDC_BTNSENDDATA);
33 ASSERT(NULL != pBtnConn);
34 ASSERT(NULL != pBtnDisConn);
35 ASSERT(NULL != pBtnSendData);
36 pBtnConn->EnableWindow(TRUE);
37 pBtnDisConn->EnableWindow(FALSE);
38 pBtnSendData->EnableWindow(FALSE);
39 }
40
41 // Hiển thị dữ liệu nhận được
42 void CALLBACK CTCPClientDlg::OnRead(CWnd *pWnd,const char *pcBuffer,int iLen)
43 {
44 CTCPClientDlg *pDlg = (CTCPClientDlg *)pWnd;
45 CString csRecvStr = pcBuffer;
46 CEdit *pEdtRecv = (CEdit *)pDlg->GetDlgItem(IDC_EDTRECV);
47
48 ASSERT(NULL != pEdtRecv);
49 // Hiển thị dữ liệu nhận được lên ô văn bản
50 pEdtRecv->SetWindowText(csRecvStr);
51 }
52
53 // Thông báo lỗi socket
54 void CALLBACK CTCPClientDlg::OnError(CWnd *pWnd,int iErrorCode)
55 {
56 CString csErrorInfo;
57
58 csErrorInfo.Format(L"%s:%d",L"Lỗi socket client",iErrorCode);
59 AfxMessageBox(csErrorInfo);
60 }
61
62 // Kết nối đến máy chủ
63 void CTCPClientDlg::OnBtnconn()
64 {
65 CStatic *pStatus = (CStatic *)GetDlgItem(IDC_LBLCONNSTATUS);
66 CButton *pBtnConn = (CButton *)GetDlgItem(IDC_BTNCONN);
67 CButton *pBtnDisConn = (CButton *)GetDlgItem(IDC_BTNDISCONN);
68 CButton *pBtnSendData = (CButton *)GetDlgItem(IDC_BTNSENDDATA);
69
70 ASSERT(NULL != pStatus);
71 ASSERT(NULL != pBtnConn);
72 ASSERT(NULL != pBtnDisConn);
73 ASSERT(NULL != pBtnSendData);
74
75 UpdateData(TRUE);
76 // Thiết lập thuộc tính m_tcpClient
77 m_tcpClient.m_csRemoteHost = m_csRemoteHost;
78 m_tcpClient.m_iPort = m_iRemotePort;
79 m_tcpClient.OnDisConnect = OnDisConnect;
80 m_tcpClient.OnRead = OnRead;
81 m_tcpClient.OnError = OnError;
82 // Mở socket client
83 m_tcpClient.Open(this);
84
85 // Kết nối đến máy chủ
86 if(m_tcpClient.Connect())
87 {
88 pStatus->SetWindowText(L"Đã kết nối");
89 UpdateData(FALSE);
90 }
91 else
92 {
93 AfxMessageBox(L"Kết nối thất bại");
94 pStatus->SetWindowText(L"Ngắt kết nối");
95 return;
96 }
97
98 pBtnConn->EnableWindow(FALSE);
99 pBtnDisConn->EnableWindow(TRUE);
100 pBtnSendData->EnableWindow(TRUE);
101 }
102
103 // Ngắt kết nối
104 void CTCPClientDlg::OnBtndisconn()
105 {
106 if(m_tcpClient.Close())
107 {
108 CStatic *pStatus = (CStatic *)GetDlgItem(IDC_LBLCONNSTATUS);
109 CButton *pBtnConn =(CButton*)GetDlgItem(IDC_BTNCONN);
110 CButton *pBtnDisConn = (CButton*)GetDlgItem(IDC_BTNDISCONN);
111 CButton *pBtnSendData = (CButton*)GetDlgItem(IDC_BTNSENDDATA);
112
113 ASSERT(NULL != pStatus);
114 ASSERT(NULL != pBtnConn);
115 ASSERT(NULL != pBtnDisConn);
116 ASSERT(NULL != pBtnSendData);
117
118 pStatus->SetWindowText(L"Ngắt kết nối");
119 pBtnConn->EnableWindow(TRUE);
120 pBtnDisConn->EnableWindow(FALSE);
121 pBtnSendData->EnableWindow(FALSE);
122 }
123 else
124 {
125 AfxMessageBox(L"Ngắt kết nối thất bại");
126 }
127 }
128
129 // Gửi dữ liệu
130 void CTCPClientDlg::OnBtnsenddata()
131 {
132 char *pcSendBuf;
133 int iSendLength = 0;
134 UpdateData(TRUE);
135
136 iSendLength = m_csSendData.GetLength();
137 pcSendBuf = new char[iSendLength * 2];
138 ASSERT(NULL != pcSendBuf);
139 wcstombs(pcSendBuf,m_csSendData,iSendLength);
140 if(!m_tcpClient.SendData(pcSendBuf,iSendLength))
141 {
142 AfxMessageBox(L"Gửi thất bại");
143 }
144 delete[] pcSendBuf;
145 pcSendBuf = NULL;
146 }