




已阅读5页,还剩13页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
计算机网络实验题目:Socket编程实验1 基于UDP的Socket编程实验2 基于TCP的Socket编程姓名:学号:学院:年级:实验时间:目录一、实验内容:3实验1 基于UDP的Socket编程3实验2 基于TCP的Socket编程3二、 程序设计31 基于UDP的Socket编程31.1 实验原理:基于UDP协议的无连接C/S的工作流程31.2 实现方案和实验结果41.2.1 方案1:Linux上两个进程间的通信4方案1实现方案4方案1实验结果41.2.2 方案2:Windows主机(client)和Linux虚拟机(server)之间的进程通信6方案2实现方案6方案2实验结果62 基于TCP的Socket编程72.1 实验原理:基于TCP协议的面向C/S的工作流程72.2 实现方案82.3 实验结果9三、讨论与心得10四、源代码12一、实验内容: 本单元实验包括的实验项目主要是UDP Socket编程和TCP Socket编程。本单元实验的主要目的是希望通过本单元实验,让学员掌握Socket编程方法,同时通过本单元实验让学生掌握如何开发基于TCP/IP协议的网络应用。实验1 基于UDP的Socket编程l 实验目的:通过本实验使学员了解和掌握编写基于UDP协议的网络应用程序。l 实验内容:编写基于UDP协议网络聊天程序,要求发送程序和接收程序能够接收键盘输入并彼此之间相互发送数据。l 实验步骤: 1.编写server端程序; 2.编写client端程序; 3.client程序与server程序联调; 4.client程序与server程序相互通信。实验2 基于TCP的Socket编程l 实验目的:通过本实验使学员了解和掌握编写基于TCP协议的网络应用程序。l 实验内容:编写基于TCP协议网络聊天程序,要求发送程序和接收程序能够接收键盘输入并彼此之间相互发送数据。l 实验步骤同UDP实验。2、 程序设计1 基于UDP的Socket编程1.1 实验原理:基于UDP协议的无连接C/S的工作流程 在server端,server首先启动,调用socket( )创建套接字,然后调用bind( )绑定server的地址(IP+port),调用recvfrom( )等待接收数据。在client端,先调用socket()创建套接字,调用sendto( )向server发送数据。server接收到client发来数据后,调用sendto( )向client发送应答数据,client调用recv接收server发来的应答数据。数据传输结束,server和client通过调用close( )关闭套接字。原理图如图1。图1 UDP通信原理图1.2 实现方案和实验结果1.2.1 方案1:Linux上两个进程间的通信方案1实现方案 server端:首先调用socket函数创建一个socket,然后调用bind函数将其与本机地址以及一个本地端口号绑定,用recvfrom函数接收到一个client端的消息后,用printf打印出来,server从标准输入设备中取得一行字符串后,调用sendto函数发送给client端。最后用close关闭该socket。 client机端:首先调用socket函数创建一个socket,初始化server地址及端口号(实际上就是本机),从标准输入设备中取得字符串,用sendto传送给server端,然后用recv函数接收server端发来的字符串,用printf打印出来。最后用close关闭该socket。 源代码见附录。方案1实验结果server和client通信截图图2 UDPserver图3 UDPClient1.2.2 方案2:Windows主机(client)和Linux虚拟机(server)之间的进程通信 方案2实现方案利用Vmware station的Linux虚拟机与Windows本机之间的局域网进行进程间通信,Linux虚拟机作为server,Windows本机作为client。除了Windows使用winsock函数库之外,实现方案与方案1类似,但要注意client端初始化的server地址虽然与方案1相同,但此时已不再是本机地址,而是局域网中Linux虚拟机的IP地址,相当于模拟了不同机器间的进程通信。方案2实验结果server和client通信截图图4 server图5 client2 基于TCP的Socket编程2.1 实验原理:基于TCP协议的面向C/S的工作流程在server端,server首先启动,调用socket( )创建套接字;然后调用bind( ) 绑定server的地址(IP+port);再调用listen( )让server做好侦听准备,并规定好请求队列长度,然后server进入阻塞状态,等待client的连接请求;最后通过accept( )来接收连接请求,并获得client的地址。当accpet接收到一个client发来的connet请求时,将生成一个新的socket,用于传输数据。在client端,client在创建套接字并指定client的socket地址,然后就调用connect( )和server建立连接。一旦连接建立成功,client和server之间就可以通过调用recv和send来接收和发送数据。一旦数据传输结束,server和client通过调用close( )来关闭套接字。原理图如图6。图62.2 实现方案 server端:首先调用socket函数创建一个Socket,然后调用bind函数将其与本机地址以及一个本地端口号绑定,然后调用listen在相应的socket上监听client,当accpet接收到一个client发来的connet请求时,将生成一个新的socket,用于传输数据。Recv( )接收到一个client端的消息后,server从标准输入设备中取得一行字符串,调用send()发送给client端。 client端:首先调用socket函数创建一个Socket,初始化server地址及端口号,然后调用connet请求连接监听server。通过server端accept函数生成的新socket,调用send函数向server端发送从标准输入设备中取得的字符串,然后用recv接收server端发来的字符串。最后用close()关闭该数据传输socket和监听socket。 源代码见附录。2.3 实验结果server和client通信截图图7 TCPserver图8 TCPserver三、讨论与心得记得助教第一节课讲socket的时候,我听得云里雾里的,只觉得是个很神奇的可以实现进程间通信的类似于接口的东西。印象深刻的是他举的那个打电话的例子,描述得很直观形象。老师说让我们做这个实验主要是为了体会TCP/IP的原理,并不要求应用做得多么高端,比如图形界面什么的。我觉得,我既然是网络工程专业的,就更应该学好计算机网络这门课,更好地了解socket编程。抱着这种决心,我开始了学习。于是我先去网上找了有关socket编程的讲解,觉得一篇一切皆socket的帖子写得很易懂,对于各个函数的功能有了初步的了解。操作系统的课程也有一个做shell命令解释器的实验,与socket编程实验基本是先后进行的。在做了shell,熟悉了“一切皆文件”的linux以及编译、运行的方法之后,感觉跟好理解,更好入手了。我先对照着一切皆socket的帖子看老师给的资料附录一中的示例一、二,也就是TCP。原来socket()是用来创建一个,然后返回值是这个socket的描述符,很类似与文件描述符。bind()是用来为这个socket绑定server的IP+port地址,当然绑定之前要先对server的地址进行初始化。listen()的作用是监听client的请求,第二个参数规定了这个刚刚创建的socket可以接受几个client的服务请求。一旦accept()了一个client的请求,就获得了这个client的地址,这真是个很神奇的东东啊!操作系统就会又给它分配了一个socket,也就是accept的返回值,用来传输数据。也就是说,TCP开了2个socket,一个用来监听子进程,一个用来传输数据。accept之后就可以自由发挥,开始进行网络I/0了!我们运行程序时可以看到的功能就是在下面的部分实现的。再往下的收发函数,read()/write(),recv()/send(),readv()/writev(),recvfrom()/sendto(),recvmsg()/sendmsg()什么的,就是用数据传输的socket了,而非监听的socket了。关于收发函数,我又上网查了一下资料。拿recv()/send()举例,其实recv只是把TCP协议传过来的数据copy到我们自己定义的buf里去。而recv所做的把发送缓冲区里的数据被协议发送到接收缓冲区这个过程对于编程用户来讲是不可见的,而recv只是把接收缓冲区里的数据copy出来而已。Send与之类似。从这个层面上讲,socket是一个TCP协议提供的接口服务,用传输层的协议干应用层想干的事。最后不要忘记用close()关掉创建的两个socket。此外,我大概理解了TCP的三次握手在函数中的体现,要传三个报文,如图1。图9 TCP的三次握手建立连接看懂、理解了代码后,我把代码拷了进去,调试成功后,看到了server会把client发过来的数据再返回去,我觉得这个没什么意义,就想改编它,让server也可以通过输入回答client。于是就又定义了一个buf3,用gets函数接受标准输入到buf3里,然后用把buf3的内容发给client。一个偶然的发现让我理解了TCP所谓的四次握手释放过程。当我把client关掉时,server就自动关掉了。当client首先调用close主动关闭连接,这时TCP发送一个FIN M;另一端接收到FIN M之后,执行被动关闭,对这个FIN进行确认。它的接收也作为文件结束符传递给server的应用程序,因为FIN的接收意味着server的应用进程在相应的连接上再也接收不到额外数据;一段时间之后,server上接收到文件结束符的应用进程调用close关闭它的socket。这导致它的TCP也发送一个FIN N;接收到这个FIN的源发送端TCP对它进行确认。这样每个方向上都有一个FIN和ACK。图10 socket中发送的TCP四次握手释放调通了TCP之后,我去尝试UDP。其实这顺序是反了的,因为UDP貌似更简单一些,可是因为TCP有现成的代码,所以就先理解了TCP。之后去查UDP的资料,直观上感觉除了创建socket()时定义的协议不同外,就是少了一些函数,少用了一个socket。对于server来讲,bind()之后,不用再listen和accept了,就是不用监听了,直接用这个socket来进行数据传输就可以了。于是我就想当然地觉得下面进行网络I/O的收发函数应该是差不多的,就直接用了刚才在TCP里用的recv和send。对于client来讲,我也是直接用的这两个函数。然后怎么都传不了数据,实现不了通信。于是我仔细比较了几个函数的参数,发现recvfrom比recv多了2个参数,后来理解了一下,这是因为TCP在accept时就为与之通信的每个进程分配了一个socket用于数据传输,也就是说对于server的每个client都有一个专门的进程与其进行通信。而UDP没有开另外的进程,也就是没有创建新的socket,所以server的多个client是共用这一个socket的,可以说是多路复用了。为了辨别多个client,server需要client的地址(ip+port)来接受和发送,于是就要用recvfrom多出来的2个参数,即client的地址和地址长度。这也就同时解释了为什么UDP只创建一个socket就够了。对于UDP的client端来说,只需要在发送数据的时候用sendto来表明server的地址,在接收数据时就不需要的了,因为只有一个server,不需要标记了。因为我是在虚拟机上进行的以上实验,我就想利用主机和虚拟机之间的局域网来建立连接,进行进程通信。虽然操作系统不同,socket库不同,但是只要使用了相同的协议(我选择的是UDP)就可以进行通信。于是我又去学习了Winsock的相关知识,实现了不同系统间的通信。我觉得虽然Windows和Linux的socket使用总体上差不多,但是Winsock的一些准备(我觉得比较冗余)复杂一点。 四、源代码【UDP方案1】Server端源码:/udpService:#include #include #include #include #include #include #include #include #include #include #define PORT 7000int main(void)int sockfd,pktlen;char buf300,buf1300;struct sockaddr_in server,client;sockfd=socket(AF_INET,SOCK_DGRAM,0);memset (char *)&server, sizeof(server), 0); /将已开辟内存空间 server 的全部字节的值设为值0.类似于bzeroserver.sin_family = AF_INET;server.sin_port = htons(PORT);/端口号server.sin_addr.s_addr = INADDR_ANY;/设置网络地址,INADDR_ANY表示机器的IP地址bind(sockfd,(struct sockaddr *)&server,sizeof(struct sockaddr_in);for (;) /*recv接受client发送的数据,recv函数仅仅是copy数据,真正的接收数据是协议来完成的),第一个参数指定接收端套接字描述符;第二个参数指明一个缓冲区,该缓冲区用来存放recv函数接收到的数据;第三个参数指明buf的长度recv函数返回其实际copy的字节数*/int l=sizeof(struct sockaddr_in);pktlen = recvfrom (sockfd, buf, sizeof (buf), 0,(struct sockaddr_in *)&client,&l); if (pktlen = 0)break;printf (Received line: %sn, buf);printf (Enter a line: );fgets(buf1,300,stdin);/*并不是send把ns的发送缓冲中的数据传到连接的另一端的,而是协议传的,send仅仅是把buf中的数据copy到ns的发送缓冲区的剩余空间里返回实际copy的字节数*/sendto (sockfd, buf1,sizeof(buf1), 0,(struct sockaddr_in*)&client,l);close(sockfd);Client端源码:/udpClient:#include #include #include #include #include #include #include #include #include #define MAX_SIZE 1024#define PORT 7000#define HOST_ADDR 192.168.145.137int main(int argc,char *argv)int sockfd,buflen;char buf1300,buf2300;struct sockaddr_in server;sockfd=socket(AF_INET,SOCK_DGRAM,0);server.sin_family=AF_INET;server.sin_port = htons(PORT);server.sin_addr.s_addr = inet_addr (HOST_ADDR);for(;)printf (Enter a line: );gets (buf1);/从stdin流中读取字符串,直至接受到换行符buflen = strlen (buf1);if (buflen = 0)break;sendto(sockfd, buf1, buflen + 1, 0,(struct sockaddr *)&server,sizeof(server);recv(sockfd, buf2, sizeof (buf2), 0);printf(Received line: %sn, buf2);close(sockfd);return 0;【UDP方案2】Linux server端源码同方案1Windows client端源码:/客户端#include #include #define INVALID_VALUE32 0xFFFF#pragma comment(lib, ws2_32.lib)/客户端主函数开始void main()int err = INVALID_VALUE32;WORD ver;WSADATA wsaData;/定义版本号ver = MAKEWORD(2, 2); /* 版本号为2.2版本,1.1也可以 */ /* 接下来初始化 */err = WSAStartup(ver, &wsaData);if (err != 0) /* 检查socket是否初始化成功 */return;if (2 != (LOBYTE(wsaData.wVersion) | (2 != HIBYTE(wsaData.wVersion) WSACleanup(); /* 版本错误则清楚导入的DLL */ return;/* 开始socket的主体部分:创建socket */ SOCKET sockClient = socket(AF_INET, SOCK_DGRAM, 0); /* AF_INET表示网络使用的范围internetwork: UDP, TCP, etc, SOCK_STREAM表示使用的是TCP SOCK_DGRAM 表示使用的是UDP类型 */SOCKADDR_IN addrSrv;addrSrv.sin_addr.S_un.S_addr = inet_addr(192.168.145.137); / 这里设置为服务器的IP地址,由于我在自己的机器上面写的,所以写回环地址了addrSrv.sin_family = AF_INET;addrSrv.sin_port = htons(7000); /* 端口随便只要不跟系统冲突就行 */* 定义几个缓冲区,也是接受和发送的,设置成100即可 */char recvbuf100;char sendbuf100;int len=sizeof(SOCKADDR);/* 下面进入死循环 */while (1)printf(nEnter a line:n);gets(sendbuf);sendto(sockClient, sendbuf, strlen(sendbuf)+1, 0, (SOCKADDR *)&addrSrv, len);int recvnum = recvfrom(sockClient, recvbuf, 100, 0, (SOCKADDR *)&addrSrv, &len);recvbufrecvnum = 0;/* 将接收到的数据存入数据缓冲区,以便显示 */printf(Receive a line %s, recvbuf);/* 最后将socket清空 */closesocket(sockClient);WSACleanup();return;【TCP】Server端源码:#include #include #include #include #include #define PORT 7000main ()struct sockaddr_in client, server;/ 客户端地址信息 本机地址信息int s, ns, namelen, pktlen;/s:监听socket ns:数据传输socket namelen:client的地址长度 pktlen:传送数据的字节数char buf400;char buf3200;s=socket(AF_INET, SOCK_STREAM, 0); /创建连接的SOCKET,s为socket描述符/ 初始化服务器地址memset (char *)&server, sizeof(server), 0); /将已开辟内存空间 server 的全部字节的值设为值0.类似于bzeroserver.sin_family = AF_INET;server.sin_port = htons(PORT);/端口号server.sin_addr.s_addr = INADDR_ANY;/设置网络地址,INADDR_ANY表示机器的IP地址/server需要在listen之前绑定一个大家都知道的地址,就是刚刚初始化好的ip+端口号bind(s, (struct sockaddr *)&server, sizeof(server);listen(s,1);/侦听客户端请求,i为socket可以排队链接的最大个数/*接受client请求,s为server的描述符(即监听socket描述符),第二个参数即指针client的协议地址,第三个参数代表地址长度返回值ns是一个全新的描述符,是数据传输socket,代表与返回客户的tcp连接*/namelen = sizeof (client);ns = accept (s, (struct sockaddr *)&client, &namelen);/开始进行网络I/Ofor (;) /*recv接受client发送的数据,recv函数仅仅是copy数据,真正的接收数据是协议来完成的),第一个参数指定接收端套接字描述符;第二个参数指明一个缓冲区
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2026届重庆一中学物理八上期末经典试题含解析
- 2026届安徽省池州市第十中学物理八上期末考试试题含解析
- 2026届江苏省常州市新北区外国语学校物理八上期末质量跟踪监视模拟试题含解析
- 药品安全处置管理办法
- 菏泽快递客户管理办法
- 检疫规程检疫管理办法
- 河南智能电源管理办法
- 淄博排污河道管理办法
- 淮安承接查验管理办法
- 源头治理超载管理办法
- 江南大学实验动物中心大楼项目报告表
- 《孙子兵法》全文及译文
- 《经济法基础》 (第2章) 第二章 会计法律制度
- 防呆培训课件
- BSL实验室生物安全管理体系文件
- 电力系统安全运行与故障预警机制
- 帕金森综合症
- 企业员工工会建设计划
- 无人驾驶技术标准-洞察分析
- 2024年江苏省学业水平合格性考试全真模拟语文试题(解析版)
- 投标货物包装、运输方案
评论
0/150
提交评论