您好,欢迎访问三七文档
当前位置:首页 > 电子/通信 > 综合/其它 > Socket通信实验报告
Socket通信实验:使用socket进行通信程序设计姓名:于广溪学号:11300720070系别:通信科学与工程日期:2013年11月7日目录:一、引言二、实验基本原理三、主要数据结构四、主要程序流程五、安装和使用说明六、附录1:程序源代码七、附录2:测试效果及程序代码获取地址八、附录3:参考资料正文:一、引言1.实验目的:本次实验的目的主要有一下三条:a.理解Socket(管道)通信的基本原理b.掌握通过socket编程实现C/S程序的基本方法c.掌握利用WindowsSocket函数库在Win32平台下编制通信程序的方法2.实验内容:编写一个界面友好的网络通信应用程序,包括服务器端和客户端两个程序。能实现以下功能:可以支持多人进行文字聊天。为了达到本次实验的目的并完成实验内容,选择了用C语言编写服务器端程序而采用VB.NET编写客户端的方法。由于本次实验基于Windows操作系统,所以编写界面友好型的程序选用VB.NET是十分适合的;而服务器端由于对性能的要求比较高并且不呈现给用户所以采用了C语言编写为控制台程序。出于可移植性的考虑,文章最后亦给出了客户端C语言的代码版本,以便在需要时参考使用。本次实验达到了多人进行文字聊天的功能,即实现了一个网络聊天室。如果服务器端IP地址是公网可方便进行网络间通信聊天。但如果服务器端IP是内网地址则程序实现局域网内聊天,对于IP穿透问题本次实验不作考虑。二、实验基本原理1.源代码编写语言a.本次实验服务器端源代码使用C语言编写,C语言的主要优点是效率高,能够提供服务器端需要的执行能力,支持较多用户同时在线,完成Socket的建立连接,中转客户间的数据进行通信。b.本次实验客户端源代码使用VB.NET编写,VB.NET是基于微软.NETFramework之上的面向对象的中间解释性语言,可以看作是VisualBasic在.NetFramework平台上的升级版本,需要在.NetFramework平台上才能执行,增强了对面向对象的支持。使用VB.NET可以直接画出窗体然后依次编写代码因此在性能负担很小而对界面有较多要求的客户端采用了这种编程语言。2.Socket函数和类1).Winsocket套接字API的实现:C语言中的通信主要使用Winsocket的系列函数,这一系列函数在库文件winsock.h中进行了定义。由于对于windows这样可以加载多个程序进入内存的复杂操作系统而言重复加载同样的共享代码没有必要,所以自windows95起使用了成为动态链接库来提供众多可以被公用的应用程序接口(API)。一般动态链接库以“.lib”后缀命名。在本次实验的服务器C语言源代码中起始部分就为winsock.h指明了相应的动态链接库文件“ws2_32.lib”.。关于应用程序,套接字API,TCP/IP函数和I/O函数的关系可以参见下图所示:2).Winsocket提供并在本次实验中用到的主要函数:A.WSAStartup函数:在使用windows套接字之前必须调用此函数来寻找合适的链接库并绑定它。其参数有两个,第一个指定使用的套接字版本,第二个有操作系统返回该套接字实际使用的版本。第一个参数是一个整数,用十六进制数表示。第二个参数指向一个WSADATA结构,操作系统将实际版本信息填入其中。B.WSACleanup函数:关闭套接字使用,释放所有数据结构和套接字绑定。C.Socket函数:创建一个新的套接字用于网络通信。函数返回一个描述符表示这个套接字。参数可以指定使用的协议族(如PF_INET代表TCP/IP)和协议、服务类型(如字节流或数据报)。对于使用internet协议族的套接字来说,协议、服务类型实际就睡tcp还是udp。D.connect函数:创建一个套接字后,客户端就可以用connect函数来建立连接了。其参数包括远端机器的IP地址和端口号。E.send函数:连接建立之后可以使用该函数来进行数据发送。其包括四个参数,套接字描述符、发送数据源地址、数据长度、控制传输的位标识。Send通常会将要发送的数据送入操作系统缓冲区以便程序可以继续执行,只有当系统缓冲区满时才会调用阻塞。F.recv函数:连接建立之后可以使用该函数来进行数据接收。其包括四个参数,套接字描述符、接收数据目的地址、缓冲区长度、控制接收的标志位。Recv获取到达套接字的数据后把它们复制到用户缓冲区。如果没有数据到达,调用就阻塞知道数据到达。如果到达数据超过缓冲区容量,则只提取有限的数据并返回获得的数据大小。G.closesocket函数:一旦套接字使用完毕,应该调用closesocket来释放它。H.bind函数:套接字创立时没有端点地址,应用程序使用bind函数来指定端点地址。对于TCP/IP协议来说,端点地址用sockaddr_in结构,它包括一个IP地址和协议端口号。I.listen函数:套接字创立时既非主动(客户端,主动进行连接)亦非被动(服务器端,被动等待连接)。Listen函数在服务器端被调用时将套接字指定为被动状态,等待连接的建立。一般在服务器端会存在一个无限循环,他接受并处理传入的连接,然后返回并接受处理下一个连接。即使处理一个连接只要几毫秒,但还是有可能出现系统忙于处理一个连接的时候另一个连接请求到达了的情况。为了保证没有连接请求被忽略,服务器端程序必须传递两个参数给listen参数,告诉操作系统对某个套接字上的连接进行排队。两个参数其一指定要置于被动态的套接字,另一个指定用于该套接字的队列大小。本实验队列大小定义为5。J.accept函数:对于TCP套接字,服务器程序调用socket创建一个套接字,调用bind绑定地址后会使用listen将其置为被动模式,然后调用accept获取一个传入请求。Accept的一个参数指定套接字并从这个套接字上接受连接及为新的连接创建新的套接字。Accept创建新的连接的套接字后返回新的套接字描述符,服务器用这个新的套接字与客户进行通信,原来的套接字用来接受其他的连接。K.htos函数:由于本地字节顺序与网络字节顺序不同,故在指定端点地址时需要进行字节顺序转换。由于实际使用中希望尽量方便,本次实验服务器端指定了端口为32007而只需用户填入会改变的IP地址。但是这个端口是本地存储的所以使用了htos进行转换。3)socket类(VB.NET中使用)区别于C语言中的Winsocket系列函数,在VB向VB.NET的改变进程中,自2005版之后已经取消了这类函数(控件)。虽然VB6.0写出的程序仍然可以在win7上运行,但是考虑到发展趋势本次试验放弃了VB中的这类控件而使用最新版VB.NET中的socket类。Socket类的继承层次结构为System.Net.Sockets.Socket,命名空间:System.Net.Sockets,其程序集为System(system.dll动态链接库中)。本程序中使用了socket类中的一个构造函数:Socket(AddressFamily,SocketType,ProtocolType)。它的三个参数指定地址族、套接字类型和协议。该函数用来初始化Socket类的新实例。本实验客户端调用该函数时指定参数为(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp)即指定IPV4协议族,数据流,TCP协议。使用Connect(EndPoint)方法进行连接,Endpoint类包含了IP地址和端口地址。使用Receive(Byte())方法从套接字接受数据放入缓存区。使用Send(Byte())方法将数据发送到套接字上。3.并发与异步IO中断由于实验要求允许多人同时聊天,即实现聊天室的功能,所以对于服务器端来说并发是必须的。服务器应该能同时完成这样的三个功能:等待新的连接接入并为之创建新的套接字,从已经创建的套接字上接收数据,从已经创建的套接字上发送数据。而对于客户端来说也应该能同时收发数据,所以在C/S两端都应该能实现并发。然而,对于服务器端和客户端来说并发的要求又有所不同。服务器端由于支撑着多个用户进行聊天所以如果采用多线程或者多进程的形式势必会创建至少2倍于客户端的线程/进程数。这对服务器能支撑的在线用户数做出了极大限制。早期的服务器端也确实是使用这种并发方式的。这种并发实现带来的死锁问题也是一个解决的难点。最近几年,服务器的并发方式有所改进,大多使用了单线程的方式实现。这样操作系统用于调度线程/进程的资源就可以节省出来,死锁的问题也自然消失了,使得服务器能轻松支撑百万人级别的服务。尽管相较于以前的传统并发实现方式,单线程有着如此优势,但是这种方式不符合人的思维方式,编程相比较为复杂。对于负载十分轻的客户端来说,使用IO异步中断也确实没有必要。因此在本次实验中,服务器端实现并发的方式选用了单线程IO异步中断,客户端实现并发采用多线程。这样能充分发挥各自并发实现方式的优点。1)异步I/O简介当socket函数recv,send被调用,或者程序正在进行accept时程序会阻塞在这里而不能继续运行。因此才无法实现并发而需要多线程/进程。但是用异步I/O的方法就能在一个线程内解决这个并发问题。首先,程序维持一个Socket的监视列表(用FD_SET函数将一个socket描述符加入到表中)然后使用select函数对表格进行检测,当发现在表格中的某个socket上有数据到达时就调用自定义的处理函数进行处理。2)异步I/O的实现方法异步I/O的实现主要使用到FD_ZERO,FD_SET,FD_ISSET,FD_CLR,select共5个函数。FD_ZERO用于把列表初始化,即清零。FD_SET把一个socket加入到列表中FD_ISSET用来判断调用FD_ISSET时指定的socket(由调用时的第一个参数指定)在不在列表中,即判断此socket是否可读写。FD_CLR用来将指定的socket(由调用时的第一个参数指定)清除出列表。Select用来进行检测,其定义如下:intselect(_In_intnfds,Inout_fd_set*readfds,_Inout_fd_set*writefds,_Inout_fd_set*exceptfds,_In_conststructtimeval*timeout);实验中调用使用的参数为:FD_SETSIZE,&rfds,(fd_set*)0,(fd_set*)0,(structtimeval*)0)==SOCKET_ERROR其中FD_SETSIZE限定了最多出现在表中socket描述符的个数,此处调用表明为64个。&rfds是检测可读的列表所在地址,之后的两个(fdset*)0代表不检测可写,没有例外,最后一行是等待的最大时间,此处表示立即返回不等待。3)多线程简介线程的概念是指一个计算活动的执行过程。当程序的代码指令装入内存后,操作系统允许多个线程(进程)执行这段代码。每个线程都有一个自己的指令指针和本地变量,它们共享一份全局变量(进程则所有资源都是独立的)。多线程的操作系统把CPU资源划分成若干时间片,以一定规则流转时间片。一个线程(进程)执行一小片时间,然后另一个线程得到执行。在1秒内可以完成以千为数量的轮转所以若干线程(进程)就好像一直在得到执行一样。4)多线程的实现方法VB.NET使用thread类来实现多线程。其继承层次结构为:System.Threading.Thread。命名空间:System.Threading。本实验使用其中一个构造函数Thread(ParameterizedThreadStart)来初始化一个thread的实例(Thre)。根据MSDN对ParameterizedThreadStart的说明VB.NET用户可以不调用ThreadStart构造函数。在传递所用方法时使用AddressOf运算符,例如DimtAsNewThread(AddressOfThreadProc)。VisualB
本文标题:Socket通信实验报告
链接地址:https://www.777doc.com/doc-7213163 .html