相信很多人都用过CSocket类,用其实现网络通讯非常方便,但其效率很低.这里我讲一讲使用API函数Socket实现
网络文件传输的方法,下面为实例程序的图片:
下面先描述一下这个程序的原理,此程序为服务器端与客户端集成在一起的程序,程序会单独为接收和发送开新的
线程.即能同时实现接收和发送的功能.单击准备接收按钮程序会自动启动新线程,开始监听向本地发送文件的请求.
如发现向本地发送文件的请求.弹出文件保存对话框,选择文件保存位置及文件名.这是接收文件的处理
当发送文件时,先在上方IP地址栏内填入对方IP地址,然后点打开,打开要发送的文件.其会显示在左边的编辑框内
最后点发送.程序会为发送创建单独线程.
在文件接收与传输的过程当中,会显示进度与各个阶段的状态.
下面为本实例源代码的下载地
址.https://www.doczj.com/doc/d39847319.html,/WinSock.rar
我将在下一次分析源代码的具体实现细节.(未完待续)
上回对这个程序做了个简单的介绍,这回我来讲讲具体实现细节.
首先最主要的是加载套接字库
在StdAfx.h中我们加入如下语句:
#include
然后初始化套接字库,在App类中的InitInstance()函数中加入如下语句:
if(!AfxSocketInit())
{
AfxMessageBox("加载套接字库失败!");
return FALSE;
}
需要注意的是当程序连接的时候还要连接Ws2_32.lib这个库.接下来的工作我们都在Dialog的View类中完成,
对于对话框上控件的摆放和命名可自由搭配.其中每个按钮都有命令响应函数.两个进度条分别关联了两个成员变量.剩下的一个ip地址框,三个编辑框,用作用户和程序交流信息.
其中打开文件按钮的命令响应函数如下
void CWinSockDlg::OnOpen()
{
CFileDialog fileDlg(TRUE); //创建打开文件对话框
fileDlg.m_ofn.lpstrTitle="打开文件";
fileDlg.m_ofn.lpstrFilter="All Files(*.*)/0*.*/0/0"; //设定筛选
if(IDOK==fileDlg.DoModal())
{
m_sendfname=fileDlg.GetPathName();
]
//如果用户选择确定设置发送文件名并对对话框显示做响应调整
(GetDlgItem(IDC_EDIT1))->SetWindowText(m_sendfname);
(GetDlgItem(IDC_EDITSEND))->SetWindowText("准备好发送");
}
}
准备接收文件按钮和发送文件按钮响应函数如下
void CWinSockDlg::OnRecv()
{
sockrecv* sr=new sockrecv; //声明一个发送参数结构体
sr->hwnd=m_hWnd; //将主窗口句柄传送给它
HANDLE handl=CreateThread(NULL,0,SocketRecv,(LPVOID)sr,0,NULL);//创建线程用于接收
CloseHandle(handl);
}
void CWinSockDlg::OnSend()
{
socksend* ss=new socksend; //声明一个发送参数结构体
//取得IP地址控件上的地址值,设定发送文件名,传递主窗口句柄
((CIPAddressCtrl*)GetDlgItem(IDC_IPADDRESS1))->GetAddress(ss->dwip); ss->sendfname=m_sendfname;
ss->hwnd=m_hWnd;
HANDLE handl=CreateThread(NULL,0,SocketSend,(LPVOID)ss,0,NULL);
CloseHandle(handl);
}
下面是接收和发送参数结构体的定义:
struct sockrecv
{
HWND hwnd;
};
struct socksend
{
HWND hwnd;
DWORD dwip;
LPCSTR sendfname;
};
下面是最关键的接收线程函数,和发送线程函数:
DWORD WINAPI CWinSockDlg::SocketRecv(LPVOID lpParameter)
{
HWND hwnd=((sockrecv*)lpParameter)->hwnd;//将参数传递到本地变量
CString recvfname;
SOCKET socketrecv=socket(AF_INET,SOCK_STREAM,0); //创建套接字if(INVALID_SOCKET==socketrecv)
{
closesocket(socketrecv);
::MessageBox(hwnd,"套接字创建失败!","警告",MB_OK);
return FALSE;
}
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
addrSrv.sin_family=AF_INET;
addrSrv.sin_port=htons(8848);
int ret1;
ret1=bind(socketrecv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR)); //绑定套接字if(SOCKET_ERROR==ret1)
{
closesocket(socketrecv);
::MessageBox(hwnd,"绑定失败!","警告",MB_OK);
return FALSE;
}
int ret2;
ret2=listen(socketrecv,5); //开始监听
if(SOCKET_ERROR==ret2)
{
closesocket(socketrecv);
::MessageBox(hwnd,"监听失败!","警告",MB_OK);
return FALSE;
}
SOCKADDR_IN addrconn;
int len=sizeof(SOCKADDR);
::PostMessage(hwnd,UM_PRGRRDY,0,0); //向主窗口发送消息
SOCKET sockConn=accept(socketrecv,(SOCKADDR*)&addrconn,&len); //开始接收if(INVALID_SOCKET ==sockConn)
{
closesocket(socketrecv);
::MessageBox(hwnd,"接收失败!","警告",MB_OK);
return FALSE;
}
::PostMessage(hwnd,UM_PRGRBGN,0,0); //向主窗口发送消息
BOOL bRet = TRUE;
int dataLength, cbBytesRet, cbLeftToReceive;
BYTE* recdData = NULL;
CFile destFile;
CFileException fe;
BOOL bFileIsOpen = FALSE;
//当接收到向本地发送文件的请求时弹出文件保存对话框
CFileDialog fileDlg(FALSE);
fileDlg.m_ofn.lpstrTitle="保存将接收到的文件";
fileDlg.m_ofn.lpstrFilter="All Files(*.*)/0*.*/0/0";
if(IDOK==fileDlg.DoModal())
{
recvfname=fileDlg.GetPathName();
}
//用从文件保存对话框中得到的文件名在指定位置创建文件
if( !( bFileIsOpen = destFile.Open( recvfname, CFile::modeCreate |
CFile::modeWrite | CFile::typeBinary, &fe ) ) )
{
TCHAR strCause[256];
fe.GetErrorMessage( strCause, 255 );
TRACE( "GetFileFromRemoteSender encountered an error while opening the local file " " File name = %s Cause = %s m_cause = %d m_IOsError = %d ",
fe.m_strFileName, strCause, fe.m_cause, fe.m_lOsError );
bRet = FALSE;
goto PreReturnCleanup;
}
//首先接收文件的长度信息
cbLeftToReceive = sizeof( dataLength );
do
{
BYTE* bp = (BYTE*)(&dataLength) + sizeof(dataLength) - cbLeftToReceive;
cbBytesRet = recv(sockConn, (char*)bp, cbLeftToReceive,0);
if ( cbBytesRet == SOCKET_ERROR || cbBytesRet == 0 )
{
int iErr = ::GetLastError();
TRACE( "GetFileFromRemoteSite returned a socket error while getting file length " " Number of Bytes received (zero means connection was closed) = %d "
" GetLastError = %d ", cbBytesRet, iErr );
bRet = FALSE;
goto PreReturnCleanup;
}
cbLeftToReceive -= cbBytesRet;
}
while ( cbLeftToReceive > 0 );
dataLength = ntohl( dataLength ); //将文件的长度信息转化为本地字节序
//准备开始接收文件
recdData = new byte[RECV_BUFFER_SIZE];
cbLeftToReceive = dataLength;
do
{
//向主窗口发送消息主要用于控制进度条
::PostMessage(hwnd,UM_PRGRECV,0,(LPARAM)cbLeftToReceive);
int iiGet, iiRecd;
iiGet = (cbLeftToReceive cbLeftToReceive : RECV_BUFFER_SIZE ; iiRecd = recv(sockConn, (char*)recdData, iiGet,0 ); if ( iiRecd == SOCKET_ERROR || iiRecd == 0 ) { int iErr = ::GetLastError(); TRACE( "GetFileFromRemoteSite returned a socket error while getting chunked file data " " Number of Bytes received (zero means connection was closed) = %d " " GetLastError = %d ", iiRecd, iErr ); bRet = FALSE; goto PreReturnCleanup; } destFile.Write( recdData, iiRecd); cbLeftToReceive -= iiRecd; } while ( cbLeftToReceive > 0 ); ::PostMessage(hwnd,UM_PRGROVER,0,0); //向主窗口发送消息 PreReturnCleanup: //文件接收结束标签 delete[] recdData; //释放内存 if ( bFileIsOpen ) destFile.Close(); closesocket(sockConn); closesocket(socketrecv); return bRet; } 下面是发送线程函数: DWORD WINAPI CWinSockDlg::SocketSend(LPVOID lpParameter) { //将参数传递给本地变量 HWND hwnd=((socksend*)lpParameter)->hwnd; CString sendfname=((socksend*)lpParameter)->sendfname; DWORD dwip=((socksend*)lpParameter)->dwip; SOCKET socketsend=socket(AF_INET,SOCK_STREAM,0);//创建Socket SOCKADDR_IN addrSrv; addrSrv.sin_addr.S_un.S_addr=htonl(dwip); addrSrv.sin_family=AF_INET; addrSrv.sin_port=htons(8848); int ret1; ret1=connect(socketsend,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));//连接指定地址 if(SOCKET_ERROR==ret1) { closesocket(socketsend); ::MessageBox(hwnd,"连接失败!","警告",MB_OK); return FALSE; } ::PostMessage(hwnd,UM_PRGSBGN,0,0); //向主窗口发送消息 //声明用于文件发送的本地变量 BOOL bRet = TRUE; int fileLength, cbLeftToSend; BYTE* sendData = NULL; CFile sourceFile; CFileException fe; BOOL bFileIsOpen = FALSE; //打开参数传递进来的文件用于发送 if( !( bFileIsOpen = sourceFile.Open( sendfname, CFile::modeRead | CFile::typeBinary, &fe ) ) ) { TCHAR strCause[256]; fe.GetErrorMessage( strCause, 255 ); TRACE( "SendFileToRemoteRecipient encountered an error while opening the local file " " File name = %s Cause = %s m_cause = %d m_IOsError = %d ", fe.m_strFileName, strCause, fe.m_cause, fe.m_lOsError ); bRet = FALSE; goto PreReturnCleanup; } //首先传递将要被发送文件的长度给接收端 fileLength = sourceFile.GetLength(); fileLength = htonl( fileLength ); cbLeftToSend = sizeof( fileLength ); do { int cbBytesSent; BYTE* bp = (BYTE*)(&fileLength) + sizeof(fileLength) - cbLeftToSend; cbBytesSent = send(socketsend, (const char*)bp, cbLeftToSend,0 ); if ( cbBytesSent == SOCKET_ERROR ) { int iErr = ::GetLastError(); TRACE( "SendFileToRemoteRecipient returned a socket error while sending file length " " Number of Bytes sent = %d " " GetLastError = %d ", cbBytesSent, iErr ); bRet = FALSE; goto PreReturnCleanup; } cbLeftToSend -= cbBytesSent; } while ( cbLeftToSend>0 ); //开始发送晚间 sendData = new BYTE[SEND_BUFFER_SIZE]; cbLeftToSend = sourceFile.GetLength(); do { ::PostMessage(hwnd,UM_PRGSEND,0,(LPARAM)cbLeftToSend); int sendThisTime, doneSoFar, buffOffset; sendThisTime = sourceFile.Read( sendData, SEND_BUFFER_SIZE ); buffOffset = 0; do { doneSoFar = send(socketsend,(const char*) (sendData + buffOffset), sendThisTime,0 ); if ( doneSoFar == SOCKET_ERROR ) { int iErr = ::GetLastError(); TRACE( "SendFileToRemoteRecipient returned a socket error while sending chunked file dat a " " Number of Bytes sent = %d " " GetLastError = %d ", doneSoFar, iErr ); bRet = FALSE; goto PreReturnCleanup; } buffOffset += doneSoFar; sendThisTime -= doneSoFar; cbLeftToSend -= doneSoFar; } while ( sendThisTime > 0 ); } while ( cbLeftToSend > 0 ); ::PostMessage(hwnd,UM_PRGSOVER,0,0);//向主窗口发送消息发送完毕 PreReturnCleanup: //结束标签后释放内存 delete[] sendData; if ( bFileIsOpen ) sourceFile.Close(); closesocket(socketsend); return bRet; } 到这里主要的文件传送和接收功能已经实现了,剩下的就只有几个控制进度条,和传输接送状态的用户自定义消息了,在对话框的View类中定义如下消息: #define UM_PRGRECV WM_USER+10 #define UM_PRGSEND WM_USER+11 #define UM_PRGROVER WM_USER+12 #define UM_PRGSOVER WM_USER+13 #define UM_PRGRRDY WM_USER+14 #define UM_PRGRBGN WM_USER+15 #define UM_PRGSBGN WM_USER+17 然后分别写这些消息的响应函数: afx_msg void CWinSockDlg::onprgrecv(WPARAM wParam,LPARAM lParam) { //控制接收进度条的消息响应函数 int prgnow=(int)lParam; //将剩余接收字节作为参数传递给函数 if(m_beginprgrecv) //判断是否第一次接到此消息 { m_prgrecv.SetRange32(0,prgnow); //设置进度条范围 m_prgrecvpre=prgnow; //给成员变量赋值 m_beginprgrecv=FALSE; //改变标志 } int thistran=m_prgrecvpre-prgnow; //本次传输字节数 int now=m_prgrecv.GetPos(); //先前的总传输量 m_prgrecv.SetPos(thistran+now); //设置进度条位置 } afx_msg void CWinSockDlg::onprgrover(WPARAM wParam,LPARAM lParam) { //恢复接收状态 m_prgrecv.SetPos(0); m_prgrecvpre=0; m_beginprgrecv=TRUE; (GetDlgItem(IDC_EDITRECV))->SetWindowText("尚未准备好接收"); } afx_msg void CWinSockDlg::onprgsend(WPARAM wParam,LPARAM lParam) { //请参考接收消息响应函数解释 int prgnow=(int)lParam; if(m_beginprgsend) { m_prgsend.SetRange32(0,prgnow); m_prgsendpre=prgnow; m_beginprgsend=FALSE; } int thistran=m_prgsendpre-prgnow; int now=m_prgsend.GetPos(); m_prgsend.SetPos(thistran+now); } afx_msg void CWinSockDlg::onprgsover(WPARAM wParam,LPARAM lParam) { //恢复发送状态 m_prgsend.SetPos(0); m_prgsendpre=0; m_beginprgsend=TRUE; (GetDlgItem(IDC_EDITSEND))->SetWindowText("尚未准备好发送"); } afx_msg void CWinSockDlg::onprgrrdy(WPARAM wParam,LPARAM lParam) { (GetDlgItem(IDC_EDITRECV))->SetWindowText("准备好接收");//UM_PRGRRD Y消息响应 } afx_msg void CWinSockDlg::onprgrbgn(WPARAM wParam,LPARAM lParam) { (GetDlgItem(IDC_EDITRECV))->SetWindowText("开始接收"); //UM_PRGRBGN 消息响应 } afx_msg void CWinSockDlg::onprgsbgn(WPARAM wParam,LPARAM lParam) //UM_PRGSBGN消息响应 { (GetDlgItem(IDC_EDITSEND))->SetWindowText("开始发送"); } 到这里所有功能都实现了,是不是很简单.本实例源代码:下载 实训任务二:控制LED灯点亮 实训准备:KeilC51软件, proteus仿真软件,STP-ISC下载软件,单片机实验板,电源线、下载线 分组情况:每4人为一组,组长一名。小老师两名协助老师指导操作过程。知识目标:1.了解单片机各引脚功能; 2.理解单片机最小系统组成部分; 3.掌握C51赋值语句用法; 4.掌握C51语言编程、编译基本方法; 5.掌握proteus仿真软件基本操作方法; 6.掌握C51程序编写、编译、仿真调试、下载流程及方法。 能力目标:1.培养学生数字逻辑分析能力; 2.培养学生分析问题及解决问题的能力; 情感目标:1.培养学生团队合作的精神; 2.培养学生的创新意识; 教学重点:1.C51赋值语句用法; 2.C51语言编程、编译基本方法 教学难点:1.半英文操作界面的理解 2.调试程序的方法 课时:8课时 讲授新课1.单片机引脚功能(40引脚) 电源、接地、I/O端口、控制引脚、时钟引脚、 复位引脚 2.单片机最小系统 组成部分:单片机、电源、接地、复位电路、 时钟电路。 解释时钟电路,比喻为学校的铃声。 区分:单片机系统与最小系统 3.C51语言基本格式 #include 实训任务三:控制LED流水灯 实训准备:KeilC51软件, proteus仿真软件,STC-ISP下载软件, 单片机实验板,电源线、下载线 分组情况:每3-4人为一组,组长一名。小老师两名协助老师指导操作过程。知识目标:1.理解C51语言数据类型; 2.了解单片机的机器周期; 3.理解数组概念及用法; 4.掌握for循环语句的用法; 5.掌握while循环语句的简单用法; 6.掌握C51程序编写、编译、仿真调试、下载流程及方法。 能力目标:1.培养学生思维逻辑分析能力; 2.培养学生分析问题及解决问题的能力; 情感目标:1.培养学生团队合作的精神; 2.培养学生的创新意识; 教学重点:1.for循环语句的用法; 2.数组的概念及用法; 3.C51语言数据类型; 教学难点:1.for循环语句的用法; 2.数组的概念及用法; 课时:4课时 子任务一:控制LED灯闪烁(2课时) 第一章 1.给出下列有符号数的原码、反码和补码(假设计算机字长为8位)。 +45 -89 -6 +112 答:【+45】原=00101101,【+45】反=00101101,【+45】补=00101101 【-89】原=11011001,【-89】反=10100110,【-89】补=10100111 【-6】原=10000110,【-6】反=11111001,【-6】补=11111010 【+112】原=01110000,【+45】反=01110000,【+45】补=01110000 2. 指明下列字符在计算机内部的表示形式。 AsENdfJFmdsv120 答:41H 73H 45H 4EH 64H 66H 4AH 46H 6DH 64H 73H 76H 31H 32H 30H 3. 什么是单片机? 答:单片机是把微型计算机中的微处理器、存储器、I/O接口、定时器/计数器、串行接口、中断系统等电路集成到一个集成电路芯片上形成的微型计算机。因而被称为单片微型计算机,简称为单片机。 4. 单片机的主要特点是什么? 答:主要特点如下: 1) 在存储器结构上,单片机的存储器采用哈佛(Harvard)结构 2) 在芯片引脚上,大部分采用分时复用技术 3) 在内部资源访问上,采用特殊功能寄存器(SFR)的形式 4) 在指令系统上,采用面向控制的指令系统 5) 内部一般都集成一个全双工的串行接口 6) 单片机有很强的外部扩展能力 5. 指明单片机的主要应用领域。 答:单机应用:1) 工业自动化控制;2) 智能仪器仪表;3) 计算机外部设备和智能接口;4) 家用电器多机应用:功能弥散系统、并行多机处理系统和局部网络系统。 第二章 1. MCS-51单片机由哪几个部分组成? 答:MCS-51单片机主要由以下部分组成的:时钟电路、中央处理器(CPU)、存储器系统(RAM和ROM)、定时/计数器、并行接口、串行接口、中断系统及一些特殊功能寄存器(SFR)。 2. MCS-51的标志寄存器有多少位,各位的含义是什么? 基础知识:51单片机编程基础 第一节:单数码管按键显示 第二节:双数码管可调秒表 第三节:十字路口交通灯 第四节:数码管驱动 第五节:键盘驱动 第六节:低频频率计 第七节:电子表 第八节:串行口应用 基础知识:51单片机编程基础 单片机的外部结构: 1. DIP40双列直插; 2. P0,P1,P2,P3四个8位准双向I/O引脚;(作为I/O输入时,要先输出高电平) 3. 电源VCC(PIN40)和地线GND(PIN20); 4. 高电平复位RESET(PIN9);(10uF电容接VCC与RESET,即可实现上电复位) 5. 内置振荡电路,外部只要接晶体至X1(PIN18)和X0(PIN19);(频率为主频的12倍) 6. 程序配置EA(PIN31)接高电平VCC;(运行单片机内部ROM中的程序) 7. P3支持第二功能:RXD、TXD、INT0、INT1、T0、T1 单片机内部I/O部件:(所为学习单片机,实际上就是编程控制以下I/O部件,完成指定任务) 1. 四个8位通用I/O端口,对应引脚P0、P1、P2和P3; 2. 两个16位定时计数器;(TMOD,TCON,TL0,TH0,TL1,TH1) 3. 一个串行通信接口;(SCON,SBUF) 4. 一个中断控制器;(IE,IP) 针对AT89C52单片机,头文件AT89x52.h给出了SFR特殊功能寄存器所有端口的定义。 C语言编程基础: 1. 十六进制表示字节0x5a:二进制为01011010B;0x6E为01101110。 2. 如果将一个16位二进数赋给一个8位的字节变量,则自动截断为低8位,而丢掉高8位。 3. ++var表示对变量var先增一;var—表示对变量后减一。 4. x |= 0x0f;表示为x = x | 0x0f; 5. TMOD = ( TMOD & 0xf0 ) | 0x05;表示给变量TMOD的低四位赋值0x5,而不改变TMOD的高 四位。 6. While( 1 ); 表示无限执行该语句,即死循环。语句后的分号表示空循环体,也就是{;} 《程序设计基础》说课稿 各位评委老师大家好,今天我给大家带来的是电子专业电子技术基础的说课,下面我就以《程序设计基础》的一堂实训课为课题进行我的说课内容。我主要从教材分析、学情分析、教学目标、教学重点及难点、教法学法、教学过程、教学评价与反思七个方面对本堂课进行阐述。 一、教材分析 我选用的教材是朱家建老师编写的《单片机与可编程控制器》,这本教材知识点讲解细腻,结构和内容新颖,并且注重理论和实践相结合,非常符合中职学生以就业为导向的实际。而《单片机与可编程控制器》这么学科本身既是高考和单招考试的必考科目,又是各用人单位招聘相关专业学生的考核重点,因此在整个电子专业的学习体系中地位突出。 我选题的《程序设计基础》是教材中第三章第三节的内容,详见教材P56页—P65页,它既作为指令格式和指令系统知识的延伸学习,又是之后各种程序设计的基础与铺垫,在整个单片机学习过程中起着至关重要的作用。 二、学情分析 我面对的学生是中职二年级电子专业的学生: 知识储备:已经掌握指令格式、寻址方式以及指令系统等基础知识; 学习能力:思维活跃、乐于动手操作,但基础薄弱、缺乏自信、特别是对理论知识的学习积极性不高。 应对措施:针对学生的这些情况,我在教学过程中首先才用鼓励式教学,多鼓励、多表扬,以此提高学生自信心。其次采用理实一体化教学模式,突出 学生爱动手操作的优点,使学生在实践中学习理论,在理论中磨练实 践。 三、教学目标 基于本堂课的教学要求和对学生实际情况的掌握,我将该节课的教学目标设定为: 1、认知目标:掌握汇编语言程序设计的流程图和一般步骤,以及基本程序结构; 2、能力目标:能够独立编写一些简单的汇编语言程序,并进行仿真、调试、烧录直到最后达到实验目的。 3、情感目标:激发学生学习热情、发挥学生主观能动性、培养学生团队合作意识。 (设定以上目标意图:简而言之就是在学生学习理论知识的同时规范和提升学生的实训能力,为今后的职业道路打好基础。) 四、教学重点及难点 1.重点的确立: 根据教学大纲的要求,结合学生以后职业道路的需要,我确定本堂课的重点:程序流程图的画写和汇编语言程序设计的基本步骤及基本程序结构 2.教学难点的确立百度文库-单片机C51程序设计
最新单片机原理与应用及C51程序设计(第二版)课后答案
51单片机C语言学习知识编程基础学习知识及其实例
单片机程序设计基础