socket套接字实验报告.docx

上传人:b****2 文档编号:24312565 上传时间:2023-05-26 格式:DOCX 页数:13 大小:58.93KB
下载 相关 举报
socket套接字实验报告.docx_第1页
第1页 / 共13页
socket套接字实验报告.docx_第2页
第2页 / 共13页
socket套接字实验报告.docx_第3页
第3页 / 共13页
socket套接字实验报告.docx_第4页
第4页 / 共13页
socket套接字实验报告.docx_第5页
第5页 / 共13页
点击查看更多>>
下载资源
资源描述

socket套接字实验报告.docx

《socket套接字实验报告.docx》由会员分享,可在线阅读,更多相关《socket套接字实验报告.docx(13页珍藏版)》请在冰豆网上搜索。

socket套接字实验报告.docx

socket套接字实验报告

甘肃政法学院

本科生实验报告

()

姓名:

学院:

计算机科学院

专业:

计算机科学与技术

班级:

09级计本一班

实验课程名称:

TCP/IP协议与实现

实验日期:

年月日

指导教师及职称:

实验成绩:

开课时间:

学年学期

 

甘肃政法学院实验管理中心印制

 

实验题目

基于socket套接字编程

小组合作

姓名

班级

09计本一班

学号

一、实验目的

1、学习在vc++6.0环境下用winsock实现网络通信的程序设计;

2、进一步掌握网络通信的原理。

二.实验环境

windowsxp操作系统的个人pc机,vc++6.0

三、实验内容与步骤

客户端程序:

#include

#include

usingnamespacestd;

#pragmacomment(lib,"ws2_32.lib")

voidmain()

{

WORDwVersionRequested;

WSADATAwsaData;

interr;

wVersionRequested=MAKEWORD(2,2);

err=WSAStartup(wVersionRequested,&wsaData);

if(err!

=0){

return;

}

if(LOBYTE(wsaData.wVersion)!

=2||

HIBYTE(wsaData.wVersion)!

=2){

WSACleanup();

return;

}

SOCKETsock=socket(AF_INET,SOCK_STREAM,0);

if(sock==INVALID_SOCKET)

{

cout<<"socketfailedwitherrcode="<

return;

}

sockaddr_inservaddr;

memset(&servaddr,0,sizeof(sockaddr_in));

servaddr.sin_family=AF_INET;

servaddr.sin_port=htons(5555);

servaddr.sin_addr.s_addr=inet_addr("127.0.0.1");

intret=connect(sock,(sockaddr*)&servaddr,sizeof(servaddr));

if(ret==SOCKET_ERROR)

{

cout<<"connectfailedwitherrcode="<

return;

}

charrecvbuf[1024]={0};

recv(sock,recvbuf,1024,0);

cout<

charsendbuf[1024]={0};

while

(1)

{

memset(sendbuf,0,sizeof(sendbuf));

memset(recvbuf,0,sizeof(recvbuf));

cin>>sendbuf;

send(sock,sendbuf,strlen(sendbuf),0);

recv(sock,recvbuf,1024,0);

cout<

}

}

服务器端程序:

#include

#include

usingnamespacestd;

#pragmacomment(lib,"ws2_32.lib")

voidmain()

{

WORDwVersionRequested;

WSADATAwsaData;

interr;

wVersionRequested=MAKEWORD(2,2);

err=WSAStartup(wVersionRequested,&wsaData);

if(err!

=0){

return;

}

if(LOBYTE(wsaData.wVersion)!

=2||

HIBYTE(wsaData.wVersion)!

=2){

WSACleanup();

return;

}

SOCKETlistenfd=socket(AF_INET,SOCK_STREAM,0);

if(listenfd==INVALID_SOCKET)

{

cout<<"socketfailedwitherrcode="<

return;

}

sockaddr_inservaddr;

memset(&servaddr,0,sizeof(sockaddr_in));

servaddr.sin_family=AF_INET;

servaddr.sin_port=htons(5555);

servaddr.sin_addr.s_addr=htonl(INADDR_ANY);

//servaddr.sin_addr.s_addr=inet_addr("127.0.0.1");

intopt=1;

intret=setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,(constchar*)&opt,sizeof(opt));

if(ret==SOCKET_ERROR)

{

cout<<"setsockoptfailedwitherrcode="<

return;

}

ret=bind(listenfd,(sockaddr*)&servaddr,sizeof(servaddr));

if(ret==SOCKET_ERROR)

