计算机网络编程实验报告.docx
- 文档编号:7249826
- 上传时间:2023-05-11
- 格式:DOCX
- 页数:37
- 大小:449.38KB
计算机网络编程实验报告.docx
《计算机网络编程实验报告.docx》由会员分享,可在线阅读,更多相关《计算机网络编程实验报告.docx(37页珍藏版)》请在冰点文库上搜索。
计算机网络编程实验报告
计算机网络编程实验报告
学院计算机学院
专业计算机科学与技术
年级2008级
姓名徐嬴
学号3008216107
2011年4月25日
Phase1:
EstablishingClient-ServerCommunications
1.题目要求
此题目要求在TCP协议基础上,建立服务器和客户端的通信,以实现客户端登录系统,向服务器发送要分享的文件表,以及搜索要下载的文件和退出登录这四项主要功能。
具体要求如下:
1.1登录功能:
客户端与服务器建立了TCP连接后,即开始请求登录,客户向服务器发送自己的用户名,加密后的密码信息,服务器接收到LGIN消息后,开始查自己“数据库”中的用户列表,以确定该用户是否合法,若合法,返回登录成功的消息,以便用户可以进行以下的操作,否则,返回登录失败的消息,允许用户再次输入;
1.2分享功能:
客户端首先从输入流读入用户想要分享的文件信息,包括供其他peer端下载的端口号以及文件名。
读入完成后进行打包,再发送给服务器,服务器接受到之后,进行解包,将文件名,文件的用户名,用户IP以及端口号保存在服务器的“数据库”即内存中;
1.3搜索功能:
客户端从输入流读入要搜索的文件名,打包,将消息传给服务器,服务器查找“数据库”找到所有peer端上传的该文件,将这些文件信息分别打包传给用户。
1.4退出功能:
客户端输入退出指令,并将退出消息发送给服务器,服务器接收到消息后首先将该客户端分享的文件列表删除,然后再断开连接。
1.5其他:
由于会有很多的客户端同时与服务器建立联系,所以在服务器端需要用到多线程来进行监听,每来了一个TCP链接,就开启一个新线程来进行客户端与服务器的交互工作。
以保证所有的客户端来到的时候都能够及时地得到服务。
2.开发环境
操作系统:
Ubuntu10.10
编程语言:
ANSIC
编译器:
gcc3.3.2
3.程序详解
3.1服务器的基本功能
3.1.1验证登录功能
char*loginTest(int);
此函数用户验证用户是否合法,在此函数中维护一张用户信息列表,每接收到一个用户登录的信息时,首先该函数进行用户的密码解密工作,解密完成后,将用户名密码同时存在一个user结构体中,然后开始查表,比对,当找到了相应的用户后,给客户端发送一个登录成功的消息。
此函数返回值为一个char*数组,未保存当前用户名信息,这样当用户分享文件的时候,就可以把用户的用户名信息加到分享的文件信息中了。
3.1.2分享文件功能
intaddFiles(intconnectfd,message*operations,char*username,structsockaddr_inclient)
此函数实现的是分享文件功能,参数传递的是当前连接的id,connectfd,当前客户端发来的消息体,message*operations,当前用户的用户名,以及当前用户的sockaddr_in。
这样在封装分享消息的时候就可以得到这些必要的信息了。
此程序保存分享文件的结构是链表结构,也就是说每来一个分享的文件,我就新建立一个节点,并将其插入到当前已有链表的头部。
3.1.3查询文件功能
voidsearchFiles(intconnectfd,message*operations)
此函数用于实现查询功能,根据用户发来的消息,首先解析消息体,从消息体中截出文件名,并用该文件名去搜索文件链表,搜索没有使用什么快速算法,只是简单的从链表的头部开始一直搜索到链表的尾部。
没搜索到一条信息,就将该消息打包,发送给客户端,直到搜索结束,发送给用户“SRES”的信息,以便让用户确定搜索已经结束。
若没有搜索到一条信息,则返回“ERRO”信息,以便让用户直到没有搜索到想要的结果。
3.14退出登录功能
此部分没有单独作为一个函数列出来,而是合并在主函数中,如下所示:
elseif(strcmp(operations->type,"QUIT")==0)
{
filePa,b;
a=dataBase;
b=dataBase;
while(a->next!
=NULL)
{
a=a->next;
if(a->ipInfo.s_addr==client.sin_addr.s_addr&&strcmp(a->username,user1.username)==0)
b->next=a->next;
}
close(connectfd);
break;
}
此部分即是进行将用户分享的文件从文件列表中删除,判断条件是用户ip和用户名。
3.1.5主处理程序
void*process(void*b)
此函数为开启新线程的处理函数,也是进行处理这个流程的主要函数,此函数根据用户端发来的消息类型,决定是调用loginTest()来验证,还是调用AddFiles()来添加文件,还是searchFiles()来查询,亦或是退出。
此部分是程序执行的逻辑控制部分。
3.1.6主程序
实现端口绑定,开始监听。
如果用用户建立连接的话,开启新线程去处理。
处理函数及是上面的void*process(void*b)函数。
主线程继续进行监听的工作。
3.2客户端的基本功能
3.2.1登录功能
voidlogin(intsockfd)
此函数实现从输入流读入用户的账号密码信息,并将密码进行加密设置,然后将消息发送到服务器。
3.2.1其他功能
客户端的分享,查找,退出等基本功能我没有单独写函数来处理,而是在主函数中,以while循环的形式,不断的读入用户的指令,用户的指令包括有“LGIN”登录“SHRE”分享和“SRCH”查找和“QUIT”退出,如果用户输入其他无意义指令,程序会提醒用户所输入的指令不正确。
此部分实现的功能截图如下所示:
(图一)
(图二)
(图三)
(图四)
说明:
1.(图一)为用户laura正确登录后的情况,laura登录后进行了SHRE操作,即分享文件a.txt,b.txt,c.txt
2.(图二)为用户linda错误登录后的情况,由于密码出错,所以显示重新输入,正确输入后,linda执行SRCH操作,即查询文件,查询a.txt,服务器执行查询操作后,将结果返回,可从图二中看到返回的结果,显示当前有laura用户分享的a.txt文件,端口号为1234,ip为127.0.0.1等信息。
3.(图三)为laura退出后,linda再次执行查询操作,此时,服务器上不再有a.txt文件,所以返回结果显示“thefileisnotfound”。
4.(图四)为服务器端运行时显示的运行结果。
4.程序流程图
服务器端
N
Y
用户端
N
Y
5.程序源代码
服务器端程序代码:
#include
#include
#include
#include
#include
#include
#include
#include
#include
/*********************************定义常量********************************/
#definePORT3000//端口号
#defineBACKLOG1//服务器端处理的等待队列长度
#defineMAXDATASIZE100//发送的数据包长度
#defineNumOfUsers3//系统中存储的用户数目
/******************************自定义结构体******************************/
//此结构体为发送消息格式,type为消息类型,即为“LGIN”“SHRE”等等,mesBody
//保存消息信息,例如对于查询指令来说,其中存储的就是查询结果
typedefstructmessage{
chartype[5];
charmesBody[95];
}message;
//此结构体保存用户名和密码,用来从消息中解析用户信息,并保存在此结构体中
typedefstructus{
charusername[10];
charpassword[10];
}user;
//此结构体为保存在server上的用户分享的文件的信息,这里我将文件的相关信息都保//存在该结构体中,每当有用户进行分享时,就新建这样的一个结构体,保存,并加到
//已存在的链表中。
typedefstructfile{
charname[10];
structin_addripInfo;
charport[16];
charusername[10];
structfile*next;
}file,*fileP;
//此结构体保存着当前连接的用户的sockaddr_in信息和创建的连接字connectfd;将其
//保存在这个结构体中是为了一参数的形式传递到新建的线程中去。
typedefstructarg{
structsockaddr_inclient;
intconnectfd;
}arg;
//此结构体是为了保存当前连接的用户,重新定义这个结构体,是因为我的代码在以//char*传参数时,总有问题,总是无法得到我要的用户名,所以我不得不重新建了一个//结构体,来传递我想要的参数。
typedefstructuserTemp{
charusername[10];
}userTemp;
/******************************函数声明部分*****************************/
structuserTemploginTest(int);//登录验证函数
intaddFiles(int,message*,char*username,structsockaddr_inclient);//分享文件函数
voidsearchFiles(int,message*,structsockaddr_inclient);//查询文件函数
void*process(void*);//主处理函数
/****************************全局变量声明部分****************************/
file*dataBase;//分享文件链表的头指针
userusers[NumOfUsers];//server端“数据库”中保存的用户信息表
pthread_mutex_tmutex=PTHREAD_MUTEX_INITIALIZER;
//加锁,保护全局变量
/****************************main函数入口*******************************/
voidmain()
{
//往用户列表中添加用户信息
strcpy(&users[0].username,"laura");
strcpy(&users[0].password,"123");
strcpy(&users[1].username,"linda");
strcpy(&users[1].password,"123");
strcpy(&users[2].username,"Mike");
strcpy(&users[2].password,"123");
intlistenfd,confd;//监听时的返回值
structsockaddr_inserver;//server端的地址信息
structsockaddr_inclient0;//有用户来建立连接时,用户的地址信息
socklen_taddrlen;//sockaddr_in结构的长度
dataBase=(fileP)malloc(sizeof(file));//在主程序中首先给dataBase指针分配内存
dataBase->next=NULL;
if((listenfd=socket(AF_INET,SOCK_STREAM,0))==-1)
{
perror("socket()error.");
exit
(1);
}
intopt=SO_REUSEADDR;
setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
//绑定服务器端口号
bzero(&server,sizeof(server));charres[2];
server.sin_family=AF_INET;
server.sin_port=htons(PORT);
server.sin_addr.s_addr=htonl(INADDR_ANY);
if(bind(listenfd,(structsockaddr*)&server,sizeof(server))==-1)
{
perror("bind()error.");
exit
(1);
}
if(listen(listenfd,BACKLOG)==-1)
{
perror("listen()error.");
exit
(1);
}
addrlen=sizeof(client0);
while
(1)
{
//监听开始,如果监听成功,则开启新线程来处理客户端的请求
if((confd=accept(listenfd,(structsockaddr*)&client0,&addrlen))==-1)
{
perror("accept()error.");
exit
(1);
}
printf("thesin_portnumberis:
%d\n",client0.sin_port);
arg*arg1=(structarg*)malloc(sizeof(arg));
//将客户的地址信息和confd打包成结构体发送到服务器
arg1->client=client0;
arg1->connectfd=confd;
pthread_tthreadID;
if(pthread_create(&threadID,NULL,process,(void*)arg1))
{
perror("errorpthread_create");
exit
(1);
}
}
close(listenfd);
}
/*****************************登录验证函数******************************/
structuserTemploginTest(intconnectfd){
inti;
intn;//n用户端在进行密码加密时系统随机数n,根据n进行移位加密
message*loginMess;
chara[100];
usercurUser;
recv(connectfd,a,100,0);//接受登录信息
loginMess=(structmes*)a;//并将信息转换成自定义的消息结构体
if(strcmp(loginMess->type,"LGIN")==0)
{
for(i=0;i<10;i++)
{
if(loginMess->mesBody[i]=='\0')//逐个字符比对
{
curUser.username[i]='\0';//将用户信息保存在curUser结构体中
break;
}
else
curUser.username[i]=loginMess->mesBody[i];
//printf("theloginMessname:
%d\n",strlen(loginMess->mesBody));
}
n=(int)(loginMess->mesBody[20]-48);
//密码解密过程,逐个字符转换
for(i=10;i<20;i++)
{
if(loginMess->mesBody[i]=='\0')
{
curUser.password[i-10]='\0';//在char数组结尾加结束标志’\0’
break;
}
else
curUser.password[i-10]=(char)(loginMess->mesBody[i]-n);
}
}
printf("theclientistryingtoconnnect:
%s\n",curUser.username);
//printf("passwordis:
%s\n",curUser.password);
structuserTempuser1;
for(i=0;i { //printf("intheloop%d\n",i); if(strcmp(curUser.username,users[i].username)==0&&strcmp(curUser.password,users[i].password)==0) { strcpy(user1.username,curUser.username); returnuser1; } } strcpy(user1.username,"\0"); returnuser1; //此函数返回值是成功登录后用户的用户名 } /*****************************用户分享文件函数***************************/ intaddFiles(intconnectfd,message*operations,char*username,structsockaddr_inclient) { inti,j; //在分享文件时,在此程序中,设计的分享文件的信息前16位保存的是端口号, //然后保存的才是文件名,所以我先端口号之后,就可以用循环来实现一个一个的 //获取用户分享的文件名 for(i=16;i<(strlen(&operations->mesBody[16])+16);i++) { //此处进行加锁,因为要再全局变量dataBase上进行修改,为防止不同用户同时修 //改文件数据库,所以需要以锁机制来实现保护 pthread_mutex_lock(&mutex); filePrecvFile; recvFile=(fileP)malloc(sizeof(file)); j=0; strcpy(recvFile->username,username); strcpy(recvFile->port,operations->mesBody); recvFile->ipInfo=client.sin_addr; while(operations->mesBody[i]! ='#') { recvFile->name[j]=operations->mesBody[i]; i++; j++; } recvFile->name[j]='\0'; //将文件加到dataBase链表的头部 recvFile->next=dataBase->next; dataBase->next=recvFile; pthread_mutex_unlock(&mutex); } return1; } /**********************************查询函数******************************/ voidsearchFiles(intconnectfd,message*operations,structsockaddr_inclient) { //此函数参数部分operations中保存着查询的具体信息,即包含查询的文件名。 filePcurFile; charsearRes[100]; curFile=dataBase; charsearFileName[10]; strcpy(searFileName,operations->mesBody); intflag=0;//标志是否有文件,0表示无,1表示查询到了 while(curFile->next! =NULL) { //循环查询 curFile=curFile->next; if(strcmp(searFileName,curFile->name)==0) { flag=1; printf("filewasfound\n"); strcpy(searRes,"SHRE"); //查询到的结果保存在searRes字符数组中 strcpy(&searRes[5],curFile->name); strcpy(&searRes[15],curFile->username); strcpy(&searRes[25],inet_ntoa(curFile->ipInfo)); strcpy(&searRes[57],curFile->port); searRes[73]='\0'; //发送查询结果 send(connectfd,searRes,100,0); } } if(flag==0) { strcpy(searRes,"ERRO"); send(connectfd,searRes,100,0); } //查询结束,发送查询结束信息 elseif(flag==1) { strcpy(searRes,"SHOK"); send(connectfd,searRes,100,0); } } /**
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 计算机网络 编程 实验 报告
![提示](https://static.bingdoc.com/images/bang_tan.gif)