软件课程设计报告.docx
《软件课程设计报告.docx》由会员分享,可在线阅读,更多相关《软件课程设计报告.docx(35页珍藏版)》请在冰点文库上搜索。
![软件课程设计报告.docx](https://file1.bingdoc.com/fileroot1/2023-5/9/be665460-9195-4462-9685-d3b570d879be/be665460-9195-4462-9685-d3b570d879be1.gif)
软件课程设计报告
北京科技大学
软件课程设计报告
——C语言
班级:
姓名:
黄伟波
学号:
指导教师:
日期:
年月日
目录
1设计内容1
2应用程序总体分析与设计1
3应用程序各功能模块分析与设计2
3.1功能模块2
3.1.1服务器模块2
3.1.2客户端模块2
3.2模块流程图4
4程序测试6
4.1测试过程6
4.1.1VS2010截图6
4.2测试结果7
4.2.1服务器端截图7
4.2.2客户器端截图7
5程序文档8
5.1使用说明8
5.2程序说明8
6程序源代码8
1设计内容
通过C语言程序编程实现一个局域网聊天程序,包括服务器端程序和客户端程序,程序能够实现两台主机或多台主机间的基本通信和文件传输。
2应用程序总体分析与设计
A.编译预处理命令
#include
#include
#pragmacomment(lib,"wsock32.lib")
B.该应用程序中使用的基本Winsock函数有:
WSAStartup,socket,bind,listen,accept,connect,shutdown和closesocket等,使用数据传输函数有send和recv。
C.服务器程序的步骤如下:
1)初始化以及创建套接字,由WSAStartup()和socket()函数完成。
2)要将指定协议的套接字绑定到本地的IP地址和端口号。
这个过程通过bind()函数来完成。
3)服务器处于监听状态,等待客户端连接,以便为它们的请求提供服务。
服务进程必须在所绑定的名字上进行监听。
因此需要把套接字置为监听模式。
通过listen()函数来实现。
4)服务器程序调用函数accept准备接受来自客户端的连接。
如果有一个客户端这时用connect()函数试图建立连接,服务器端就可以接受连接。
5)建立连接后,服务器端和客户端之间就可以使用send()和recv()函数进行通信。
对输入和接收到的信息进行甄别,如果收到“SendFile”,则调用接收文件模块,进入接收文件状态;若在键盘输入“SendFile”,则调用发送文件模块,进入发送文件状态。
否则,进程将按照普通信息进行收发对话。
(这里需要说明的是,默认情况下recv()函数处于阻塞模式,在接收数据前,程序将不向下执行。
)
◆在控制台应用窗口中,程序只能依次执行语句,服务器和客户端在收到信息后,可进行回复(输入回复信息,并按回车键发送),或者不回复(按回车键以接收下一条信息)。
D.客户端程序的步骤如下:
1)输入服务器IP,存放在servIP[]。
2)初始化以及创建套接字,由WSAStartup和socket函数完成。
3)呼叫服务器,请求连接,构造intCallServer()函数,其中调用了connect()函数。
4)建立连接后,服务器端和客户端之间就可以使用send和recv函数进行通信。
对输入和接收到的信息进行甄别,如果收到“SendFile”,则调用接收文件模块,进入接收文件状态;若在键盘输入“SendFile”,则调用发送文件模块,进入发送文件状态。
否则,进程将按照普通信息进行收发对话。
3应用程序各功能模块分析与设计
3.1功能模块
3.1.1服务器模块
1.套接口建立模块
1)打开Winsock——WSAStartup
应用程序或DLL只能在一次成功的WSAStartup()调用后才能进一步调用其它的WindowsSocketsAPI函数。
2)创建套接口——socket()
服务器在使用套接口通信前,必须要拥有一个套接口。
使用socket()函数创建套接口sock.三个参数分别为:
AF_INET,SOCK_STREAM,0。
3)指定本地地址——bind()
Bind()函数通过给套接口sock分配一个本地名字来为套接口建立本地捆绑,即把一个套即可与一个主机地址和端口号联系起来。
2.接收文件模块
1)定义结构体——structFiledataDataPacket(),
用于存放文件的文件名、数据和文件大小。
2)构造写文件函数——DWORDWritefile(char*fname,char*fdata,intflen)
将收到的数据写入文件。
fname:
文件名字指针,fdata:
文件数据指针,
flen:
文件大小。
3.发送文件模块
1)读文件函数——DWORDReadfile(char*fname)
读取键盘输入的路径所指定的文件,fname:
文件名字指针。
2)构造发送文件函数——DWORDSendFile(structFiledataPacket)
将文件发送到客户端,其中要使用send()函数。
4.信息交换模块——voidCommunicateWithClient()
1)监听连接——listen()
指示套接口sock进入监听连接请求的状态以及接收由客户端发出的连接请求。
接收连接时使用套接口sock1.
2)对客户端发送的信息进行处理,若收到”SendFile”,则启用接收文件模块;否则按照一般信息处理。
3)发送信息或文件,若输入”SendFile”,则启用发送文件模块,读取并向客户端发送文件;否则按照一般信息发送给客户端。
3.1.2客户端模块
1.套接口建立和连接模块
1)打开Winsock——WSAStartup
应用程序或DLL只能在一次成功的WSAStartup()调用后才能进一步调用其它的WindowsSocketsAPI函数。
2)创建套接口——socket()
服务器在使用套接口通信前,必须要拥有一个套接口。
使用socket()函数创建套接口sock.三个参数分别为:
AF_INET,SOCK_STREAM,0。
3)请求连接——connect()
调用connect()函数,提出与一个服务器建立连接的请求,如果服务器接受请求,就可以在服务器端的远程套接口与客户端的本地套接口之间建立一条连接。
2.接收文件模块
1)定义结构体——structFiledataDataPacket(),
用于存放文件的文件名、数据和文件大小。
2)构造写文件函数——DWORDWritefile(char*fname,char*fdata,intflen)
将收到的数据写入文件。
fname:
文件名字指针,fdata:
文件数据指针,
flen:
文件大小。
3)构造文件接收函数——voidRecvFile()
在RecvFile()函数中,当接收完数据后,对Writefile()函数进行调研,将数据写入文件中。
3.发送文件模块
1)读文件函数——DWORDReadfile(char*fname)
读取键盘输入的路径所指定的文件,fname:
文件名字指针。
2)构造发送文件函数——DWORDSendFile(structFiledataPacket)
将文件发送到客户端,其中要使用send()函数。
4.信息交换模块——voidCommunicateWithClient()
1)监听连接——listen()
指示套接口sock进入监听连接请求的状态以及接收由客户端发出的连接请求。
接收连接时使用套接口sock1.
2)对客户端发送的信息进行处理,若收到”SendFile”,则启用接收文件模块;否则按照一般信息处理。
3)发送信息或文件,若输入”SendFile”,则启用发送文件模块,读取并向客户端发送文件;否则按照一般信息发送给客户端。
3.2模块流程图
发送信息
服务器端
客户端
否
是
发送信息
发送文件
发送文件
否
是
读取文件名:
gets(sendfilename)
是
调用WSAStartup()初化Winsock
调用socket()创建套接字Socket
WSAStartup()初始化Winsock
closesocket()关闭socket
closesocket()关闭socket
是
否
sock1=accept()准备接受连接
listen()进入监听状态
bind()绑定套接字sock
socket()创建流式套接字sock,sock1
否
3.3程序结构图
4程序测试
4.1测试过程
4.1.1VS2010截图
服务器端
客户端
4.2测试结果
4.2.1服务器端截图
4.2.2客户器端截图
5程序文档
5.1使用说明
运行程序,在客户端窗口输入服务器IP,待连接成功后,由客户端率先向服务器发送文字信息(按回车键结束输入并发送),服务器收到信息后可对客户端信息进行回复,若不回复,则需按回车键进入接收下一条信息的状态。
在服务器(或客户端)输入“SendFile”命令后,服务器(或客户端)进入发文件状态,同时提醒客户端(或服务器)进入接收文件状态。
在服务器(或客户端)写入文件路径,按回车就可发送到客户端(或服务器)。
5.2程序说明
本程序有两大部分构成,包括服务器端程序和客户端程序。
服务器端包含的模块有套接口建立模块、文件发送模块、文件接收模块、信息交换模块;客户端包含的模块有套接口建立和连接模块、文件发送模块、文件接收模块、信息交换模块。
服务器端
(一)套接口建立模块用于加载套接字库,初始化和创建流式套接字,绑定套接字。
(二)接收文件模块用来实现文件的接收。
(三)发送文件模块用于实现向客户端发送文件。
(四)信息交换模块中调用listen()函数和accept()函数,与客户端连接成功后,会对收到和发送的信息进行甄别,如果收到“SendFile”命令,则调用接收文件函数,如果输入“SendFile”命令,则调用发送文件函数。
客户端
(一)套接口建立和接收模块用于加载套接字库,初始化和创建流式套接字,绑定套接字,和服务器进行连接。
(二)接收文件模块用来实现文件的接收。
(三)发送文件模块用于实现向客户端发送文件。
(四)第
(一)步成功与服务器建立连接后,在信息交换模块中可以与服务器进行信息传递。
该模块会对收发的信息进行甄别,如果收到“SendFile”命令,则调用接收文件函数,如果输入“SendFile”命令,则调用发送文件函数。
6程序源代码
//服务器端程序
#include//使用版本1.1,若用版本2则使用“Winsock2.h”
#include//创建一个Windows.console程序所必须的头文件
#include
#pragmacomment(lib,"wsock32.lib")//制定连接的库文件。
版本Winsock1.1使用wsock32.lib,版本Winsock2,使用ws2_32.lib
#defineRECV_PORT2000
//*******************************************套接口建立模块****************************************//
SOCKETsock,sock1;//sock监听,sock1传送数据
structsockaddr_inServerAddr;
structsockaddr_inClientAddr;
intAddrlen;
#defineMAX_FILESIZE100*1024//限制文件最大为100K
voidStartSock()
{
WSADATAWSAData;
if(WSAStartup(MAKEWORD(2,2),&WSAData)!
=0)//检查版本,初始化套接字
{
printf("sockinitfail!
\n");
}
}
voidCreateSocket()
{
sock=socket(AF_INET,SOCK_STREAM,0);//创建套接字,AF_INET表示使用互联网协议(IP)地址,SOCK_STREAM创建流式套接口
if(sock==SOCKET_ERROR)
{
printf("sockcreatefail!
\n");
WSACleanup();
}
ServerAddr.sin_family=AF_INET;//填充服务器地址及端口号
ServerAddr.sin_addr.s_addr=htonl(INADDR_ANY);//htonl()函数把32位主机字节顺序的IP地址转化为网络字节顺序
ServerAddr.sin_port=htons(RECV_PORT);//htons()函数把14位主机字节顺序的端口号转化为网络字节顺序
if(bind(sock,(LPSOCKADDR)&ServerAddr,sizeof(ServerAddr))==SOCKET_ERROR)//绑定套接字
{
printf("bindistheerror");
}
}
//***************************************************************************************************//
//*******************************************接收文件模块****************************************//
structFiledata//定义Filedata结构体,变量为DataPacket
{
charffname[30];//ffname数组用于存放数据文件名,最长为30个字符
charffdata[MAX_FILESIZE];//ffdata数组存放文件数据,文件大小最大为,MAX_FILESIZE
intlen;
}DataPacket;
DWORDWritefile(char*fname,char*fdata,intflen)//写文件函数,将将接收到的数据写入文件。
fname:
文件名字指针,fdata:
文件数据指针,flen:
文件大小
{
inti;
FILE*fp;//定义FILE类型的文件指针变量*fp,系统可以通过文件指针变量fp找到该文件的信息,并通过这些信息访问该文件
fp=fopen(fname,"w");//以只写方式打开一个新文本文件,若文件已经存在,则打开是清除原内容
if(fp==NULL)//检查打开fname指向的文件操作是否成功
{
printf("cannotopenthisfile\n");
}
i=0;
for(i=0;i{
fputc(fdata[i],fp);//调用文件的字符输入函数fputc(),将文本数据逐个字节写入fp所指向的文件,若调用失败,则返回EOF,其值为-1
}
fclose(fp);//写入完成后记得关闭文件
return
(1);
}
voidRecvFile()//接收文件函数
{
for(;;)
{
memset(DataPacket.ffname,0,30);
memset(DataPacket.ffdata,0,MAX_FILESIZE);
DataPacket.len=0;
if(recv(sock1,(char*)&DataPacket,sizeof(DataPacket),0)<=0)//接收数据
{
break;
}
Writefile(DataPacket.ffname,DataPacket.ffdata,DataPacket.len);//写文件,调用Writefile函数,将接收到的数据写入文件
printf("Hasreceivefile:
%s,lengthis%d",DataPacket.ffname,DataPacket.len);
printf("\n");
break;//跳出循环
}
}
//***************************************************************************************************//
//*******************************************发送文件模块****************************************//
DWORDReadfile(char*fname)//读文件函数,fname:
文件名字指针
{
FILE*fp;//定义FILE类型的文件指针变量*fp,系统可以通过文件指针变量fp找到该文件的信息,并通过这些信息访问该文件
inti;
intFilesize;//Filesize:
文件大小
charc;
intcount,total=0;//count变量用于存放要传送文件的数据项
charbuffer[100];
charSenddata[MAX_FILESIZE];//Senddata数组用于发送数据,文件大小最大为,MAX_FILESIZE
fp=fopen(fname,"r");//以只读方式打开一个文件
if(fp==NULL)//检查打开fname指向的文件操作是否成功
{
printf("cannotopenthisfile\n");
return(0);
}
i=0;
Filesize=0;//将文件大小置0
memset(Senddata,0,MAX_FILESIZE);//清空Senddata数组
while(!
feof(fp))//调用文件结束测试函数feof(),feof()功能:
测试fp所指向的文件是否已经读到文件尾部。
若该文件没有结束,则返回0.否知返回非0
//当未读到文件尾部是,则继续读取数据
{
count=fread(buffer,sizeof(char),100,fp);//调用fread()函数,从文件指针fp指向的文件中,读取100个数据项,存放到字符串数组buffer中
//若fread()函数调用成功,则返回实际读取的数据项数,存于变量count中,若文件结束或调用失败,则返回0
if(ferror(fp))//调用ferror()函数,检测文件读写是否出错,若出错则返回1,提示“ReadFileerror”
{
printf("ReadFileerror");
break;
}
Filesize+=count;//计算文件总大小
if(Filesize>MAX_FILESIZE)//如果文件大于32KB,提示出错信息
{
printf("ThisFileistoobig\n");
fclose(fp);
return(0);
}
memcpy(&Senddata[i],buffer,count);//字符串拷贝
i+=count;
}
fclose(fp);//关闭文件
Senddata[i]='\0';//字符串以\0结束
strcpy(DataPacket.ffname,fname);
memcpy(DataPacket.ffdata,Senddata,Filesize);
DataPacket.len=Filesize;
printf("%s%d\n",DataPacket.ffname,DataPacket.len);
return
(1);
}
DWORDSendFile(structFiledataPacket)//发送文件数据
{
intlength;
length=send(sock,(char*)&Packet,sizeof(DataPacket),0);//调用send()函数,发送数据
if(length<=0)
{
printf("sendFileerror!
\n");
closesocket(sock);
WSACleanup();
return(-1);
}
return
(1);
}
//***************************************************************************************************//
//*********************************************信息交换模块******************************************//
voidCommunicateWithClient()
{
charrecvBuffer[80],sendBuffer[80];
Addrlen=sizeof(structsockaddr_in);
if(listen(sock,10)!
=0)//设置本地套接字为监听模式,正在等待连接的最大队列长度为10
{
printf("listenerror");
}
elseprintf("Listening....\n");
for(;;)
{
sock1=accept(sock,(structsockaddrFAR*)&ClientAddr,&Addrlen);//接受连接
printf("ConnectisOK!
\n");
for(;;)
{
memset(recvBuffer,0,80);//用memset函数将recvBuffer缓存清零
memset(sendBuffer,0,80);//用memset函数将sendBuffer缓存清零
//接收信息和文件**********当收到"SendFile"时,启动接收文件程序******************//
recv(sock1,recvBuffer,80,0);
while(!
strcmp(recvBuffer,"SendFile"))//如果收到"SendFile",则表明客户端要向服务器发送文件,服务器进入接收文件状态
{
printf("准备接收文件,请从客户端输写入要传送文件的文件名\