操作系统课程设计Winsock+API编程.doc_第1页
操作系统课程设计Winsock+API编程.doc_第2页
操作系统课程设计Winsock+API编程.doc_第3页
操作系统课程设计Winsock+API编程.doc_第4页
操作系统课程设计Winsock+API编程.doc_第5页
已阅读5页,还剩12页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

操作系统课程设计 学习Winsock API编程操作系统课程设计学习Winsock API编程班级:信计0501姓名:李 宁学号:305010400?姓名:宋若军学号:3050104025学习Winsock API编程Windpows Sockets 是广泛应用的、开放的、支持多种协议的网络编程接口,主要由winsock.h头文件和动态链接库winsock.dll组成。一、 套接字套接字(Sockes)是通信的基础,是支持TCP/IP协议的网络通信的基本操作单元。可以将套接字看作是不同主机之间的进程进行双向通信的端点。根据通信网络的特性,套接字可以分为以下两类。1、 流套接字流套接字提供没有边界的数据流(即字节流),能够确保数据流以正确的顺序无重复地被送达,使用于处理大量数据。流套接字是面向连接的。2、 数据报套接字数据报套接字支持双向数据流,此数据流不能保证按顺序和不重复送达,也不能保证数据传输的可靠性。数据报套接字是无连接的。Winsock对有可能阻塞的函数提供了两种处理方式:阻塞方式和非阻塞方式。在阻塞方式下,收发数据的函数在被调用后一直等到传送完毕或出错才能返回,期间不能进行任何操作。在非阻塞方式下,函数被调用后立即返回,当网络传送完后,由Winsock给应用程序发送一个消息,通知操作完成。在编程时,应尽量使用非阻塞模式。二、 Winsock的启动和终止由于Winsock服务是以动态链接库的形式实现的,所以在使用前必须调用WSAStartup函数对其进行初始化,协商Winsock的版本支持,并分配必要的资源。WSAStartup函数声明如下:int WSAStartup(WORD wVersionRequested,LPWSADATA IpWSAData);参数说明:wVersionRequested:指定加载的Winsock版本,通常高位字节指定Winsock的副版本,低位字节指定Winsock的主版本,然后用MAKEWORD(X,Y)宏获取该值。IpWSAData:WSADATA数据结构指针,其中WSADATA结构的定义如下:Typedef struct WSADataWORD wVersion;/期望使用的Winsock版本WORD wHighVersion;/返回现有Winsock最高版本char szDescriptionWSADESCRIPTION_LEN+1;/套接字实现描述、char szSystemStatusWSASYS_STATUS_LEN+1;/状态或配置信息unsigned short iMaxSockets;/最大套接字数unsigned short iMaxUdpDg;/最大数据报长度char FAR * IpVendorInfo;/保留WSADATA,FAR *LPWSADATA;在应用程序关闭套接字连接后,还需要调用WSACleanup函数终止对Winsock库的使用,并释放资源,函数声明如下:int WSACleanup(void);三、 Winsock编程模型不论是流套接字还是数据报套接字编程,一般都采用客户端/服务器模式,其运行原理基本类似。数据报套接字的编程模型如图一所示。流套接字的编程模型如图二所示。服务器socket()bind()closesocket()recvfrom()sendto()socket()客户端closesocket()recvfrom()sendto() 服务器socket()bind()listen ()accept()send() recv()closesocket()socket()connect()closesocket()send() recv()客户端图一 数据报套接字编程模型 图二 流套接字编程模型流套接字的服务进程和客户端进程在通信前必须创建各自的套接字并建立连接,然后才能实现数据传输。具体编程步骤如下:(1) 服务器进程创建套接字。(2) 将本地地址绑定到套接字上以标识该套接字。(3) 将套接字置入监听模式并准备接收连接请求。(4) 客户端进程调用socket函数创建客户端套接字。(5) 客户端进程向服务进程发出连接请求。(6) 数据传输。(7) 关闭套接字。服务器进程总是先于客户进程启动,调用socket创建一个流套接字,该函数声明如下:SOCKET socket(int af,int type,int protocol);参数说明: af:指定网络地址族,一般为AF_INET。 type:指定套接字类型,可选的取值如下:SOCK_STREAM流套接字。SOCK_DGRAM数据报套接字。 protocol:指定网络协议,一般为0,表示默认的TCP/IP协议。成功创建了Socket之后,就应该选定通信的对象。调用bind()函数可以将本地地址绑定到套接字上,该函数声明如下:int bind(SOCKET s,const struct sockaddt FAR* name ,int namelen);参数说明: s:指定一个未绑定的套接字句柄,用于等待客户进程的连接。 name:指向sockaddr结构对象的指针。 namelen:指定sockaddr结构的长度。其中sockddr结构随选择的协议的不同而变化,因此常用的是sockaddr_in结构,用来标识TCP/IP协议下的地址,该结构定义如下:struct sockaddr_inshortsin_family;/指定地址族,一般为AF_INETu_shortsin_port;/指定端口号struct in_addrsin_addr;/指定IP地址charsin_zero8;/填充位;其中IP地址结构in_addr的定义如下:struct in_addrunionstructu_char s_b1,s_b2,s_b3,s_b4;S_un_b;structu_short s_w1,s_w2;S_un_w;u_long S_addr;S_un;绑定成功后,调用listen函数用于设置套接字的等待连接状态,该函数声明如下:int listen(SOCKET s,int backlog);参数说明: s:指定一个已绑定未连接的套接字句柄。 backlog:指定正在等待连接的队列的最大长度,可取15。进入监听状态后,通过调用accept函数使套接字做好接受客户连接的准备,该函数声明如下:SOCKET accept(SOCKET s,struct sockaddr FAR* addr,int FAR* addrlen);参数说明: s:指定处于监听状态的套接字句柄。 addr:指定一个有效的SOCKADDR_IN结构地址。 addrlen:指定SOCKADDR_IN结构的长度。accept函数返回后,addr变量中会包含请求连接的客户IP地址,并返回一个新的套接字句柄,对应于已经接受的那个客户端连接。而原来的监听套接字仍处于监听状态。客户进程调用connect函数可以主动提出连接请求,该函数声明如下:int connect(SOCKET s,const struct sockaddr FAR* name,int namelen);参数说明: s:指定一个未连接的套接字句柄。 name:指定服务进程的IP地址信息,针对TCP协议。 namelen:指定name参数的长度。当服务器进程接受连接请求后,将生成一个新的套接字,并向各客户进程返回接受信号。一旦客户进程收到来自服务器的接受信号,表示建立连接,即可进行数据传输了。调用send函数用于发送数据,调用recv函数用于接受数据,函数声明如下:int send(SOCKET s,const char FAR* buf,int len,int flags);int recv(SOCKET s,const char FAR* buf,int len,int flags);参数说明 s:指定已建立连接的套接字。 buf:发送或接受数据缓冲区。 len:指定数据缓冲区的长度。 flags:标志,一般为0。通信结束,必须关掉连接以释放套接字占用的资源。调用closesocket函数用于关闭套接字,该函数声明如下:Int closesocket( SOCKET s);为了保证套接字正常关闭,一般在调用closesocket之前先调用shutdown函数中断连接。该函数声明如下:int shutdown(SOCKET s,int how);参数说明: s:指定要中断的套接字句柄。 how:指定将禁止的操作,可选的取值如下:SD_RECEIVE禁止调用接受函数。SD_SEND禁止调用发送函数。SD_BOTH取消收发操作。四、Winsock I/O模型Winsock套接字在两种模式下执行I/O操作,即阻塞和非阻塞。默认情况下,套接字为阻塞模式。下面的代码演示了创建一个套接字,并将其设置为非阻塞模式的过程:SOCKET s;/套接字句柄unsigned long cmd;/指令参数int nStatus;/返回值s = socket(AF_INET,SOCK_STREAM,0); /创建流套接字nStatus = ioctlsocket(s,FIOBIO,&cmd);/设置为非阻塞模式将一个套接字设置为非阻塞模式之后,Winsock API调用会立即返回。Winsock提供了几种不同的套接字I/O模型,如选择(Select)、异步选择(WSAAsyncSelect)、事件选择(WSAEventSelect)和重叠(Overlapped)等。1. WSAAsyncSelect模型利用异步选择模型,应用程序可以在一个套接字上接收以Windows消息为基础的网络事件通知。该模型的实现方法是通过调用WSAAsyncSelect函数自动将套接字设置为非阻塞模式,并注册一个或多个网络事件,提供一个消息通知的窗口句柄。当注册的网络事件发生时,对应的窗口将接收到一个基于消息的通知。WSAAsyncSelect函数声明如下:int WSAAsyncSelect(SOCKET s,HWND hWnd,unsigned int wMsg,long IEwent);参数说明: s:指定需要事件通知的套接句柄。 hWnd:指定接收消息的窗口句柄。 wMsg:指定发送的消息。 IEvent:指定网络事件集合,可以是以下取值的和:FD_READ想要接收读准备好的通知。FD_WRITE想要接收写准备好的通知。FD_OOB想要接收带外数据到达的通知。FD_ACCEPT想要接收连接准备好的通知。FD_CONNECT想要接收已经连接的通知。FD_CLOSE想要接收套接字关闭的通知。如果要取消所有的通知,则将IEvent参数设置为0即可。2. WSAEventSelect模型与异步选择模型相似,利用事件选择模型应用程序可以在一个或多个套接字上接收以事件为基础的网络事件通知,并且它支持的网络事件与异步选择模型一样。它与WSAAsyncSelect模型最主要的区别在于,网络事件会被发送到一个事件对象句柄,而不是一个窗口句柄。首先需要调用WSACreateEvent函数创建事件对象来接收网络事件,该函数声明如下:WSAEVENT WSACreateEvent(void);返回的事件对象具有两种工作状态:有信号和无信号。接着调用WSAEventSelect函数将所创建的事件对象与套接字关联起来,并注册网络事件,该函数声明如下:Int WSAEventSelect(SOCKET s,WSAEVENT hEventObject, Long INetworkEvents);参数说明: s:指定需要事件通知的套接字句柄。 hEventObject:指定事件对象句柄。 InetworkEvent:指定网络事件集合。在完成一个I/O操作之后应用程序需要调用WSAResetEvent函数重置该事件对象,函数声明如下:BOOL WSAResetEvent(WSAEVENT hEvent);参数说明 hEvent:用于指定事件对象句柄。一个套接字与一个事件对象句柄关联起来之后,应用程序就可以通过WSAWaitForMultipleEvents函数等待网络事件来触发事件句柄的工作状态,进行I/O操作,该函数声明如下:DWORD WSAWaitForMultipleEvents(DWORD cEvents,const WSAEVENT FAR *IphEvents,BOOL fWaitAll,DWORD dwTimeOUT,BOOL fAlertable);参数说明: cEvent:指定事件对象句柄数组的元素的个数。、 IphEvents:指向一个事件对象句柄数组的指针。 fWaitAll:指定是否等待所有事件对象同时有信号。 dwTimeOUT:指定超时等待时间。 fAlertable:指定当系统将I/O例程放入队列时,函数是否返回。五、应用实例:基于Winsock API调用的聊天室1、服务器端应用程序设计(1)首先建立一个空的工作空间E1701。(2)在空间E1701里建立一个对话框工程Server作为服务端。对话框设计如图三所示。 图三 服务器端窗口设计(3)在类CServerDlg中给显示聊天内容和发送聊天内容的文本框添加变量/ Dialog Data/AFX_DATA(CServerDlg)enum IDD = IDD_SERVER_DIALOG ;CEditm_Show;/ 聊天记录显示控件对象CStringm_strShow;/ 聊天记录字符串CStringm_strMsg;/ 聊天内容字符串/AFX_DATA(4)在服务器端要保存客户端的socket连接,故引入链表支持,在ServerDlg.h中添加代码:#include typedef CList SOCKET_ARRAY;(5)要调用Winsock API必须包含winsock.h头文件和动态链接库winsock.dll。在StdAfx.h文件中添加如下代码:#include #pragma comment(lib, wsock32.lib)(6)自定义消息,并添加消息处理函数。在Server.h中添加:#define WM_SERVERMSG (WM_USER+100)/ Generated message map functions/AFX_MSG(CServerDlg)afx_msg long OnServerMsg(WPARAM wParam, LPARAM lParam);/AFX_MSG(7)初始化服务器窗口,创建服务器端socket。BOOL CServerDlg:OnInitDialog()CDialog:OnInitDialog();/ TODO: Add extra initialization heretryWSADATA wsaData;/ WSADATA结构对象WORD wVersionRequested=MAKEWORD(2, 0);/ 指定Winsock版本为2.0WSAStartup(wVersionRequested, &wsaData);/ 启动Winsockm_hSocket = socket(AF_INET, SOCK_STREAM, 0);/ 创建流套接字UINT len=WSAGetLastError();/获取错误代码if(len!=0)throw len;/抛出异常错误m_saList.RemoveAll();/ 清空套接字列表WSAAsyncSelect(m_hSocket, this-m_hWnd, / 接收消息的窗口为对话框WM_SERVERMSG, / 指定消息FD_ACCEPT | FD_READ | FD_WRITE| FD_CLOSE);/指定事件m_uPort = 8080;/ 设置端口号/ 设置套接字地址结构对象m_addr.sin_family = AF_INET;m_addr.sin_addr.S_un.S_addr = INADDR_ANY;m_addr.sin_port = htons(m_uPort);bind(m_hSocket,(LPSOCKADDR)&m_addr,sizeof(m_addr);/绑定套接字listen(m_hSocket, 3);/ 进入监听状态len=WSAGetLastError();if(len!=0)throw len;m_strShow = _T(服务器启动成功);catch(UINT &error)switch(error)case WSANOTINITIALISED:MessageBox(创建套接字失败!);m_strShow = _T(创建套接字失败!);break;case WSAEINVAL:MessageBox(监听端口已被占用!);m_strShow = _T(服务器启动失败);break;default:MessageBox(监听端口已被占用!);m_strShow = _T(服务器启动失败);UpdateData(FALSE);/ 更新显示return TRUE; (8)修改OnDestroy()函数void CServerDlg:OnDestroy() CDialog:OnDestroy();WSAAsyncSelect(m_hSocket, this-m_hWnd, 0, 0);/ 取消异步选择模式WSACleanup();/ 清理Winsock(9)编写发送按钮函数void CServerDlg:OnButton1() UpdateData(TRUE);m_strShow += _T(rn);/ 回车、换行m_strShow += m_strMsg;/ 添加聊天内容SOCKET s;for (int i=0; im_saList.GetCount(); i+)/ 向每个客户端发送聊天内容s = m_saList.GetAt(m_saList.FindIndex(i);int t=send(s, m_strMsg.GetBuffer(0), m_strMsg.GetLength(), 0);if(t 0)closesocket(s);m_saList.RemoveAt(m_saList.FindIndex(i);m_strMsg.Empty();UpdateData(FALSE);/ 更新显示m_Show.LineScroll(m_Show.GetLineCount();/ 跟踪滚动条的位置(10) 编写消息处理函数long CServerDlg:OnServerMsg(WPARAM wParam, LPARAM lParam) SOCKET socket, s;int i,j;char buf1024,name6,buf11024;int len;switch(lParam)case FD_ACCEPT:socket = accept(m_hSocket, NULL, NULL);for(i=0; im_saList.GetCount(); i+)s = m_saList.GetAt(m_saList.FindIndex(i);UintToChar(socket,name);buf0 = NULL;strcat(buf, 游客 );strcat(buf,name);strcat(buf, 进入聊天室);len=send(s, buf, strlen(buf), 0);if(len 0)closesocket(s);m_saList.RemoveAt(m_saList.FindIndex(i);m_strShow += rn;UintToChar(socket,name);m_strShow+=游客 ;m_strShow+=name;m_strShow += 进入聊天室;UpdateData(FALSE);m_saList.AddHead(socket);return 0;case FD_READ:length = m_saList.GetCount();for(i=0; ilength; i+)s = m_saList.GetAt(m_saList.FindIndex(i);if(s = wParam)len = recv(s, buf, 1024, 0);UintToChar(wParam,name);buflen = NULL;buf10 = 0;strcat(buf1, 游客 );strcat(buf1,name);strcat(buf1, 说:);strcat(buf1, buf);for(j=0; jm_saList.GetCount(); j+)s = m_saList.GetAt(m_saList.FindIndex(j);if(s!=wParam)len=send(s, buf1, strlen(buf1),0);if(len 0)closesocket(s);m_saList.RemoveAt(m_saList.FindIndex(j);len=WSAGetLastError();len=WSAENOTCONN;m_strShow += rn;m_strShow += buf1;UpdateData(FALSE);return 0;return 0;case FD_WRITE:return 0;case FD_CLOSE:UintToChar(wParam,name);m_strShow += rn;m_strShow +=游客 ;m_strShow +=name;m_strShow += 断线;UpdateData(FALSE);return 0;default:UintToChar(wParam,name);m_strShow += rn;m_strShow +=游客 ;m_strShow +=name;m_strShow += 离开;UpdateData(FALSE);return 0;2、客户端应用程序设计(1)在工作间E1701中创建一个对话框工程Client作为客户端。对话框设计如图四所示。 图四 客户端窗口设计(2) 在类CClientDlg中给显示聊天内容、发送聊天内容、端口号和服务器IP的文本框添加变量:/ Dialog Data/AFX_DATA(CClientDlg)enum IDD = IDD_CLIENT_DIALOG ;CEditm_Show;CIPAddressCtrlm_ip;/IP地址UINTm_uPort;/端口号CStringm_strShow;CStringm_strMsg;/AFX_DATA(3)要调用Winsock API必须包含winsock.h头文件和动态链接库winsock.dll。在StdAfx.h文件中添加如下代码:#include #pragma comment(lib, wsock32.lib)(4)自定义消息,并添加消息处理函数。在Client.h中添加:#define WM_CLIENTMSG (WM_USER+200)/ Generated message map functions/AFX_MSG(CClientDlg)afx_msg long OnClientMsg(WPARAM wParam, LPARAM lParam);/AFX_MSG(5) 初始化客户端窗口,创建socket。准备建立连接BOOL CClientDlg:OnInitDialog()/窗口初始化函数CDialog:OnInitDialog();/ TODO: Add extra initialization hereWSADATA wsaData;WORD wVersionRequested = MAKEWORD(2, 0);WSAStartup(wVersionRequested, &wsaData);m_hSocket=socket(AF_INET,SOCK_STREAM,0);/调用socket函数创建套接字WSAAsyncSelect(m_hSocket, this-m_hWnd, WM_CLIENTMSG, FD_CONNECT | FD_READ | FD_WRITE | FD_CLOSE);/利用异步选择模型,自动设置为非阻塞模式return TRUE; / return TRUE unless you set the focus to a control(6) 编写连接按钮函数void CClientDlg:OnButton2() UpdateData(true);BYTE f0,f1,f2,f3;m_ip.GetAddress(f0,f1,f2,f3);CString addr;addr.Format(%d.%d.%d.%d, f0, f1, f2, f3);m_addr.sin_family = AF_INET;m_addr.sin_addr.S_un.S_addr = inet_addr(addr.GetBuffer(0);m_addr.sin_port = htons(m_uPort); connect(m_hSocket,(LPSOCKADDR)&m_addr,sizeof(m_addr);/连接服

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论