{

cout<<"bindfailedwitherrcode="<

return;

}

ret=listen(listenfd,10);

if(ret==SOCKET_ERROR)

{

cout<<"listenfailedwitherrcode="<

return;

}

sockaddr_inpeeraddr;

intlen=sizeof(sockaddr_in);

//while

(1)

//{

SOCKETconnSocket=accept(listenfd,(sockaddr*)&peeraddr,&len);

if(connSocket==INVALID_SOCKET)

{

cout<<"acceptfailedwitherrcode="<

return;

}

//开辟一个线程

cout<<"peerip=["<

send(connSocket,"welcometoby",strlen("welcometoby"),0);

charbuf[1024];

while

(1)

{

memset(buf,0,sizeof(buf));

recv(connSocket,buf,1024,0);

cout<

send(connSocket,buf,strlen(buf),0);

}

}

实验结果截图:

图一:

客户端与服务器端的启动

图二:

服务器端接受客户端发送的的信息

四、实验过程与分析

服务器,使用ServerSocket监听指定的端口,端口可以随意指定(由于1024以下的端口通常属于保留端口,在一些操作系统中不可以随意使用,所以建议使用大于1024的端口),等待客户连接请求,客户连接后,会话产生;在完成会话后,关闭连接。

客户端,使用Socket对网络上某一个服务器的某一个端口发出连接请求,一旦连接成功,打开会话;会话完成后,关闭Socket。

客户端不需要指定打开的端口,通常临时的、动态的分配一个1024以上的端口。

--winAPIsocket

本文所谈到的Socket函数如果没有特别说明,都是指的WindowsSocketAPI。

一、WSAStartup函数

intWSAStartup(

WORDwVersionRequested,

LPWSADATAlpWSAData

);

使用Socket的程序在使用Socket之前必须调用WSAStartup函数。

该函数的第一个参数指明程序请求使用的Socket版本,其中高位字节指明副版本、低位字节指明主版本;操作系统利用第二个参数返回请求的Socket的版本信息。

当一个应用程序调用WSAStartup函数时,操作系统根据请求的Socket版本来搜索相应的Socket库,然后绑定找到的Socket库到该应用程序中。

以后应用程序就可以调用所请求的Socket库中的其它Socket函数了。

该函数执行成功后返回0。

例:

假如一个程序要使用2.1版本的Socket,那么程序代码如下

wVersionRequested=MAKEWORD(2,1);

err=WSAStartup(wVersionRequested,&wsaData);

二、WSACleanup函数

intWSACleanup(void);

应用程序在完成对请求的Socket库的使用后,要调用WSACleanup函数来解除与Socket库的绑定并且释放Socket库所占用的系统资源。

三、socket函数

SOCKETsocket(

intaf,

inttype,

intprotocol

);

应用程序调用socket函数来创建一个能够进行网络通信的套接字。

第一个参数指定应用程序使用的通信协议的协议族,对于TCP/IP协议族,该参数置PF_INET;第二个参数指定要创建的套接字类型,流套接字类型为SOCK_STREAM、数据报套接字类型为SOCK_DGRAM;第三个参数指定应用程序所使用的通信协议。

该函数如果调用成功就返回新创建的套接字的描述符,如果失败就返回INVALID_SOCKET。

套接字描述符是一个整数类型的值。

每个进程的进程空间里都有一个套接字描述符表,该表中存放着套接字描述符和套接字数据结构的对应关系。

该表中有一个字段存放新创建的套接字的描述符,另一个字段存放套接字数据结构的地址,因此根据套接字描述符就可以找到其对应的套接字数据结构。

每个进程在自己的进程空间里都有一个套接字描述符表但是套接字数据结构都是在操作系统的内核缓冲里。

下面是一个创建流套接字的例子:

structprotoent*ppe;

ppe=getprotobyname("tcp");

SOCKETListenSocket=socket(PF_INET,SOCK_STREAM,ppe->p_proto);

四、closesocket函数

intclosesocket(

SOCKETs

);

closesocket函数用来关闭一个描述符为s套接字。

由于每个进程中都有一个套接字描述符表,表中的每个套接字描述符都对应了一个位于操作系统缓冲区中的套接字数据结构,因此有可能有几个套接字描述符指向同一个套接字数据结构。

套接字数据结构中专门有一个字段存放该结构的被引用次数,即有多少个套接字描述符指向该结构。

