实验四 客户服务器通信.docx

上传人:b****7 文档编号:8852030 上传时间:2023-02-02 格式:DOCX 页数:14 大小:71.88KB
下载 相关 举报
实验四 客户服务器通信.docx_第1页
第1页 / 共14页
实验四 客户服务器通信.docx_第2页
第2页 / 共14页
实验四 客户服务器通信.docx_第3页
第3页 / 共14页
实验四 客户服务器通信.docx_第4页
第4页 / 共14页
实验四 客户服务器通信.docx_第5页
第5页 / 共14页
点击查看更多>>
下载资源
资源描述

实验四 客户服务器通信.docx

《实验四 客户服务器通信.docx》由会员分享,可在线阅读,更多相关《实验四 客户服务器通信.docx(14页珍藏版)》请在冰豆网上搜索。

实验四 客户服务器通信.docx

实验四客户服务器通信

实验四客户/服务器通信实验

一、实验目的

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

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 高等教育 > 农学

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1