计算机网络原理实验三.doc
《计算机网络原理实验三.doc》由会员分享,可在线阅读,更多相关《计算机网络原理实验三.doc(6页珍藏版)》请在冰点文库上搜索。
实验三、套接字编程
1.实验目的:
两人一组,编写一个客户端、服务器程序,掌握Socket编程原理。
2.实验环境:
连入局域网络的主机一台。
3.实验指导:
SocketAPI是实现进程间通信的一种编程设施,也是一种为进程间提供底层抽象的机制。
理解socket编程模型及其原理。
4.实验分析,回答下列问题。
a.运行指导书中的程序,并修改服务器的功能(自己思考),改写成多线程web服务器(选作),附上源代码,并加关键代码注释。
服务器:
#include"stdafx.h"
#include
#include
#include
#include
#pragmacomment(lib,"ws2_32.lib")
#defineMYPORT3490/*定义用户连接端口*/
#defineBACKLOG10/*多少等待连接控制*/
#defineSERVER_IP_ADDR"113.55.34.41"/*服务器的IP地址*/
int_tmain(intargc,_TCHAR*argv[])
{
SOCKETsock,msgsock;
#definePORT3490
intlength=0;
structsockaddr_inserver;
structsockaddrtcpaddr;
charbuf[1024]="";
intrval=0,len=0,err=0,i;//用于循环
WORDwVersionRequested;
WSADATAwsaData;
wVersionRequested=MAKEWORD(2,2);
err=WSAStartup(wVersionRequested,&wsaData);
if(err!
=0)
return-1;
sock=socket(AF_INET,SOCK_STREAM,0);
if(sock<0)
{
perror("openingstreamsocket");
exit
(1);
}
server.sin_family=AF_INET;
server.sin_port=htons(MYPORT);
server.sin_addr.s_addr=inet_addr(SERVER_IP_ADDR);
memset(server.sin_zero,0,sizeof(server.sin_zero));
rval=bind(sock,(structsockaddr*)&server,sizeof(server));
if(rval<0)
{
perror("bindingstreamsocket");
exit
(1);
}
length=sizeof(server);
if(getsockname(sock,(structsockaddr*)&server,&length)<0)
{
perror("gettingsocketname");
exit
(1);
}
printf("socketport#%d\n",ntohs(server.sin_port));
listen(sock,5);
len=sizeof(structsockaddr);
do
{msgsock=accept(sock,(structsockaddr*)&tcpaddr,(int*)&len);
if(msgsock==-1)
perror("accept");
else
{
for(i=0;i<=1000;i++)//循环控制
{
memset(buf,0,sizeof(buf));
if((rval=recv(msgsock,buf,sizeof(buf),0)<0))
perror("readingstreammessage");
if(rval==0)
printf("%s\n",buf);
}
}
closesocket(msgsock);
}while(TRUE);
closesocket(msgsock);
return0;
}
客户端:
#include"stdafx.h"
#include
#include
#include
#include
#pragmacomment(lib,"ws2_32.lib")
#definePORT3490/*客户机连接远程主机的端口*/
#defineMAXDATASIZE100/*每次可以接收的最大字节*/
int_tmain(intargc,_TCHAR*argv[])
{
WORDwVersionRequested;
WSADATAwsaData;
interr=0,rval=0,i;
SOCKETfd;
structsockaddr_inservaddr;
structhostent*hp;
charbuf[1024]="";
wVersionRequested=MAKEWORD(2,2);
err=WSAStartup(wVersionRequested,&wsaData);
if(err!
=0)
return-1;
if((fd=socket(AF_INET,SOCK_STREAM,0))<0)
{
printf("Cannotcreatesocket!
");
exit
(2);
}
servaddr.sin_family=AF_INET;
servaddr.sin_port=htons(PORT);
servaddr.sin_addr.S_un.S_addr=inet_addr("113.55.34.41");
memset(servaddr.sin_zero,0,sizeof(servaddr.sin_zero));
rval=connect(fd,(sockaddr*)&servaddr,sizeof(servaddr));
if(rval<0)
{
printf("Cannotcreateconnect!
");
exit(3);
}
else
{
for(i=0;i<=1000;i++)//循环控制
{
memset(buf,0,1024);
scanf("%s",&buf);
rval=send(fd,buf,strlen(buf)+1,0);
if(rval<0)
printf("Writeerror!
");
}
}
closesocket(fd);
exit(5);
return0;
}
b.给出程序调试和运行的截图。
c.回答下列问题:
(1)为什么在服务器和客户端要包含winsock2.h文件?
答:
用Winsock2API,这样有助于对异步、非阻塞Socket编程机制的理解。
为了简单起见,服务器端和客户端的应用程序均是基于MFC的标准对话框,网络通信部分基于Winsock2API实现。
(2)为什么在服务器和客户端程序中要加入#pragmacomment(lib,"ws2_32.lib")语句,如果不加会出现什么问题?
答:
用该语句,指示编译当前单元时,linker会优先链接该preproccessor指定的lib文件,这样等于就告诉编译器有这样一个lib文件可以去链接。
如果不加就会出现类似“LNK2019:
无法解析的外部符号__imp__closesocket@4,该符号在函数_wmain中被引用”的错误。
(3)为什么在服务器和客户端程序中要使用WSAStartup函数,如果不用程序会有什么问题?
答:
使用Winsock库函数之前,必须先调用函数WSAStartup,该函数负责初始化动态连接库Ws2_32.dll.若ws2_32.dll尚未初始化,是无法调用WSAGetLastError.WSAStartup是任何使用Winsock的应用程序或者DLL首先必须调用Winsock库函数.一方面它初始化ws2_32.dll,另一方面他用于在应该程序DLL与系统Winsock库版本协商。
所以,如果不用该函数程序将无法按照正常的情况进行。
(4)如果程序在TurboC环境下运行,在服务器和客户程序中还需要上面的内容吗?
答:
不需要。
(5)如果服务器程序中没有memset(server.sin_zero,0,sizeof(server.sin_zero));语句,程序会出现什么错误,为什么?
答:
此函数是为新申请的内存做初始化工作。
如果去掉该语句在理论上面应该有错误,但是在我的实际操作中却没有错误,这个问题在我将继续深究。
(6)如果先运行客户端程序,程序会有什么现象,为什么会有这一现象?
答:
无法连接。
因为如果先运行客户端的话,没有服务器端等待,那么服务器端艰苦肯定无法给予响应,所以也就无法连接。
(7)如果服务器程序所在计算机没有连接网络,程序会发生什么错误?
我们捕获到什么错误信息?
答:
无法连接。
会出现报错。
(8)上述服务器是串行处理多个客户端的请求,如何该成并发处理的服务器?
答:
用父进程调用accept,然后调用fork,这样,已连接套接口就在父进程与子进程间共享,一般来说,接下来便是子进程读、写已连接套接口并关闭侦听套接口,而父进程则关闭已连接套接口。
5.实验环境:
提供的Socket程序在visualc++2008中的设置
(1)创建工程
(2)在应用程序开发中选择预编译头
(3)源程序有如下改变,winsock2.hwindows.h头文件顺序要改变
#include
#include