版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、套接字编程,Socket简介,80年代初,人们在UNIX操作系统下实现TCP/IP协议。 研究人员为TCP/IP网络通信开发了一个API(应用程序接口)。 这个API称为Socket接口(套接字)。 SOCKET接口是TCP/IP网络最为通用的API,也是在INTERNET上进行应用开发最为通用的API。,Socket简介,90年代初,Microsoft联合了其他几家公司共同制定了一套WINDOWS下的网络编程接口,即WindowsSockets规范。 增加了一些异步函数,并增加了符合Windows消息驱动特性的网络事件异步选择机制。 WINDOWSSOCKETS规范是一套开放的、支持多种协议
2、的Windows下的网络编程接口。 从1991年的1.0版到1995年的2.0.8版,经过不断完善并在Intel、Microsoft、Sun、SGI、Informix、Novell等公司的全力支持下,已成为Windows网络编程的事实上的标准。 在实际应用中的WINDOWSSOKCETS规范主要有1.1版和2.0版。 两者的最重要区别是1.1版只支持TCP/IP协议,而2.0版可以支持多协议。 2.0版有良好的向后兼容性,任何使用1.1版的源代码,二进制文件,应用程序都可以不加修改地在2.0规范下使用。,Socket简介,SOCKET实际在计算机中提供了一个通信端口,可以通过这个端口与任何一个
3、具有SOCKET接口的计算机通信。 应用程序在网络上传输,接收的信息都通过这个SOCKET接口来实现。 在应用开发中就像使用文件句柄一样,可以对SOCKET句柄进行读,写操作。,Socket的机制是什么,简单的把Socket理解为一个可以连通网络上不同计算机程序之间的管道,把一堆数据从管道的A端扔进去,则会从管道的B端(也许同时还可以从C、D、E、F端冒出来)。 管道的端口由两个因素来唯一确认,即机器的IP地址和程序所使用的端口号。 端口号就是程序员指定的一个数字,如:http的80端口和ftp的21端口。 建议大家自己写程序不要使用太小的端口号,它们一般被系统占用了,也不要使用一些著名的端口
4、,一般来说使用10005000之内的端口比较好。,Socket的机制是什么,Socket可以支持数据的发送和接收 它定义一种称为套接字的变量,发送数据时首先创建套接字,然后使用该套接字的sendto等方法对准某个IP/端口进行数据发送 接收端也首先创建套接字,然后将该套接字绑定到一个IP/端口上,所有发向此端口的数据会被该套接字的recv等函数读出。如同读出文件中的数据一样。,所需的头文件、库文件和DLL,最广泛的Windows Socket2.0版本,所需的一些文件如下(以安装了VC6为例说明其物理位置): (1)头文件winsock2.h,通常处于C:“Program Files”Micr
5、osoft Visual Studio“VC98”INCLUDE;查看该头文件可知其中又包含了windows.h和pshpack4.h头文件,因此在windows中的一些常用API都可以使用 (2)库文件Ws2_32.lib,通常处于C:Program FilesMicrosoft Visual StudioVC98Lib; (3)DLL文件Ws2_32.dll,通常处于C:WINDOWSsystem32。,编写Socket程序需要的编程基础,(1)C+语法; (2)一点windows SDK的基础,了解一些SDK的数据类型与API的调用方式; (3)一点编译、链接和执行的技术。,UDP,所谓
6、UDP,就是发送出去就不管的一种网络协议。 UDP编程的发送端只管发送就可以了,不用检查网络连接状态。 下面用例1来说明怎样编写UDP,并会详细解释每个API和数据类型。,SOCKET类型,SOCKET是socket套接字类型,在WINSOCK2.H中有如下定义: typedef unsigned int u_int; typedef u_int SOCKET; 可知套接字实际上就是一个无符号整型,它将被Socket环境管理和使用。 套接字将被创建、设置、用来发送和接收数据,最后会被关闭。,WORD类型、MAKEWORD、LOBYTE和HIBYTE宏,WORD类型是一个16位的无符号整型,在W
7、TYPES.H中被定义为: typedef unsigned short WORD; 目的是提供两个字节的存储,在Socket中这两个字节可以表示主版本号和副版本号。 使用MAKEWORD宏可以给一个WORD类型赋值。例如要表示主版本号2,副版本号0,可以使用以下代码: WORD wVersionRequested; wVersionRequested = MAKEWORD( 2, 0 ); 注意低位内存存储主版本号2,高位内存存储副版本号0,其值为0 x0002。使用宏LOBYTE可以读取WORD的低位字节,HIBYTE可以读取高位字节。,WSADATA类型和LPWSADATA类型,WSAD
8、ATA类型是一个结构,描述了Socket库的一些相关信息,其结构定义如下: typedef struct WSAData WORD wVersion; WORD wHighVersion; char szDescriptionWSADESCRIPTION_LEN+1; char szSystemStatusWSASYS_STATUS_LEN+1; unsigned short iMaxSockets; unsigned short iMaxUdpDg; char FAR * lpVendorInfo; WSADATA; typedef WSADATA FAR *LPWSADATA;,WSASt
9、artup函数,WSAStartup函数被用来初始化Socket环境,它的定义如下: int PASCAL FAR WSAStartup(WORD wVersionRequired, LPWSADATA lpWSAData); 其返回值为整型,调用方式为PASCAL(即标准类型,PASCAL等于_stdcall),参数有两个,第一个参数为WORD类型,指明了Socket的版本号,第二个参数为WSADATA类型的指针。 若返回值为0,则初始化成功,若不为0则失败。,WSACleanup函数,这是Socket环境的退出函数。返回值为0表示成功,SOCKET_ERROR表示失败。,socket函数,
10、socket的创建函数,其定义为: SOCKET PASCAL FAR socket (int af, int type, int protocol); 第一个参数为int af,代表网络地址族,目前只有一种取值是有效的,即AF_INET,代表internet地址族; 第二个参数为int type,代表网络协议类型,SOCK_DGRAM代表UDP协议,SOCK_STREAM代表TCP协议; 第三个参数为int protocol,指定网络地址族的特殊协议,目前无用,赋值0即可。 返回值为SOCKET,若返回INVALID_SOCKET则失败。,setsockopt函数,函数用来设置Socket的
11、属性,若不能正确设置socket属性,则数据的发送和接收会失败。定义如下: int PASCAL FAR setsockopt (SOCKET s, int level, int optname, const char FAR * optval, int optlen); 其返回值为int类型,0代表成功,SOCKET_ERROR代表有错误发生。 第一个参数SOCKET s,代表要设置的套接字; 第二个参数int level,代表要设置的属性所处的层次,层次包含以下取值:SOL_SOCKET代表套接字层次;IPPROTO_TCP代表TCP协议层次,IPPROTO_IP代表IP协议层次(后面两个
12、我都没有用过); 第三个参数int optname,代表设置参数的名称,SO_BROADCAST代表允许发送广播数据的属性,其它属性可参考MSDN; 第四个参数const char FAR * optval,代表指向存储参数数值的指针,注意这里可能要使用reinterpret_cast类型转换; 第五个参数int optlen,代表存储参数数值变量的长度。,sockaddr_in,sockaddr_in定义了socket发送和接收数据包的地址,定义: struct sockaddr_in short sin_family; u_short sin_port; structin_addr sin
13、_addr; char sin_zero8; ;,in_addr类型,in_addr的定义如下: struct in_addr union struct u_char s_b1,s_b2,s_b3,s_b4; S_un_b; struct u_short s_w1,s_w2; S_un_w; u_long S_addr; S_un; 首先阐述in_addr的含义,很显然它是一个存储ip地址的联合体(忘记union含义的请看c+书),有三种表达方式: 第一种用四个字节来表示IP地址的四个数字; 第二种用两个双字节来表示IP地址; 第三种用一个长整型来表示IP地址。,inet_addr,给in_a
14、ddr赋值的一种最简单方法是使用inet_addr函数,它可以把一个代表IP地址的字符串赋值转换为in_addr类型,如 addrto.sin_addr.s_addr=inet_addr(); sockaddr_in的含义比in_addr的含义要广泛,其各个字段的含义和取值如下: 第一个字段short sin_family,代表网络地址族,如前所述,只能取值AF_INET; 第二个字段u_short sin_port,代表IP地址端口,由程序员指定; 第三个字段structin_addr sin_addr,代表IP地址; 第四个字段char sin_zero8,是为了保证
15、sockaddr_in与SOCKADDR类型的长度相等而填充进来的字段,inet_ntoa函数,函数是inet_ntoa,可以把一个in_addr类型转换为一个字符串,sockaddr类型,sockaddr类型是用来表示Socket地址的类型,同上面的sockaddr_in类型相比,sockaddr的适用范围更广,因为sockaddr_in只适用于TCP/IP地址。 Sockaddr的定义如下: struct sockaddr u_short sa_family; char sa_data14; ; 可知sockaddr有16个字节,而sockaddr_in也有16个字节,所以sockaddr
16、_in是可以强制类型转换为sockaddr的。事实上也往往使用这种方法。,Sleep函数,线程挂起函数,表示线程挂起一段时间。如,Sleep(1000)表示挂起一秒。 定义于WINBASE.H头文件中。 WINBASE.H又被包含于WINDOWS.H中,然后WINDOWS.H被WINSOCK2.H包含。,sendto函数,在Socket中有两套发送和接收函数,一是sendto和recvfrom;二是send和recv。前一套在函数参数中要指明地址;而后一套需要先将套接字和一个地址绑定,然后直接发送和接收,不需绑定地址。sendto的定义如下: int PASCAL FAR sendto (SO
17、CKET s, const char FAR * buf, int len, int flags, const struct sockaddr FAR *to, int tolen); 第一个参数就是套接字; 第二个参数是要传送的数据指针; 第三个参数是要传送的数据长度(字节数); 第四个参数是传送方式的标识,如果不需要特殊要求则可以设置为0,其它值请参考MSDN; 第五个参数是目标地址,注意这里使用的是sockaddr的指针; 第六个参数是地址的长度; 返回值为整型,如果成功,则返回发送的字节数,失败则返回SOCKET_ERROR。,WSAGetLastError函数,该函数用来在Socke
18、t相关API失败后读取错误码,根据这些错误码可以对照查出错误原因,closesocket,关闭套接字,其参数为SOCKET类型。成功返回0,失败返回SOCKET_ERROR。,总结,写一个UDP发送程序的步骤如下: 1.用WSAStartup函数初始化Socket环境; 2.用socket函数创建一个套接字; 3.用setsockopt函数设置套接字的属性,例如设置为广播类型;很多时候该步骤可以省略; 4.创建一个sockaddr_in,并指定其IP地址和端口号; 5.用sendto函数向指定地址发送数据,这里的目标地址就是广播地址;注意这里不需要绑定,即使绑定了,其地址也会被sendto中的
19、参数覆盖;若使用send函数则会出错,因为send是面向连接的,而UDP是非连接的,只能使用sendto发送数据; 6.用closesocket函数关闭套接字; 7.用WSACleanup函数关闭Socket环境。,总结,一个UDP接收程序的步骤如下,注意接收方一定要bind套接字: 1.用WSAStartup函数初始化Socket环境; 2.用socket函数创建一个套接字; 3.用setsockopt函数设置套接字的属性,例如设置为广播类型; 4.创建一个sockaddr_in,并指定其IP地址和端口号; 5.用bind函数将套接字与接收的地址绑定起来,然后调用recvfrom函数或者re
20、cv接收数据;注意这里一定要绑定,因为接收报文的套接字必须在网络上有一个绑定的名称才能保证正确接收数据; 6.用closesocket函数关闭套接字; 7.用WSACleanup函数关闭Socket环境。,TCP,TCP与UDP最大的不同之处在于TCP是一个面向连接的协议,在进行数据收发之前TCP必须进行连接,并且在收发的时候必须保持该连接。,TCP,发送方的步骤如下(省略了Socket环境的初始化、关闭等内容): 1.用socket函数创建一个套接字sock; 2.用bind将sock绑定到本地地址; 3.用listen侦听sock套接字; 4.用accept函数接收客户方的连接,返回客户方
21、套接字clientSocket; 5.在客户方套接字clientSocket上使用send发送数据; 6.用closesocket函数关闭套接字sock和clientSocket;,TCP,接收方的步骤如下: 1.用socket函数创建一个套接字sock; 2.创建一个指向服务方的远程地址; 3.用connect将sock连接到服务方,使用远程地址; 4.在套接字上使用recv接收数据; 5.用closesocket函数关闭套接字sock;,TCP,注意:在服务方有两个地址,一个是本地地址myaddr,另一个是目标地址addrto。 本地地址myaddr用来和本地套接字sock绑定,目标地址被
22、sock用来accept客户方套接字clientSocket。 这样sock和clientSocket连接成功,这两个地址也连接上了。 在服务方使用clientSocket发送数据,则会从本地地址传送到目标地址。 在客户方只有一个地址,即来源地址addrfrom。这个地址被用来connect远程的服务方套接字,connect成功则本地套接字与远程的来源地址连接了,因此可以使用该套接字接收远程数据。,bind函数,bind函数用来将一个套接字绑定到一个IP地址。一般只在服务方(即数据发送方)调用,很多函数会隐式的调用bind函数。,listen函数,从服务方监听客户方的连接。同一个套接字可以多次
23、监听。,connect和accept函数,connect是客户方连接服务方的函数,而accept是服务方同意客户方连接的函数。 这两个配套函数分别在各自的程序中被成功调用后就可以收发数据了。,send和recv函数,send和recv是用来发送和接收数据的两个重要函数。 send只能在已经连接的状态下使用,而recv可以面向连接和非连接的状态下使用。 send的定义如下: int WSAAPI send( SOCKET s, const char FAR * buf, int len, int flags ); 其参数的含义和sendto中的前四个参数一样。 而recv的定义如下: int W
24、SAAPI recv( SOCKET s, char FAR * buf, int len, int flags ); 其参数含义与send中的参数含义一样。,一个局域网聊天工具的编写,能设计如下: 1.要能够指定聊天对象的IP和端口(端口可以内部确定); 2.要能够发送消息给指定聊天对象; 3.要能够接收聊天对象的消息; 4.接收消息时要播放声音; 5.接收消息时如果当前对话框不是最前端,要闪动图标; 6.要有托盘图标,可以将对话框收入托盘;,功能实现,将内部端口设为5556,提供一个IP地址控件来设置聊天对象的IP。该控件必须能够读取IP地址并赋值给内部变量。将地址转换为in_addr类型
25、。 发送消息需要使用一个套接字。 接收消息也需要使用一个套接字,由于发送消息也使用了一个套接字,为了在同一个进程中同时发送和接收消息,需要使用多线程技术,将发送消息的线程设为主线程;而接收消息的线程设为子线程,子线程只负责接收UDP消息,在收到消息后显示到主界面中。 接收消息时播放声音这个功能在子线程中完成,使用sndPlaySound函数,并提供一个wav文件即可。 闪动图标功能需要使用一个Timer,在主对话框类中添加一个OnTimer函数,定时检查当前窗口状态变量是否为假,若为假就每次设置另一个图标。若当前窗口显示到最顶端,则设置为默认图标。 托盘图标功能用网上下载的CtrayIcon类
26、。需要提供一个自定义消息,一个弹出菜单资源。,所需资源,头文件:winsock2.h,Mmsystem.h 库文件:ws2_32.lib,winmm.lib dll:Ws2_32.dll,winmm.dll wav文件:recv.wav 图标:一个主程序图标IDI_MAIN、四个变化图标IDI_ICON14; 菜单:一个给托盘用的弹出菜单IDR_TRAYICON; 说明,Mmsystem.h和winmm.lib、winmm.dll是为了那个播放声音的功能。,托盘功能,托盘属于界面功能,是变更很少的需求,因此首先完成。 1.引入TRAYICON.H和TRAYICON.cpp两个类; 2.在CLA
27、NTalkDlg类中加入一个CTrayIconm_trayIcon;属性; 3.在CLANTalkDlg的构造函数中初始化m_trayIcon,m_trayIcon(IDR_TRAYICON); 4.添加一个自定义消息WM_MY_TRAY_NOTIFICATION,即在三个地方添加消息定义、消息响应函数、消息映射; 5.在InitDialog方法中调用托盘初始化的两个函数 m_trayIcon.SetNotificationWnd(this, WM_MY_TRAY_NOTIFICATION); m_trayIcon.SetIcon(IDI_MAIN); 6.重写OnClose方法,添加弹出菜单
28、的OnAppSuspend和OnAppOpen以及OnAppAbout方法; 7.重写对话框的OnCancel方法。,动态图标,动态图标也是界面相关功能,首先完成。 1.添加四个HICON变量m_hIcon1,m_hIcon2,m_hIcon3,m_hIcon4; 2.在构造函数中初始化这四个变量m_hIcon1 = AfxGetApp()-LoadIcon(IDI_ICON1); 3.在InitDialog中设置调用SetTimer(1,300,NULL);设置一个timer,id为1,间隔为300微秒; 4.添加一个布尔属性m_bDynamicIcon,指示目前是否需要动态图标,并给出一个
29、设置函数SetDynamicIcon; 5.添加一个OnTimer函数,让每次timer调用时根据m_bDynamicIcon的值修改图标; 两个地方是用来设置动态图标的,一个是当程序收到消息并且程序不在桌面顶端时,这时设置为动态图标,在后面的消息接收线程中处理;二是当程序显示到桌面顶端时,设置为非动态; 重载OnActivate方法可以完成第二个时刻的要求。当窗口状态为WA_ACTIVE或者WA_CLICKACTIVE时SetDynamicIcon(false),否则设置SetDynamicIcon(true);,发送UDP报文功能,发送UDP报文只需在主线程中完成,需要以下步骤: 1.初始化Socket环境,这可以在CLANTalkApp的InitInstance中完成,同理关闭Socket环境在ExitInstance中完成;我们可以使用前面的方法,也可以直接调用MFC中的AfxSocketInit函数,这个函数可以确保在程序结束时自动关闭Socket环境; 2.创建socket,考虑到报错信息需要弹出对话框,因此不在CLANTalkDlg的构造函数中创建,而是在InitDialog中构建;发送报文的socket为m_sendSock; 3.设置目的地址功能,需要一个地址赋值函数setAddress(c
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 大型地质勘探用吊车租赁合同
- 税务筹划在互联网企业中的应用
- 娱乐经纪公司财务内控制度
- 文化创意产业园区租赁承包协议
- 畜牧业设备租赁协议
- 航空航天工程力工合同
- 厨房设备管理细则
- 电子产品体验租赁合同
- 湖北省养老产业发展招投标
- 建筑工程改造合同模板
- 2024年采矿权转让合同范本
- 《汽车电气设备构造与维修》课件 项目六 汽车辅助电器设备
- 双手向前投掷实心球 课件
- 第六章 回归分析课件
- 学校食堂食品验收制度
- 期中阶段性练习(一~四单元)(试题)-2024-2025学年五年级上册数学苏教版
- 医疗设备供货安装调试培训、售后组织方案
- 2024秋期国家开放大学专科《EXCEL在财务中的应用》一平台在线形考(形考作业一至四)试题及答案
- 2024年云南德宏州州级事业单位选调工作人员历年高频难、易错点500题模拟试题附带答案详解
- 2024年秋新鲁科版三年级上册英语课件 Unit 6 lesson 1
- 英语国家概况-Chapter10-government解析
评论
0/150
提交评论