一.实验目的
了解Socket通信原理,实现以太网双节点通讯。
二.实验要求
了解三次握手的原理。
编程实现以太网双节点通讯。
了解端口与IP的关系和差异。
三.实验原理
基于TCP(面向连接)的socket编程,分为客户端和服务器端。
客户端的流程如下:
(1)创建套接字(socket)
(2)向服务器发出连接请求(connect)
(3)和服务器端进行通信(send/recv)
(4)关闭套接字
服务器端的流程如下:
(1)创建套接字(socket)
(2)将套接字绑定到一个本地地址和端口上(bind)
(3)将套接字设为监听模式,准备接收客户端请求(listen)
(4)等待客户请求到来;当请求到来后,接受连接请求,返回一个新的对应于此次连接的套接字(accept)
(5)用返回的套接字和客户端进行通信(send/recv)
(6)返回,等待另一个客户请求。
(7)关闭套接字。
四.实验步骤
编程实现过程如下:
a、 在MFC中建立对话框工程如下图所示;在Project name中填入要建工程的名字:TCP
b、 下一步选择Dialog based如下图:
c、 点击Finish即可
d、 完成以上步骤之后从工具栏中添加控件到对话框中,如下图。
e、 IP后的编辑框ID为IDC_IP,Port后的编辑框设为IDC_Port, Start_Server按钮ID设为IDC_Start_Server,CloseServer按钮设为IDC_Close_Server。SendMsg下的编辑框ID设为IDC_Send_MSG,Receive下的是一个列表框(CtrlList),Send按钮ID设为IDC_Send。Server选项ID为Radio1,Client选项ID为Radio2。
f、 添加成员变量。如图
g、 添加函数。
(a) 分别双击Server和Client选项,添加函数名为OnRadio1和OnRadio1,添加代码如下
void CTCPDlg::OnRadio2()
{
// TODO: Add your control notification handler code here
m_WorkType=1;
SetDlgItemText(IDC_Start_Server,"ConnectServer");
SetDlgItemText(IDC_Close_Server,"ShutDown");
GetDlgItem(IDC_IP)->EnableWindow(TRUE);
}
void CTCPDlg::OnRadio1()
{
// TODO: Add your control notification handler code here
m_WorkType=0;
SetDlgItemText(IDC_Start_Server,"StartServer");
SetDlgItemText(IDC_Close_Server,"CloseServer");
GetDlgItem(IDC_IP)->EnableWindow(FALSE);
m_IP=GetIP();
UpdateData(FALSE);
}
(b) 分别双击StartServer和CloseServer按钮,添加函数如下:
void CTCPDlg::OnStartServer()
{
// TODO: Add your control notification handler code here
WSADATA wsaData;
if(WSAStartup(MAKEWORD(2,2),&wsaData)!=0){
MessageBox("Failed to load Winsock!");
return;
}
if(m_WorkType==0)
{
if(!m_bStarting)
{
UpdateData(TRUE);
if(m_Port==0)
{
MessageBox("Please input the Port!");
return;
}
if(sockListen.Create(m_Port,SOCK_STREAM,m_IP))
{
m_State="Service Start...";
sockListen.Bind(m_Port,m_IP);
sockListen.Listen(5);
WSAAsyncSelect(sockListen,GetSafeHwnd(),ACCEPT_EVENT,FD_ACCEPT|FD_READ|FD_CLOSE);
m_bStarting=TRUE;
}
else
{
LPVOID lpMsgBuf;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0, NULL );
MessageBox((LPCTSTR)lpMsgBuf,_T("Error"),MB_OK|MB_ICONINFORMATION);
}
}
}
else if(m_WorkType==1)
{
if(!m_bConnected)
{
CString str;
UpdateData(TRUE);
if(m_Port==0||m_IP=="")
{
MessageBox("Please input IP and Port!");
return;
}
m_TCPsock.Create();
if(BOOL fC=m_TCPsock.Connect(m_IP,m_Port))
{
WSAAsyncSelect(m_TCPsock,GetSafeHwnd(),ACCEPT_EVENT,FD_READ|FD_CLOSE);
m_bConnected=TRUE;
m_State="Connect with Server";
}
else
{
m_TCPsock.Close();
m_State="Connect failed";
}
}
}
else
MessageBox("Please choose the type of the machine!");
UpdateData(FALSE);
}
void CTCPDlg::OnCloseServer()
{
// TODO: Add your control notification handler code here
if(m_bConnected)
{
m_TCPsock.ShutDown(2);
m_TCPsock.Close();
m_bConnected=FALSE;
WSAAsyncSelect(m_TCPsock,GetSafeHwnd(),0,0);
}
if(m_WorkType==1)
{
m_State="Service Stop!";
sockListen.Close();
}
if(m_WorkType==1)
m_State="Shut down!";
UpdateData(FALSE);
}
(c) 双击Send按钮,添加函数如下
void CTCPDlg::OnSend()
{
// TODO: Add your control notification handler code here
UpdateData(TRUE);
int iSend=m_TCPsock.Send(m_SendMsg,40,0);
TRACE("sent %d byte\n",iSend);
}
h、 进行编译,运行程序。客户端选择Client选项,服务端选择Server选项,先开启服务端设置好Port,再开启客户端。从客户端IP和port上输入服务端IP以连接实现通讯。
i、 通讯结束后,先点击客户端的ShutDown再关闭程序,最后关闭服务端。