Socket程序设计
目录
一、获取计算机的名称和IP地址 (2)
1.1、实验目的 (2)
1.2、实验内容 (2)
1.3、实验过程 (2)
1.4、实验结果及其分析 (2)
1.5、关键代码 (2)
二、基于UDP模式的点对点聊天 (6)
2.1、实验目的 (6)
2.2、实验内容 (6)
2.3、实验过程 (6)
2.4、实验结果及其分析 (6)
2.5、关键代码 (7)
三、基于TCP 模式的通信 .................................................................. 1错误!未定义书签。
3.1、实验目的.............................................................................. 1错误!未定义书签。
3.2、实验内容.............................................................................. 1错误!未定义书签。
3.3、实验过程.............................................................................. 1错误!未定义书签。
3.4、实验结果及其分析 ............................................................. 1错误!未定义书签。
3.5、关键代码 ............................................................................. 1错误!未定义书签。
四、FTP客户端实验 (17)
4.1、实验目的 (17)
4.2、实验内容............................................................................... 错误!未定义书签。7
4.3、实验过程............................................................................... 错误!未定义书签。7
4.4、实验结果及其分析 .............................................................. 错误!未定义书签。7
4.5、关键代码 (20)
五、简单的浏览器 (28)
5.1、实验目的 (28)
5.2、实验内容 (28)
5.3、实验过程 (28)
5.4、实验结果及其分析 (28)
5.5、关键代码 (29)
六、考勤管理系统................................................................................ 3错误!未定义书签。
6.1、实验目的.............................................................................. 3错误!未定义书签。
6.2、实验内容.............................................................................. 3错误!未定义书签。
6.3、实验过程.............................................................................. 3错误!未定义书签。
6.4、实验结果及其分析 (37)
6.5、关键代码 (40)
七、实验中遇到的问题及改进 (55)
八、小节 (56)
九、参考文献 (57)
获取计算机名和IP地址
1.1实验目的
?熟悉VC++6.0的使用环境。
?掌握最基本的基于控制台、对话框、单文档及多文档界面的项目创建方法。
?掌握项目中的文件构成及作用,学会在程序文件中增加功能代码的方法。
?掌握程序的生成和调试方法。
1.2实验内容
编写程序实现:获取计算机的名称和IP地址。
1.3实验过程
利用Winsock函数获取主机名和IP地址,只需通过gethostname和gethostbyname两个函数实现:
☉载入Winsock动态库
☉通过gethostname获取计算机名,并保存在变量sHostName中,然后调用GetIPAddress获得IP地址
☉在对话框初始化的时候,调用以上函数,可获得计算机名和IP地址1.4实验结果及分析
1.5关键代码
①载入Winsock动态库,代码如下:
int CB09010701_IPAddressDlg::StartUp()
{
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD(2,0);
err = WSAStartup( wVersionRequested,&wsaData);
if(err!=0)
{
//不能找到Winsock动态库
return err;
}
if( LOBYTE(wsaData.wVersion)!=2||HIBYTE(wsaData.wVersion)!=0)
{
return WSAVERNOTSUPPORTED;
} //WinSock动态库可用
return 0;
}
②获取计算机名的函数GetLocalHostName,代码如下:
Int CB09010701_IPAddressDlg::GetLocalHostName(CString& sHostName) {
char szHostName[256];
int nRetCode;
nRetCode=gethostname(szHostName,sizeof(szHostName));
if (nRetCode != 0)
{
//错误产生
sHostName = _T("Not available");;
return WSAGetLastError();
}
sHostName = szHostName;
return 0;
}
③调用GetIPAddress获得IP地址,代码如下:
int CB09010701_IPAddressDlg::GetB09010701_IPAddress(const CString& sHostName,CString& sB09010701_IPAddress)
{
struct hostent FAR *IpHostEnt = gethostbyname(sHostName);
if(IpHostEnt == NULL)
{
//错误产生
sB09010701_IPAddress = _T("");
return WSAGetLastError();
}
LPSTR IpAddr = IpHostEnt->h_addr_list[0];
if (IpAddr)
{
struct in_addr inAddr;
memmove (&inAddr,IpAddr,4);//将地址进行转换成常规形式
sB09010701_IPAddress= inet_ntoa(inAddr);
if(sB09010701_IPAddress.IsEmpty())
sB09010701_IPAddress = _T("Not available");//保存在变量sB09010701_IPAddress中
}
return 0;
}
④初始对话框,获得计算机名和IP地址,代码如下:
BOOL CB09010701_PAddressDlg::OnInitDialog()
{
CDialog::OnInitDialog();
......(省略一段代码)
int nRetCode;
nRetCode=StartUp();//调用StarUp函数
TRACE1("StartUp RetCode:%d\n",nRetCode);
nRetCode=GetLocalHostName(m_sHostName);//调用GetLocalHostName函数 TRACE1("GetLocalHostName nRetCode:%d\n",nRetCode);
nRetCode=GetB09010701_IPAddress(m_sHostName,m_sB09010701_IPAddress);
//调用 GetB09010701_IPAddress函数
TRACE1("GetB09010701_IPAddress nRetCode:%d\n",nRetCode);
nRetCode=CleanUp();//释放资源
TRACE1("CleanUp nRetCode:%d\n",nRetCode);
UpdateData(FALSE);//更新控件的显示内容
return TRUE; // return TRUE unless you set the focus to a control }
基于UDP模式的点对点聊天
2.1实验目的
?熟悉VC++6.0的使用环境。
?掌握最基本的基于控制台、对话框、单文档及多文档界面的项目创建方法。
?掌握项目中的文件构成及作用,学会在程序文件中增加功能代码的方法。
?掌握程序的生成和调试方法。
2.2实验内容
使用UDP实现两台计算机之间的点对点聊天。
2.3实验过程
既然是点对点,就不需要连接服务器,每个程序都是一个接收者以及发送者,因此每个客户端只需要建立一个socket,然后同一个本地端口绑定在一起,这样只要对方计算机向这个端口发送数据,该客户端就可以接收数据了;同时该socket发送信息的时候,只需确定对方的端口和IP就可以发送了。
2.4实验结果及分析
第一个程序应用端口设置为:第二个程序应用端口设置为://设定UDP绑定端口 //设定UDP绑定端口
listeningport=200; listeningport=100;
//设定发送端口 //设定发送端口
destport=100; destport=200;
?程序一、二运行界面如下图:
?程序一发送“hello”,程序二发送“hi”,运行结果分别如下图:
2.5关键代码
①构造和析构函数,代码如下:
//构造函数
CComm::CComm()
{
ListenSocket = INVALID_SOCKET; // 开始设置为INVALID_SOCKET
#ifdef _WIN32 // 如果是win32系统
WORD VersionRequested = MAKEWORD(1,1);
WSADATA wsaData;
WSAStartup(VersionRequested, &wsaData); // 启动winsock服务
if ( wsaData.wVersion != VersionRequested )
{
printf("Wrong version or WinSock not loaded\n");
fflush(0);
}
#endif
}
//析构函数
CComm::~CComm()
{
if ( ListenSocket != INVALID_SOCKET )
closesocket( ListenSocket ); // 如果已经创建、则关闭#ifdef _WIN32 // 调用WSACleanup
WSACleanup();
#endif
}
②数据接收,代码如下:
//地址绑定【这里函数listen只是绑定一个端口】
bool CComm::Listen( int PortNum )
{
ListenSocket = socket(PF_INET, SOCK_DGRAM, 0);
if ( ListenSocket == INVALID_SOCKET )
{
printf("Error: socket创建失败\n");
fflush(0);
return false;
}
srv.sin_family = PF_INET;
srv.sin_addr.s_addr = htonl( INADDR_ANY ); // 任何地址
srv.sin_port = htons( PortNum );
if ( bind( ListenSocket, (struct sockaddr *)&srv, sizeof(srv)) != 0 )
{
printf("Error: 绑定失败\n");
fflush(0);
closesocket( ListenSocket );
return false;
}
int ThreadID; // 线程id
DWORD thread;
//调用createthread创建线程
ThreadID = (int)CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)(CComm::ListenThread), (void *)this, 0, &thread);
ThreadID = ThreadID ? 0 : 1; // 如果成功,则返回为0 if(ThreadID) // ThreadID如果不为0,则线程创建失败
{
printf("线程创建失败\n");
return false;
}
else
return true;
}
//实际的数据接收线程
void *CComm::ListenThread( void *data )
{
char buf[4096];
CComm *Comm = (CComm *)data;
int len = sizeof(Comm->client);
while(1) // 一直循环
{ //接收数据
int result = recvfrom( Comm->ListenSocket, buf, sizeof(buf)-1, 0, (sockaddr *)&Comm->client, (socklen_t *)&len);
if ( result > 0 )
{
buf[result] = 0;
printf("Message received from host %s port %i\n", inet_ntoa(Comm->client.sin_addr), ntohs(Comm->client.sin_port));
printf(">> %s", buf);
fflush(0);
}
}
}
③数据发送,代码如下:
bool CComm::SendMsg( char *Msg, int Len, char *host, short port ) {
signed int Sent;
hostent *hostdata;
if ( atoi(host) ) // 是否IP地址为标准形式
{
u_long ip = inet_addr( host );
hostdata = gethostbyaddr( (char *)&ip, sizeof(ip), PF_INET );
}
else // 否则则可能是机器名
{
hostdata = gethostbyname( host );
}
if ( !hostdata )
{
printf("获得机器名错误\n");
fflush(0);
return false;
}
sockaddr_in dest; // 发送目标地址
dest.sin_family = PF_INET;
dest.sin_addr = *(in_addr *)(hostdata->h_addr_list[0]);
dest.sin_port = htons( port );
printf("信息已经被发送到主机%s 端口为%i\n", inet_ntoa(dest.sin_addr), ntohs(dest.sin_port)); //数据发送Sent = sendto(ListenSocket, Msg, Len, 0, (sockaddr *)&dest, sizeof(sockaddr_in));
if ( Sent != Len )
{
printf("错误发送UDP信息\n");
fflush(0);
return false;
}
return true;
}
④主函数代码如下:
#include "CComm.h"
#include
int main( int argc, char *argv[] )
{
char buffer[4096]; // buffer we'll use to store msg read in from stdin
short listeningport; // port to listen on
short destport; // port to send to
char *desthost; // address of destination machine
printf("初始化...\n");
//设定UDP绑定端口
listeningport=100;
//设定发送端口
destport=200;
desthost = "10.34.9.61";//设定目标地址
CComm myComm;
if (! myComm.Listen(listeningport)) // 绑定地址
{
printf("端口 %s绑定失败\n", listeningport);
return 0;
}
printf("聊天程序成功建立,端口为100:
while( fgets(buffer, sizeof(buffer), stdin) ) // 获得输入数据
{
myComm.SendMsg( buffer, strlen(buffer), desthost, destport );
// 发送数据到目标地址
}
return 0;
}
基于TCP 模式的通信
3.1实验目的
?熟悉VC++6.0的使用环境。
?掌握最基本的基于控制台、对话框、单文档及多文档界面的项目创建方法。
?掌握项目中的文件构成及作用,学会在程序文件中增加功能代码的方法。
?掌握程序的生成和调试方法。
3.2实验内容
使用TCP实现C/S结构的聊天程序。
3.3实验过程
☉服务端程序开发:初始化socket、接收线程、数据发送。
☉客户端程序开发:连接服务器、接收线程、数据发送。
3.4实验结果及分析
?首先启动服务器程序,然后启动两个客户端,两个客户端分别发送“hello”“thank you”后运行界面如下图:
3.5关键代码
①初始化socket(服务端程序)
BOOL CCSocketDlg::OnInitDialog()
{
CDialog::OnInitDialog();
(省略部分代码)
count=0;
m_list.InsertColumn(0,"消息");
m_list.SetColumnWidth(0,435);
m_edit.SetLimitText(99);
//初始化socket数组
for (int i=0;i<50;i++)
msgsock[i]=NULL;
//设定地址
serv.sin_addr.s_addr=htonl(INADDR_ANY);
serv.sin_family=AF_INET;
serv.sin_port=5000;//htons(5000);
addlen=sizeof(serv);
m_button.EnableWindow(FALSE);
//创建socket
sock=socket(AF_INET,SOCK_STREAM,0);
//绑定
if (bind(sock,(sockaddr*)&serv,addlen))
{
m_edit.SetWindowText("绑定错误");
}else
{
//显示提示信息,表示服务器创建成功
//m_list.InsertItem(count++,inet_ntoa(serv.sin_addr));
m_edit.SetWindowText("服务器创建成功");
//开始侦听
listen(sock,5);
//调用线程
AfxBeginThread(&thread,0);
}
return TRUE; // return TRUE unless you set the focus to a control }
②接收线程(服务端程序)
UINT thread(LPVOID p)
{
char buff[100];
CSize size;
size.cx=0;
size.cy=30;
int s=1,msgcount,loop=1,flag=0;
CCSocketDlg *dlg=(CCSocketDlg*)AfxGetApp()->GetMainWnd();
//获得客户端数量
msgcount=dlg->getcount();
if (msgcount==-1)
loop=0;
if(loop)
{
s=1;
dlg->msgsock[msgcount]=accept(dlg->sock,(sockaddr*)&(dlg->serv),& (dlg->addlen));
if (dlg->msgsock[msgcount]==INVALID_SOCKET)
{
dlg->m_edit.SetWindowText("Error accept");
}
else
{
//启动线程
AfxBeginThread(thread,0);
dlg->SetForegroundWindow();
dlg->m_list.InsertItem(dlg->count++,"连接成功");
dlg->m_list.InsertItem(dlg->count++,inet_ntoa(dlg->serv.sin_addr) );
dlg->m_list.Scroll(size);
dlg->m_button.EnableWindow(TRUE);
while(s!=SOCKET_ERROR)
{
//循环接收数据
s=recv(dlg->msgsock[msgcount],buff,100,0);
dlg->SetForegroundWindow();
if (s!=SOCKET_ERROR)
{
dlg->m_list.InsertItem(dlg->count++,buff);
dlg->m_list.Scroll(size);
dlg->sendtoall(dlg->msgsock[msgcount],buff);
}
}
send(dlg->msgsock[msgcount],"Disconnected",100,0);
dlg->m_list.InsertItem(dlg->count++,"Disconnected");
dlg->m_list.Scroll(size);
dlg->msgsock[msgcount]=NULL;
for (int i=0;i<50;i++)
if (dlg->msgsock[i]!=NULL)
flag=1;
if (flag!=1)
dlg->m_button.EnableWindow(FALSE);
closesocket(dlg->msgsock[msgcount]);
}
}
//终止线程
AfxEndThread(0);
return 0;
}
③数据发送(服务端程序)
void CCSocketDlg::OnButton1()
{
char buff[100];
m_edit.GetWindowText(buff,99);
m_edit.SetWindowText("");
m_list.InsertItem(count++,buff);
CSize size;
size.cx=0;
size.cy=30;
m_list.Scroll(size);
//循环向所有客户发送信息
for (int i=0;i<50;i++)
{
if (msgsock[i]!=NULL)
send(msgsock[i],buff,100,0);
}
}
④连接服务器(客户端程序)
void CCSocketcliDlg::OnButton2()
{
char ipaddress[35];
m_edit2.GetWindowText(ipaddress,30);
cli.sin_addr.s_addr=inet_addr(ipaddress);
cli.sin_family=AF_INET;
cli.sin_port=5000;//htons(5000);
//创建socket
clisock=socket(AF_INET,SOCK_STREAM,0);
ee=1;
//启动线程
AfxBeginThread(thread,0);
}
⑤接收线程(客户端程序)
UINT thread(LPVOID v)
{
char buff[100];
char array[25][30]=
{"192.168.0.3",
"192.168.0.4",
"192.168.0.5",
"192.168.0.6",
"192.168.0.7",
"192.168.0.8",
"192.168.0.9",
"192.168.0.10",
"192.168.0.11",
"192.168.0.12",
"192.168.0.13",
"192.168.0.14",
"192.168.0.15",
"192.168.0.16",
"192.168.0.17",
"192.168.0.18",
"192.168.0.19",
"192.168.0.20",
"192.168.0.21",
"192.168.0.22",
"192.168.0.23",
"192.168.0.24",
"192.168.0.25",
"192.168.0.26",
"192.168.0.27"};
CSize size;
size.cx=0;
size.cy=30;
int s=1,addcount=0;
CCSocketcliDlg *dlg=(CCSocketcliDlg*) AfxGetApp()->GetMainWnd();
dlg->m_connect.EnableWindow(FALSE);
dlg->m_disconnect.EnableWindow(TRUE);
//连接到服务器
while(connect(dlg->clisock,(sockaddr*)&(dlg->cli),sizeof(dlg->cli )) && dlg->ee!=0)
{
dlg->m_edit.SetWindowText("等待.....");
//空循环
for (int i=0;i<=65000;i++)
for(int j=0;j<=200;j++);
if (addcount==25)
addcount=0;
dlg->cli.sin_addr.s_addr=inet_addr(array[addcount++]);
}
if (dlg->ee==1)
dlg->m_list.InsertItem(dlg->count++,"连接成功");
dlg->m_button1.EnableWindow(TRUE);
dlg->SetForegroundWindow();
//循环获得数据
while(s!=SOCKET_ERROR && dlg->ee!=0)
{
//调用recv函数接收数据
s=recv(dlg->clisock,buff,100,0);
dlg->SetForegroundWindow();
if (s!=SOCKET_ERROR && dlg->ee!=0)
dlg->m_list.InsertItem(dlg->count++,buff);
dlg->m_list.Scroll(size);
}
//发送断开命令
send(dlg->clisock,"Disconnected",100,0);
dlg->m_button1.EnableWindow(FALSE);
dlg->m_connect.EnableWindow(TRUE);
dlg->m_disconnect.EnableWindow(FALSE);
closesocket(dlg->clisock);
AfxEndThread(0);
return 0;
}
⑥数据发送(客户端程序)
void CCSocketcliDlg::OnButton1()
{
char buff[100];
CSize size;
size.cx=0;
size.cy=30;
//获得发送信息
m_edit.GetWindowText(buff,99);
m_edit.SetWindowText("");
m_list.InsertItem(count++,buff);
m_list.Scroll(size);
//发送数据
send(clisock,buff,100,0);
}
FTP客户端实验
4.1实验目的
?熟悉VC++6.0的使用环境。
?掌握最基本的基于控制台、对话框、单文档及多文档界面的项目创建方法。
?掌握项目中的文件构成及作用,学会在程序文件中增加功能代码的方法。
?掌握程序的生成和调试方法。
4.2实验内容
构建一个ftp服务器,设计一个ftp客户端软件来实现文件的上传和下载。
4.3实验过程
☉使用MFC AppWizard创建应用程序框架
☉添加控件对象:在主对话框中必须有编辑控件,让用户输入FTP服务器的地址、登录用户名和登录口令;为显示服务器缺省目录下文件和用户名,在对话框中添加一个列表框;还有相应的用来激活“查询”、“上传”、“下载”
等各种操作的按钮控件。
☉实现文件查询和显示:对“查询”按钮控件(ID:IDOK)的BN_CLICKED事件添加事件处理函数,将函数的名称改为OnQuery,添加带码。使用ListContent()函数来专门负责文件及目录名的显示工作。
☉文件下载:在CFtpDlg中为“下载”按钮(ID:IDC_DOWNLOAD)的BN_CLICKED 事件添加事件处理函数,接受系统缺省函数名。将文件下载的任务交由函数GetFile()来处理。
☉文件上传:在CFtpDlg类中为“上传”按钮(ID:IDC_UPLOAD)的BN_CLICKED 事件添加事件处理函数,接受系统缺省函数名。将文件上传的任务交由函数PutFile()来处理,为CFtpDlg类添加函数。
4.4实验结果及分析
?开启本地机的FTP服务功能,运行程序,域名编辑框输入“localhost”,保持登录名和口令编辑框为空,单击查询按钮,获得FTP服务器的缺省目录下的文件名和目录名,其中目录名被标记。
?选择列表框中一个文件,单击“下载”按钮,程序提示用户在本地机上选择一个合适路径和文件名存储下载的文件。选定之后,文件开始下载,下载完成之后收到应用程序的提示信息。
?FTP服务程序记录的上述下载操作过程。
?测试文件的上传功能,单击“上传”按钮,选择要上传的文件,程序获得该文件的路径和文件名,以相同的文件名向FTP服务器上传该文件,上
传完毕给出提示信息。
?FTP服务程序记录的上述上传过程。
4.5关键代码
①CB09010701FTPDlg的OnQuery()函数
void CB09010701FTPDlg::OnQuery()
{
//获得当前输入
U pdateData(TRUE);
//清除列表框的内容
w hile(m_ListFile.GetCount()!=0)
m_ListFile.DeleteString(0);
//显示文件和目录名
L istContent();
}
②CB09010701FTPDlg的ListContent()函数
void CB09010701FTPDlg::ListContent()