实验四 客户服务器通信文档格式.docx
- 文档编号:21988187
- 上传时间:2023-02-02
- 格式:DOCX
- 页数:14
- 大小:71.88KB
实验四 客户服务器通信文档格式.docx
《实验四 客户服务器通信文档格式.docx》由会员分享,可在线阅读,更多相关《实验四 客户服务器通信文档格式.docx(14页珍藏版)》请在冰豆网上搜索。
客户机/服务器模型工作时要求有一套为客户机和服务器所共识的协议,以保证服务能够被提供或被接收,它必须在通信的两端都被实现。
在非对称协议中,一方为主机(服务器),另一方则是从机(客户机)。
当服务被提供时必然存在“客户进程”和“服务进程”。
一个服务器通常在一个众所周知的端口监听对服务的请求。
也就是说,服务器一直处于休眠状态,直到一个客户对这个服务的端口提出连接请求。
在这个时刻,服务程序被唤醒并且为客户提供服务,对客户的请求做出了适当的反应。
其流程见图1。
例1:
服务器端程序通过一个连接向客户发送字符串"
\n”。
在PC机上运行服务器端程序,在开发板上运行客户端程序并输入服务器的IP地址,则开发板的LCD屏上能显示该字符串。
服务器端发送程序host.c:
#include<
stdio.h>
stdlib.h>
errno.h>
string.h>
sys/types.h>
netinet/in.h>
sys/wait.h>
sys/socket.h>
ctype.h>
#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)/*绑定*/
bindB"
/*绑定失败*/
if(listen(sock_fd,BACKLOG)==−1)/*监听端口是否有请求*/
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);
}
close(new_fd);
/*父进程不再需要该socket*/
while(waitpid(−1,NULL,WNOHANG)>
0);
/*等待子进程结束,清除子进程所占用资源*/
return0;
服务器首先创建一个socket,然后将该socket与本地地址/端口号捆绑,成功之后就在相应的socket上监听,当accpet捕捉到一个连接服务请求时,就生成一个新的socket,并调用fork()函数产生一个子进程与客户机通信。
该子进程处理数据传输部分,通过这个新的socket向客户端发送字符串"
\n"
,然后关闭该socket。
fork()函数语句是一个单调用双返回的函数。
若调用成功,在子进程中返回的值为0,在父进程中返回子进程的进程标识号;
若调用失败,则返回−1。
包含fork函数的if语句是子进程代码部分,它与if语句后面的父进程代码部分是并发执行的。
客户端接收程序ethernet.c:
unistd.h>
#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;
4;
/*循环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];
structsockaddr_inservaddr;
if(!
aiptoi(argv[1],&
uiip)||argc<
=1)/*检查IP地址格式是否正确及IP是否输入*/
theipisnotcorrectorhavenotinputtheip!
return0;
if((sockfd=socket(AF_INET,SOCK_STREAM,0))==−1)/*建立流式套接字描述符*/
/*给定主机信息*/
servaddr.sin_family=AF_INET;
servaddr.sin_port=htons(PORT);
(servaddr.sin_zero),8);
servaddr.sin_addr.s_addr=uiip;
if(connect(sockfd,(structsockaddr*)&
servaddr,sizeof(structsockaddr))==−1)/*建立连接*/
Can'
tconnecttotheserver!
initgraph();
/*初始化显示环境*/
if((numbytes=recv(sockfd,buf,MAXDATASIZE,0))==−1)/*接收服务器传送过来的字符串*/
recv"
buf[numbytes]='
\0'
;
clearscreen();
/*清屏*/
textout(0,0,buf,0xffff,0x1111);
/*显示字符串*/
delay();
clearscreen();
close(sockfd);
函数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所示。
sys/stat.h>
intmain()
{intsock_fd,new_fd;
charfilename[20];
/*存放要传送文件的文件名*/
FILE*fp;
charszsendbuf[1024],head[8];
/*发送数据缓冲区大小为1K*/
intnsize,allsize=0;
int*phead=head+4;
if((sock_fd=socket(AF_INET,SOCK_STREAM,0))==−1)/*建立流式套接字描述符/
/*服务器的端口号3000*/
if(listen(sock_fd,BACKLOG)==−1)/*监听端口是否有请求*/
{sin_size=sizeof(structsockaddr_in);
if((new_fd=accept(sock_fd,(structsockaddr*)&
sin_size))==−1)
{perror("
continue;
read(new_fd,filename,20);
/*从端口读文件名*/
printf("
%s\n"
filename);
if((fp=fopen(filename,"
r"
))==NULL)/*打开文件*/
{printf("
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("
tsenddata!
close(new_fd);
fclose(fp);
close(sock_fd);
字符串的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:
#definePORT3000
#defineMax60000/*接收文件的最大字节数*/
voiddelay()
{inti,j;
j++){}
for(i=0,psztmp1=psziphere,pchar=(char*)piip;
if((psztmp2=strstr(psztmp1,"
))==NULL)return0;
psztmp2[0]=0;
*(pchar+i)=atoi(psztmp1);
psztmp1=psztmp2+1;
intmain(intargc,char**argv)
intsockfd;
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)/*建立流式套接字描述符/
myaddr.sin_family=AF_INET;
myaddr.sin_port=htons(PORT);
(myaddr.sin_zero),8);
=1)
theipisnotcorrectorhavenotinputtheip!
myaddr.sin_addr.s_addr=uiip;
if(connect(sockfd,(structsockaddr*)&
myaddr,sizeof(structsockaddr)))
/*初始化显示环境*/
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文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 实验四 客户服务器通信 实验 客户 服务器 通信