当调用closesocket函数时,操作系统先检查套接字数据结构中的该字段的值,如果为1,就表明只有一个套接字描述符指向它,因此操作系统就先把s在套接字描述符表中对应的那条表项清除,并且释放s对应的套接字数据结构;如果该字段大于1,那么操作系统仅仅清除s在套接字描述符表中的对应表项,并且把s对应的套接字数据结构的引用次数减1。

closesocket函数如果执行成功就返回0,否则返回SOCKET_ERROR。

五、send函数

intsend(

SOCKETs,

constcharFAR*buf,

intlen,

intflags

);

不论是客户还是服务器应用程序都用send函数来向TCP连接的另一端发送数据。

客户程序一般用send函数向服务器发送请求,而服务器则通常用send函数来向客户程序发送应答。

该函数的第一个参数指定发送端套接字描述符;第二个参数指明一个存放应用程序要发送数据的缓冲区;第三个参数指明实际要发送的数据的字节数;第四个参数一般置0。

这里只描述同步Socket的send函数的执行流程。

当调用该函数时,send先比较待发送数据的长度len和套接字s的发送缓冲区的长度,如果len大于s的发送缓冲区的长度,该函数返回SOCKET_ERROR;如果len小于或者等于s的发送缓冲区的长度,那么send先检查协议是否正在发送s的发送缓冲中的数据,如果是就等待协议把数据发送完,如果协议还没有开始发送s的发送缓冲中的数据或者s的发送缓冲中没有数据,那么send就比较s的发送缓冲区的剩余空间和len,如果len大于剩余空间大小send就一直等待协议把s的发送缓冲中的数据发送完,如果len小于剩余空间大小send就仅仅把buf中的数据copy到剩余空间里(注意并不是send把s的发送缓冲中的数据传到连接的另一端的,而是协议传的,send仅仅是把buf中的数据copy到s的发送缓冲区的剩余空间里)。

如果send函数copy数据成功,就返回实际copy的字节数,如果send在copy数据时出现错误,那么send就返回SOCKET_ERROR;如果send在等待协议传送数据时网络断开的话,那么send函数也返回SOCKET_ERROR。

要注意send函数把buf中的数据成功copy到s的发送缓冲的剩余空间里后它就返回了,但是此时这些数据并不一定马上被传到连接的另一端。

如果协议在后续的传送过程中出现网络错误的话,那么下一个Socket函数就会返回SOCKET_ERROR。

(每一个除send外的Socket函数在执行的最开始总要先等待套接字的发送缓冲中的数据被协议传送完毕才能继续,如果在等待时出现网络错误,那么该Socket函数就返回SOCKET_ERROR)

注意:

在Unix系统下,如果send在等待协议传送数据时网络断开的话,调用send的进程会接收到一个SIGPIPE信号,进程对该信号的默认处理是进程终止。

六、recv函数

intrecv(

SOCKETs,

charFAR*buf,

intlen,

intflags

);

不论是客户还是服务器应用程序都用recv函数从TCP连接的另一端接收数据。

该函数的第一个参数指定接收端套接字描述符;第二个参数指明一个缓冲区,该缓冲区用来存放recv函数接收到的数据;第三个参数指明buf的长度;第四个参数一般置0。

这里只描述同步Socket的recv函数的执行流程。

当应用程序调用recv函数时,recv先等待s的发送缓冲中的数据被协议传送完毕,如果协议在传送s的发送缓冲中的数据时出现网络错误,那么recv函数返回SOCKET_ERROR,如果s的发送缓冲中没有数据或者数据被协议成功发送完毕后,recv先检查套接字s的接收缓冲区,如果s接收缓冲区中没有数据或者协议正在接收数据,那么recv就一直等待,只到协议把数据接收完毕。

当协议把数据接收完毕,recv函数就把s的接收缓冲中的数据copy到buf中(注意协议接收到的数据可能大于buf的长度,所以在这种情况下要调用几次recv函数才能把s的接收缓冲中的数据copy完。

recv函数仅仅是copy数据,真正的接收数据是协议来完成的),recv函数返回其实际copy的字节数。

如果recv在copy时出错,那么它返回SOCKET_ERROR;如果recv函数在等待协议接收数据时网络中断了,那么它返回0。

注意:

在Unix系统下,如果recv函数在等待协议接收数据时网络断开了,那么调用recv的进程会接收到一个SIGPIPE信号,进程对该信号的默认处理是进程终止。

七、bind函数

intbind(

SOCKETs,

conststructsockaddrFAR*name,

intnamelen

);

