TCP流式套接字的編程步驟:
服務器端程序:
1、加載套接字庫
2、創建套接字(socket)。
3、將套接字綁定到一個本地地址和端口上(bind)。
4、將套接字設為監聽模式,準備接收客戶請求(listen)。
5、等待客戶請求到來;當請求到來后,接受連接請求,返回一個新的對應于此次連接的套接字(accept)。
6、用返回的套接字和客戶端進行通信(send/recv)。
7、返回,等待另一客戶請求。
8、關閉套接字。
客戶端程序:
1、加載套接字庫
2、創建套接字(socket)。
3、向服務器發出連接請求(connect)。
4、和服務器端進行通信(send/recv)。
5、關閉套接字。
以上是Socket網絡編程基本步驟,必須熟知!
更多linux服務器開發高階知識Linux,Nginx,ZeroMQ,MySQL,redis,fastdfs,MongoDB,ZK,流媒體,CDN,P2P,K8S,Docker,TCP/IP,協程,DPDK等等學習資料請后臺私信【學習資料】獲取
下面就講講如何在MFC中進行Socket 編程。
首先新建一個基于對話框的工程取名為sFile,此工程作為服務器端。刪除對話框中的中間代碼和OK按鈕。在新建的對話框中添加一個List Box控件作為顯示窗口,并添加一個Edit control控件作為輸入窗口,然后增加一個發送按鈕:IDC_BtnSend。在List Box控件上右鍵點擊選擇——添加變量,將變量定義為控件類型并取名為m_listwords。
然后再建一個客戶端對話框取名為cFile,刪除對話框中的中間代碼和OK按鈕。在新建的對話框中添加一個List Box控件作為顯示窗口,并添加一個Edit control控件作為輸入窗口,然后增加兩個按鈕:一個為發送按鈕IDC_BtnSend,另一個為連接按鈕IDC_BtnConnect。在List Box控件上右鍵點擊選擇——添加變量,將變量定義為控件類型并取名為m_listwords。另外需要添加一個IP Adress control控件,然后按添加變量的方法將其關聯為控件類型,并取名為m_ip。
服務器端具體步驟如下:
1、 在sFileDlg.h中添加public:void update(CString s);
private: CEdit* send_edit;
void CString2Char(CString str, char ch[]); //此函數為字符格式轉換函數,后面會講到
2、 新建兩個socket套接字: SOCKET listen_sock;
SOCKET sock;
在sFileDlg.h中添加 CString IP; //定義為全局變量
并聲明線程函數 UINT server_thd(LPVOID p);
3、在OnInitDialog()函數中添加:
send_edit = (CEdit *)GetDlgItem(IDC_EDIT1);
send_edit->SetFocus();
char name[128];
hostent* pHost;
gethostname(name, 128);//獲得主機名
pHost = gethostbyname(name);//獲得主機結構
IP = .NET_ntoa(*(in_addr *)pHost->h_addr);
update(_T("本服務器IP地址:") + IP);
AfxBeginThread(server_thd, NULL);//創建線程
4、添加函數update():
void CSFileDlg::update(CString s)
{
m_listwords.AddString(s);
}
5、添加線程函數server_thd():
UINT server_thd(LPVOID p)//線程要調用的函數
{
WSADATA wsaData;
WORD wVersion;
wVersion = MAKEWORD(2, 2);
WSAStartup(wVersion, &wsaData);
// WSAStartup(0x0202, &wsaData);
SOCKADDR_IN local_addr;
SOCKADDR_IN client_addr;
int iaddrSize = sizeof(SOCKADDR_IN);
int res;
char msg[1024];
CsFileDlg * dlg = (CsFileDlg *)AfxGetApp()->GetMainWnd();
char ch_ip[20];
CString2Char(IP, ch_ip);//注意!這里調用了字符格式轉換函數,此函數功能:CString類型轉換為Char類型,實現代碼在后面添加
//local_addr.sin_addr.s_addr = htonl(INADDR_ANY);//獲取任意IP地址
local_addr.sin_addr.s_addr=inet_addr(ch_ip);
local_addr.sin_family = AF_INET;
local_addr.sin_port = htons(8888);
if ((listen_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET)//創建套接字
{
dlg->update(_T("創建監聽失敗"));
}
if (bind(listen_sock, (struct sockaddr*) &local_addr, sizeof(SOCKADDR_IN)))//綁定套接字
{
dlg->update(_T("綁定錯誤"));
}
listen(listen_sock, 1);
if ((sock = accept(listen_sock, (struct sockaddr *)&client_addr, &iaddrSize)) == INVALID_SOCKET)//接收套接字
{
dlg->update(_T("accept 失敗"));
}
else
{
CString port;
port.Format(_T("%d"), int(ntohs(client_addr.sin_port)));
dlg->update(_T("已連接客戶端:") + CString(inet_ntoa(client_addr.sin_addr)) + " 端口:" + port);
}
////////////接收數據
while (1)
{
if ((res = recv(sock, msg, 1024, 0)) == -1)
{
dlg->update(_T("失去客戶端的連接"));
break;
}
else
{
msg[res] = '';
dlg->update(_T("client:") + CString(msg));
}
}
return 0;
}
6、添加按鈕發送函數(在對話框中右鍵點擊剛添加的按鈕彈出菜單,然后選擇添加事件響應函數這一欄,將函數命名為OnSending)
事件響應代碼如下:
void CsFileDlg::OnSending()
{
// TODO: Add your control notification handler code here
CString s;
char msg[1024];
send_edit->GetWindowTextW(s);
CString2Char(s, msg); // 注意!這里調用了字符格式轉換函數,此函數功能:CString類型轉換為Char類型,實現代碼后面添加
if (send(sock, msg, strlen(msg), 0) == SOCKET_ERROR)
{
show_edit->ReplaceSel(_T("發送失敗"));
m_listwords.SetWindowTextW(_T("發送失敗"));
}
else if (s == "")
{
MessageBox(_T("請輸入信息"));
}
else
{
s = msg;
//update(s);//消息上屏,清空輸入,并重獲焦點
//show_edit->ReplaceSel(_T("server:") + s);//消息上屏,清空輸入,并重獲焦點
m_listwords.AddString(_T("server:") + s);
send_edit->SetWindowText(_T(""));
m_listwords.SetFocus();
}
}
void CString2Char(CString str, char ch[])//此函數就是字符轉換函數的實現代碼
{
int i;
char *tmpch;
int wLen = WideCharToMultiByte(CP_ACP, 0, str, -1, NULL, 0, NULL, NULL);//得到Char的長度
tmpch = new char[wLen + 1]; //分配變量的地址大小
WideCharToMultiByte(CP_ACP, 0, str, -1, tmpch, wLen, NULL, NULL); //將CString轉換成char*
for (i = 0; tmpch[i] != ''; i++) ch[i] = tmpch[i];
ch[i] = '';
}
客戶端具體步驟如下:
1、 在cFileDlg.h中添加 public:void update(CString s);
private: CEdit* send_edit;
void CString2Char(CString str, char ch[]);
2、 新建一個socket套接字: SOCKET sock;
聲明線程函數 UINT server_thd(LPVOID p);
3、在OnInitDialog()函數中添加:
send_edit = (CEdit *)GetDlgItem(IDC_EDIT1);
4、添加函數update():
void CcFileDlg::update(CString s)
{
m_listwords.AddString(s);
}
5、添加線程函數server_thd():
UINT recv_thd(LPVOID p)
{
int res;
char msg[1024];
//CString s;
CcFileDlg * dlg = (CcFileDlg *)AfxGetApp()->GetMainWnd();
////////////接收數據
while (1)
{
if ((res = recv(sock, msg, 1024, 0)) == -1)//接收服務器的數據
{
dlg->update(_T("失去連接"));
break;
}
else
{
msg[res] = '';
dlg->update(_T("server:") + CString(msg));
}
}
//closesocket(sock);
return 0;
}
6、添加連接按鈕事件響應函數:在連接按鈕上右鍵選擇添加事件響應這一欄,將函數取名OnConnecting,如下
void CcFileDlg::OnConnecting()
{
// TODO: Add your control notification handler code here
WSADATA wsaData;
SOCKADDR_IN server_addr;
memset(&server_addr, 0, sizeof(server_addr));
WORD wVersion;
wVersion = MAKEWORD(2, 2);
WSAStartup(wVersion, &wsaData);
// WSAStartup(0x0202, &wsaData);
if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET)
{
update(_T("create socket error !!!"));
}
//CString ip;
//ip_edit->GetWindowTextW(ip);//取得服務器的IP地址
//server_addr.sin_addr.s_addr = inet_addr((LPSTR)(LPCSTR)ip.GetBuffer());
BYTE nArrIP[4];
m_ip.GetAddress(nArrIP[0], nArrIP[1], nArrIP[2], nArrIP[3]);
CString str;
str.Format(_T("%d.%d.%d.%d"), nArrIP[0], nArrIP[1], nArrIP[2], nArrIP[3]);
ip_edit->SetWindowTextW(str);
char cp[50];
CString2Char(str, cp);
server_addr.sin_addr.s_addr = inet_addr(cp);
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(8888);
bind(sock, (SOCKADDR*)&server_addr, sizeof(SOCKADDR));
if (connect(sock, (struct sockaddr *) &server_addr, sizeof(SOCKADDR_IN)) == SOCKET_ERROR)
{
update(_T("連接失敗"));
}
else
{
//show_edit->SetWindowText(_T(""));
update(_T("連接成功"));
btnconn->EnableWindow(FALSE);//按鈕變灰
AfxBeginThread(recv_thd, NULL);
}
}
7、添加發送按鈕事件響應函數:在發送按鈕上右鍵選擇添加事件響應這一欄,將函數取名OnSending,如下
void CcFileDlg::OnSending()
{
// TODO: Add your control notification handler code here
//CString s;
//char * msg;
//send_edit->GetWindowTextW(s);
//msg = (LPSTR)(LPCTSTR)s;
//CString2Char(s, msg);
CString s;
char msg[1024];
send_edit->GetWindowTextW(s);
CString2Char(s, msg);
if (send(sock, msg, strlen(msg), 0) == SOCKET_ERROR)
{
update(_T("發送失敗"));
}
else if (s == "")
{
MessageBox(_T("請輸入信息"));
}
else
{
s = msg;
update(_T("client:") + s);//消息上屏,清空輸入,并重獲焦點
send_edit->SetWindowText(_T(""));
send_edit->SetFocus();
}
/*CString s;
char * msg;
send_edit->GetWindowText(s);
msg = (char*)s.GetBuffer(s.GetLength());
if (send(sock, msg, strlen(msg), 0) == SOCKET_ERROR)
{
show_edit->ReplaceSel(_T("發送失敗/r/n"));
}
else if (s == "")
{
MessageBox(_T("請輸入信息"));
}
else
{
show_edit->ReplaceSel(_T("client:") + s + "/r/n");//消息上屏,清空輸入,并重獲焦點
send_edit->SetWindowText(_T(""));
send_edit->SetFocus();
}*/
}
8、最后添加字符格式轉換函數
/*
* 函數名: CString2Char
* 參數1: CString str 待轉換字符串
* 參數2: char ch[] 轉換后將要儲存的位置
* 將Unicode下的CString轉換為char*
*/
void CString2Char(CString str, char ch[])
{
int i;
char *tmpch;
int wLen = WideCharToMultiByte(CP_ACP, 0, str, -1, NULL, 0, NULL, NULL);//得到Char的長度
tmpch = new char[wLen + 1]; //分配變量的地址大小
WideCharToMultiByte(CP_ACP, 0, str, -1, tmpch, wLen, NULL, NULL); //將CString轉換成char*
for (i = 0; tmpch[i] != ''; i++) ch[i] = tmpch[i];
ch[i] = '';
}
實驗結果如圖: