实验四 客户服务器通信.docx
- 文档编号:30635304
- 上传时间:2023-08-18
- 格式:DOCX
- 页数:18
- 大小:477.85KB
实验四 客户服务器通信.docx
《实验四 客户服务器通信.docx》由会员分享,可在线阅读,更多相关《实验四 客户服务器通信.docx(18页珍藏版)》请在冰豆网上搜索。
实验四客户服务器通信
实验四客户/服务器通信实验
一、实验目的
1.学习Linux的网络编程的基本知识
2.理解socket结构和机制
3.编写简单客户/服务器通信程序
二、实验内容
1.了解Linux的网络编程的基本知识:
TCP/IP协议,寻址机制,客户/服务器通信机制;
2.理解端口的概念,熟悉socket有关的编程结构和函数,比如:
socket(),bind(),connect(),listen(),accept(),send(),recv(),close();
3.自己编写ip2uint()函数,把IP地址转换为unsignedint格式;
4.参考附录中的源文件,在两个虚拟控制台分别实现分别服务器端和客户端功能,实现以下功能:
1)服务器端程序通过一个连接向客户端发送字符串"Hello,world!
\n”,画出客户端程序和服务器端程序的流程图;
2)服务器端程序通过一个连接向客户端发送由客户端指定的文件,画出客户端程序和服务器端程序的流程图;
5.在虚拟控制台分别编译、调试程序;
三、实验指导与步骤
按照以下步骤分别实现功能1和功能2:
1、首先编写好服务器和客户端程序;
2、打开一个虚拟终端,用gcc编译预先写好的服务器和客户端程序;
3、运行服务器程序;
4、打开另一个虚拟终端,运行客户端程序,连接成功后服务器给客户端发送数据;
四、实验报告要求
1.实验目的
2.实验内容
3.实验步骤
记录自己实际完成的步骤,实验过程中所碰到的难题以及你解决问题的步骤和方法;
4.实验技巧和心得体会
附录:
简单的客户/服务器通信示例
一个建立分布式应用时最常用的范例便是客户机/服务器模型。
在这种方案中,客户应用程序向服务器程序请求服务,这种方式隐含了在建立客户机/服务器间通信的非对称性。
客户机/服务器模型工作时要求有一套为客户机和服务器所共识的协议,以保证服务能够被提供或被接收,它必须在通信的两端都被实现。
在非对称协议中,一方为主机(服务器),另一方则是从机(客户机)。
当服务被提供时必然存在“客户进程”和“服务进程”。
一个服务器通常在一个众所周知的端口监听对服务的请求。
也就是说,服务器一直处于休眠状态,直到一个客户对这个服务的端口提出连接请求。
在这个时刻,服务程序被唤醒并且为客户提供服务,对客户的请求做出了适当的反应。
其流程见图1。
例1:
服务器端程序通过一个连接向客户发送字符串"Hello,world!
\n”。
在PC机上运行服务器端程序,在开发板上运行客户端程序并输入服务器的IP地址,则开发板的LCD屏上能显示该字符串。
服务器端发送程序host.c:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#defineMYPORT3000/*定义服务器的监听端口*/
#defineMax100/*定义了服务器一次可以发送的字符数目*/
#defineBACKLOG10/*BACKLOG指定在请求队列中允许的最大请求数,进入的连接请求将在队列中等待accept()函数接受它们*/
main()
{
intsock_fd,new_fd,numbytes,i;/*sock_fd,new_fd是套接字描述*/
charbuf[Max];/*发送数据的缓冲区*/
structsockaddr_inmy_addr;/*服务器的地址结构体*/
structsockaddr_intheir_addr;/*主机的地址结构体*/
intsin_size;
if((sock_fd=socket(AF_INET,SOCK_STREAM,0))==−1)/*建立流式套接字描述符*/
{perror("socket");
exit
(1);
}
/*服务器结构体的地址赋初值*/
my_addr.sin_family=AF_INET;
my_addr.sin_port=htons(MYPORT);/*服务器的端口号*/
my_addr.sin_addr.s_addr=INADDR_ANY;
bzero(&(my_addr.sin_zero),8);/*填充0,凑齐长度*/
if(bind(sock_fd,(structsockaddr*)&my_addr,sizeof(structsockaddr))==−1)/*绑定*/
{perror("bindB");/*绑定失败*/
exit
(1);
}
if(listen(sock_fd,BACKLOG)==−1)/*监听端口是否有请求*/
{perror("listen");/*监听失败*/
exit
(1);
}
while
(1)
{
sin_size=sizeof(structsockaddr_in);
if((new_fd=accept(sock_fd,(structsockaddr*)&their_addr,&sin_size))==−1)
{perror("accept");
continue;
}
printf("server:
gotconnectionfrom%s\n",inet_ntoa(their_addr.sin_addr));
if(!
fork())/*子进程代码段:
创建一个子进程,用来处理与刚建立的套接字的通信*/
{if(send(new_fd,"Hello,World!
\n",14,0)==−1)/*发送字符串*/
{perror("send");
close(new_fd);
exit
(1);
}
close(new_fd);/*父进程不再需要该socket*/
}
}
while(waitpid(−1,NULL,WNOHANG)>0);/*等待子进程结束,清除子进程所占用资源*/
return0;
}
服务器首先创建一个socket,然后将该socket与本地地址/端口号捆绑,成功之后就在相应的socket上监听,当accpet捕捉到一个连接服务请求时,就生成一个新的socket,并调用fork()函数产生一个子进程与客户机通信。
该子进程处理数据传输部分,通过这个新的socket向客户端发送字符串"Hello,world!
\n",然后关闭该socket。
fork()函数语句是一个单调用双返回的函数。
若调用成功,在子进程中返回的值为0,在父进程中返回子进程的进程标识号;若调用失败,则返回−1。
包含fork函数的if语句是子进程代码部分,它与if语句后面的父进程代码部分是并发执行的。
客户端接收程序ethernet.c:
#include
#include
#include
#include
#include
#include
#include
#include"../gui/gui.h"/*用于LCD屏的显示*/
#definePORT3000/*定义连接到服务器的端口号*/
#defineMAXDATASIZE100/*客户机一次可接收的最大传输量*/
/*延时程序,用于LCD屏的显示*/
voiddelay()
{inti,j;
for(i=0;i<4500;i++)
for(j=0;j<4000;j++){}
}
/*将命令行输入的字符串IP地址转换成connect函数可识别的整数uiip*/
intaiptoi(char*pszip,unsignedint*piip)
{
charpsziphere[17],*psztmp1,*psztmp2,*pchar;/*定义指针*/
inti;
bzero(psziphere,17);/*清空将要进行操作的数组*/
strcpy(psziphere,pszip);/*将要转换的IP地址存入该数组*/
strcat(psziphere,".");/*在IP地址串的末尾加“·”*/
for(i=0,psztmp1=psziphere,pchar=(char)piip;i<4;i++)
/*循环4次,将“·”转变成0,并将字符串型转换成整型*/
{
if((psztmp2=strstr(psztmp1,"."))==NULL)/*psztmp2返回指向字符“·”位置的指针*/
return0;
psztmp2[0]=0;
(pchar+i)=atoi(psztmp1);/*调用atoi()函数,将字符串转换成整数*/
psztmp1=psztmp2+1;/*指针psatmp1移到下一段的开始*/
}
return1;
}
intmain(intargc,char**argv)
{
intsockfd,numbytes;
unsignedintuiip;
charbuf[MAXDATASIZE];
inti;
structsockaddr_inservaddr;
if(!
aiptoi(argv[1],&uiip)||argc<=1)/*检查IP地址格式是否正确及IP是否输入*/
{
printf("theipisnotcorrectorhavenotinputtheip!
\n");
return0;
}
if((sockfd=socket(AF_INET,SOCK_STREAM,0))==−1)/*建立流式套接字描述符*/
{perror("socket");
exit
(1);
}
/*给定主机信息*/
servaddr.sin_family=AF_INET;
servaddr.sin_port=htons(PORT);
bzero(&(servaddr.sin_zero),8);
servaddr.sin_addr.s_addr=uiip;
if(connect(sockfd,(structsockaddr*)&servaddr,sizeof(structsockaddr))==−1)/*建立连接*/
{
printf("Can'tconnecttotheserver!
\n");
return0;
}
initgraph();/*初始化显示环境*/
if((numbytes=recv(sockfd,buf,MAXDATASIZE,0))==−1)/*接收服务器传送过来的字符串*/
{perror("recv");
exit
(1);
}
buf[numbytes]='\0';
clearscreen();/*清屏*/
textout(0,0,buf,0xffff,0x1111);/*显示字符串*/
delay();
clearscreen();
close(sockfd);
return0;
}
函数aiptoi()处理的是字符串型的IP地址。
首先将该字符串存放入psziphere[]数组中,“psztmp1=psziphere”语句将字符串的首地址赋给psztmp1。
为了便于使用循环,调用strcat(psziphere,"."),将符号“·”添加到字符串尾部。
接着循环开始,先找到第一个“·”的位置,psztmp2指向该处,并将该处赋为0(作为字符串的串结束符),调用函数atoi(psztmp1),此时psztmp1指向的是psziphere[]数组的首地址,该函数将psztmp1指针所指处到其后面字符串串结束符处(即psztmp2处)的字符串转换成整型数,并存放到pchar[]数组中。
然后,将psztmp1指针指向psztmp2所指的下一个字符,准备开始下一个循环。
以IP地址“192.168.2.100”为例,第一个循环中,psztmp1指向了“192.”的“1”处,而psztmp2指向了“192.168”的“·”处,并将该串改为“196\0168”;atoi(psztmp1)函数将该串转换成整数192;然后psztmp1指向了字符串“196\0168”中的“1”;经过四次循环,用“·”分开的四段字符串就可以转换成整数了。
客户端代码相对来说要简单一些,首先通过命令行得到服务器的IP地址,然后创建一个socket,调用connect函数与服务器建立连接,连接成功之后接收从服务器发送过来的数据,最后关闭socket,结束程序。
无连接的客户/服务器程序的在原理上和连接的客户/服务器是一样的,两者的区别在于无连接的客户/服务器中的客户一般不需要建立连接,而且在发送接收数据时,需要指定远端机的地址。
例2:
在PC机上运行一个发送程序,将一文件(图片或文本文件)通过网口传送到开发板,并在LCD上显示该文件。
该程序的流程如图2所示。
服务器端发送程序host.c:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#defineMYPORT3000/*定义服务器的监听端口*/
#defineBACKLOG10/*BACKLOG指定在请求队列中允许的最大请求数,进入的连接请求将在队列中等待accept()函数接受它们*/
intmain()
{intsock_fd,new_fd;/*sock_fd,new_fd是套接字描述*/
charfilename[20];/*存放要传送文件的文件名*/
structsockaddr_inmy_addr;/*服务器的地址结构体*/
structsockaddr_intheir_addr;/*主机的地址结构体*/
intsin_size;
FILE*fp;
charszsendbuf[1024],head[8];/*发送数据缓冲区大小为1K*/
intnsize,allsize=0;;
int*phead=head+4;
if((sock_fd=socket(AF_INET,SOCK_STREAM,0))==−1)/*建立流式套接字描述符/
{perror("socket");
exit
(1);
}
/*服务器结构体的地址赋初值*/
my_addr.sin_family=AF_INET;
my_addr.sin_port=htons(MYPORT);/*服务器的端口号3000*/
my_addr.sin_addr.s_addr=INADDR_ANY;
bzero(&(my_addr.sin_zero),8);/*填充0,凑齐长度*/
if(bind(sock_fd,(structsockaddr*)&my_addr,sizeof(structsockaddr))==−1)/*绑定*/
{perror("bindB");/*绑定失败*/
exit
(1);
}
if(listen(sock_fd,BACKLOG)==−1)/*监听端口是否有请求*/
{perror("listen");/*监听失败*/
exit
(1);
}
while
(1)
{sin_size=sizeof(structsockaddr_in);
if((new_fd=accept(sock_fd,(structsockaddr*)&their_addr,&sin_size))==−1)
{perror("accept");
continue;
}
printf("server:
gotconnectionfrom%s\n",inet_ntoa(their_addr.sin_addr));
read(new_fd,filename,20);/*从端口读文件名*/
printf("%s\n",filename);
if((fp=fopen(filename,"r"))==NULL)/*打开文件*/
{printf("Can'tfindthefile");
exit
(1);
}
nsize=1024;
allsize=0;
/*每次从文件读取1024个字节发送出去,若读出少于1024个字节,则认为已到达文件末尾*/
while(nsize==1024)
{bzero(szsendbuf,1024);/*清空缓冲区*/
/*从文件中读取并发送到缓冲区,填写通信头的数据长度*/
nsize=*phead=fread(szsendbuf,1,1024,fp);
write(new_fd,head,8);/*发送协议头*/
nsize=write(new_fd,szsendbuf,nsize);/*发送数据*/
allsize+=nsize;/*统计发送字节数*/
if(allsize)/*每发送一次,打印当前发送信息*/
printf("nowsize:
%dthistime%dtimes:
%d\n",allsize,nsize,allsize/1024);
if(nsize<=0)/*若发送完毕,退出*/
{printf("Can'tsenddata!
\n");
return0;
}
}
close(new_fd);
fclose(fp);
}
close(sock_fd);
return0;
}
字符串的IP和32位整型IP的转换,由函数inet_aton()和inet_ntoa()完成。
函数原型:
intinet_aton(constchar*cp,structin_addr*inp);
char*inet_ntoa(structin_addrin);
函数里面a代表ascii,n代表network。
第一个函数表示将a.b.c.d的IP地址转换为32位的整型IP,由inp指针指向;第二个是将32位整型IP转换为a.b.c.d的格式。
客户端接收程序file.c:
#include
#include
#include
#include
#include
#include
#include
#include"../gui/gui.h"
#definePORT3000
#defineMax60000/*接收文件的最大字节数*/
voiddelay()
{inti,j;
for(i=0;i<4500;i++)
for(j=0;j<4000;j++){}
}
intaiptoi(char*pszip,unsignedint*piip)
{
charpsziphere[17],*psztmp1,*psztmp2,*pchar;
inti;
bzero(psziphere,17);
strcpy(psziphere,pszip);
strcat(psziphere,".");
for(i=0,psztmp1=psziphere,pchar=(char*)piip;i<4;i++)
{
if((psztmp2=strstr(psztmp1,"."))==NULL)return0;
psztmp2[0]=0;
*(pchar+i)=atoi(psztmp1);
psztmp1=psztmp2+1;
}
return1;
}
intmain(intargc,char**argv)
{
intsockfd;
unsignedintuiip;
charbuf[MAX],*bmpbuf;
structsockaddr_inmyddr;
FILE*fp;
charszrecvbuf[1024]/*一次接收数据缓冲区大小为1K*/
charhead[8],*filename=”test.bmp”;/*filename指向要传送文件的文件名*/
int*phead=head+4,nsize=1024,allsize=0,i;
if((sockfd=socket(AF_INET,SOCK_STREAM,0))==−1)/*建立流式套接字描述符/
{perror("socket");
exit
(1);
}
/*给定主机信息*/
myaddr.sin_family=AF_INET;
myaddr.sin_port=htons(PORT);
bzero(&(myaddr.sin_zero),8);
if(!
aiptoi(argv[1],&uiip)||argc<=1)
{
printf("theipisnotcorrectorhavenotinputtheip!
\n");
return0;
}
myaddr.sin_addr.s_addr=uiip;
if(connect(sockfd,(structsockaddr*)&myaddr,sizeof(structsockaddr)))
{
printf("Can'tconnecttotheserver!
\n");
return0;
}
initgraph();/*初始化显示环境*/
bmpbuf=(char*)malloc(Max);/*申请空间,存放整个接收文件*/
bzero(bmpbuf,Max);/*清空缓冲区*/
write(sockfd,filename,10);/*将文件名发送给服务器*/
/*每次接收1024个字节,若接收的数据少于1024个字节,则认为已接收完毕*/
while(nsize==1024)
{bzero(szrecvbuf,1024);/*清空缓冲区*/
read(sockfd,head,8);/*接收协议头*/
nsize=read(sockfd,szrecvbuf,1024);/*接收数据*/
/*将接收的数据存入bmpbuf缓冲区*/
strcat(bmpbuf+allsize,szrec
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 实验四 客户服务器通信 实验 客户 服务器 通信