当创建了一个Socket以后,套接字数据结构中有一个默认的IP地址和默认的端口号。

一个服务程序必须调用bind函数来给其绑定一个IP地址和一个特定的端口号。

客户程序一般不必调用bind函数来为其Socket绑定IP地址和断口号。

该函数的第一个参数指定待绑定的Socket描述符;第二个参数指定一个sockaddr结构,该结构是这样定义的:

structsockaddr{

u_shortsa_family;

charsa_data[14];

};

sa_family指定地址族,对于TCP/IP协议族的套接字,给其置AF_INET。

当对TCP/IP协议族的套接字进行绑定时,我们通常使用另一个地址结构:

structsockaddr_in{

shortsin_family;

u_shortsin_port;

structin_addrsin_addr;

charsin_zero[8];

};

其中sin_family置AF_INET;sin_port指明端口号;sin_addr结构体中只有一个唯一的字段s_addr,表示IP地址,该字段是一个整数,一般用函数inet_addr()把字符串形式的IP地址转换成unsignedlong型的整数值后再置给s_addr。

有的服务器是多宿主机,至少有两个网卡,那么运行在这样的服务器上的服务程序在为其Socket绑定IP地址时可以把htonl(INADDR_ANY)置给s_addr,这样做的好处是不论哪个网段上的客户程序都能与该服务程序通信;如果只给运行在多宿主机上的服务程序的Socket绑定一个固定的IP地址,那么就只有与该IP地址处于同一个网段上的客户程序才能与该服务程序通信。

我们用0来填充sin_zero数组,目的是让sockaddr_in结构的大小与sockaddr结构的大小一致。

下面是一个bind函数调用的例子:

structsockaddr_insaddr;

saddr.sin_family=AF_INET;

saddr.sin_port=htons(8888);

saddr.sin_addr.s_addr=htonl(INADDR_ANY);

bind(ListenSocket,(structsockaddr*)&saddr,sizeof(saddr));

八、listen函数

intlisten(SOCKETs,intbacklog);

服务程序可以调用listen函数使其流套接字s处于监听状态。

处于监听状态的流套接字s将维护一个客户连接请求队列,该队列最多容纳backlog个客户连接请求。

假如该函数执行成功,则返回0;如果执行失败,则返回SOCKET_ERROR。

九、accept函数

SOCKETaccept(

SOCKETs,

structsockaddrFAR*addr,

intFAR*addrlen

);

服务程序调用accept函数从处于监听状态的流套接字s的客户连接请求队列中取出排在最前的一个客户请求,并且创建一个新的套接字来与客户套接字创建连接通道,如果连接成功,就返回新创建的套接字的描述符,以后与客户套接字交换数据的是新创建的套接字;如果失败就返回INVALID_SOCKET。

该函数的第一个参数指定处于监听状态的流套接字;操作系统利用第二个参数来返回新创建的套接字的地址结构;操作系统利用第三个参数来返回新创建的套接字的地址结构的长度。

下面是一个调用accept的例子:

structsockaddr_inServerSocketAddr;

intaddrlen;

addrlen=sizeof(ServerSocketAddr);

ServerSocket=accept(ListenSocket,(structsockaddr*)&ServerSocketAddr,&addrlen);

十、connect函数

intconnect(

SOCKETs,

conststructsockaddrFAR*name,

intnamelen

);

客户程序调用connect函数来使客户Sockets与监听于name所指定的计算机的特定端口上的服务Socket进行连接。

如果连接成功,connect返回0;如果失败则返回SOCKET_ERROR。

五、实验总结

Socket(套接字)是一种网络编程接口,一个套接字是通信的一端.它意味着Socket用来让一个进程和其他的进程互通信息,就像人们通过手机来和其他人交流一样.Socket是主要的TCP/IP网络的API之一.Socket接口定义了许多函数,开发人员可以用它们来开发TCP/IP网络的应用程序.网络的Socket数据传输是一种特殊的I/O.

套接字被使用时,都有其类型和相关进程.套接字存在于通信域中(WindowsSockets规范支持单一的通信域,即Internet域),各个进程使用该域的套接字,相互之间用Internet协议族来进行通信.套接字可以根据通信性质分类,该性质对于用户是可见的.应用程序一般仅在同一类的套接字之间通信,不过只要底层的通信协议允许,不同类型的套接字之间也可以通信.

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

当前位置:首页 > IT计算机 > 电脑基础知识

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

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