初探Socket网络编程.pdf

上传人:asd****56 文档编号:70331202 上传时间:2023-01-19 格式:PDF 页数:8 大小:204.51KB
返回 下载 相关 举报
初探Socket网络编程.pdf_第1页
第1页 / 共8页
初探Socket网络编程.pdf_第2页
第2页 / 共8页
点击查看更多>>
资源描述

《初探Socket网络编程.pdf》由会员分享,可在线阅读,更多相关《初探Socket网络编程.pdf(8页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。

1、初探初探初探初探 SocketSocketSocketSocket 网络编程网络编程网络编程网络编程出处:dschool 开发者学院 http:/学 C+之前,就想用 C+写一个网络软件,到达目的地的路很多,但我选择了学 C+来达到我的目的。虽然用 VB 或 Delphi 来写我的这个网络软件,会更快更便捷,不过我还是选择了 C+。走上 C+之路,要想写一个 Windows 下的软件,要学的实在太多了。首先要学 SDK,学会用 API 函数来写软件的界面,但学 SDK 要有 C 语言的 基础,如果 C 语言基础不好,还得返回去恶补 C 语言基础。学了 SDK,虽然能写 Windows 界面软件

2、,但制作流程太复杂,想要省事儿还得学 MFC总 之,这是一个漫长的过程。用 C+写网络程序,以前想都没想过,认为只用 C+既不能写 Windows 界面,也没有网络控件可用。说来惭愧,小弟我接触程序的时间也不短了,但编程水平仍然停留菜鸟水平,对程序的认识也不是一清二楚。最近一段时间一直在学习 C+和 SDK,也一直在调整自己的学习计划。C+看了本C+Primer Plus,对 C+面向对象的诸多特性有一定的认识,但动手能力差。后来又学了几天 SDK,其实我到是挺喜欢 SDK 的,看了几章Windows 程序设计,最大的难点是消息机制,虽然对消息机制的概念容易理解,但真要熟悉对消息的控制还需要大

3、量的实践。对 SDK 有了一定了解,又不想学 MFC,而到了这一步,最多也就能用 SDK 画个界面,写些一般的应用,虽然对我来说仍然有些困难,但到目前至少了解 了实现的方法。写界面,用 VB 或 Delphi 更容易,所以目前也没必要花时间在这上面。接下来就是怎样仅不用控件又不用 MFC 类库来实现网络编程,于是认 识了 socket。什么是 socket,它是一种实现方法,当然你也可以理解为是一种接口或是一种工具。就像我们从一个城市到达另一个城市,你可以选择坐火车去还是坐飞机去。进行网络编程有多种实现方法,但我只知道 socket 这一种方法,而这也是最普遍使用的一种方法。有了实现方法,根据

4、需求还会对这种方法进行改良,比如坐火车从一个城市到另一个城市,会对火车的速度进行改进,以便我们能更快的到达目的地。而 socket 从出现在现在,也有过一进改良,最初是 socket1.1,现在有了 socket2.1。我们可以使用 socket1.1也可以使用socket2.1,当然,经过改良后的 socket2.1比 socket1.1有多更多的功能。了解了 socket 的概念,那么怎样用 socket 来实现网络编程呢?socket 具体的实现代码是放在动态链接库文件(DLL)里的,这个文件名称为 wsock32.dll 和 WS2_32.dll,位于“windows/system32

5、/”文件夹下,其中 wsock32.dll 是 socket1.1的实现代码,WS2_32.dll 是socket2.1的实现代码。这两个DLL文件提供了socket网络编程的接口,在编译器的 winsock.h和 winsock2.h 中声明了这两个版本的接口函数,只要包含了其中一个头文件,就能调用 DLL来进行 socket 网络编程。或许因为某些文字的描述,导致上面这段话的解释有些不清不楚,但至少也能明白个大概。第一步是了解,那第二步就是实现了,怎么进行 socket 编 程,这主要是熟悉 winsock.h 和winsock2.h 头文件中声明的这些函数,比如 socket、bind、

6、listen、accept、send、recv 这些函数,不过要弄清这些函数的作用还得有一定的网络知识,这些网络知识包括什么是 TCP/IP协议、IPX/SPX 协议和 NetBIOS 协议,还有 HTTP、FTP、SMTP、POP3这些网络协议。下面该说说正题了,怎样用 socket 来进行网络编程,就是怎么使用 winsock.h 和 winsock2.h中的那些函数。本来前阵子也下了不少网络编 程的书,每本书都有讲 socket 编程,但一打开书目录,看到的全都是一些陌生的技术术语,让我有了畏惧心理,一直没认真看过。今晚发现在孙鑫的 VC+深入详解里有一章是讲 socket 网络编程的,

7、所以就准备认真的学习一下,因为孙鑫的书是以通俗易懂闻名的。一、七步实现服务器端程序的编写一、七步实现服务器端程序的编写第一步:选择 socket 版本,加载 socket 库(使用 WSAStartup 函数)第二步:创建套接字(使用 socket 函数)第三步:绑定 IP 和端口(使用 bind 函数)第四步:指定创建的套接字为监听模式(使用 listen 函数)第五步:接受客户端请求(使用 accept 函数)第六步:发送/接受数据(使用 send/recv 函数)第七步:关闭套接字(使用 closesocket 函数)以上是一个服务器端程序的编写流程,步骤虽然简单,每一步的作用都好理解,

8、但难的是熟悉这些函数的使用,这些函数里的参数设置搞得人头晕脑涨的,下面来一步一步的解释。第一步:使用 WSAStarup 函数选择 socket 库前面已经说到,socket 有两个版本,所以我们需要选择一个使用的版本,WSAPStarup 函数就是用来选择 socket 库版本的,其声明原型如下:1int WSAStartup(WSAStartup(WSAStartup(WSAStartup(2WORDWORDWORDWORD wVersionRequested,wVersionRequested,wVersionRequested,wVersionRequested,3LPWSADATAL

9、PWSADATALPWSADATALPWSADATAlpWSADatalpWSADatalpWSADatalpWSAData4););););第一个参数是选择要加载的版本,确定套接字库,第二个参数是指定要使用的套接字库,该函数会返回执行状态,成功则返回0。注意这句话中的“选择”和“指定”,这就好比我们在设置密码时要输入两次,第一次是设置密码,第二次是确认密码。在使用 WSAStartup 函数时,发现了一个问题,就是当为第一 个参数胡乱设置一个版本时,仍然为返回0,即成功选择了套接字库。经过实践证明,当 WSAStartup 找不到选择要加载的版本时,会自动选择socket1.1。第二步:使用

10、 socket 函数创建套接字为什么要创建套接字?这就好比问人为什么要吃饭一样。socket 函数的声明原型如下:5SOCKET socket(6int af,7int type,8int protocol9);第一个参数是设置地址簇,啥是地址簇呀,我不知道,只知道对于 TCP/IP 的套接字,这里必须设置为 AF_INET 或 PF_INET。第二个参数是指定套接字类型,在 socket1.1中有 SOCK_STREAM 和 SOCK_DGRAM 两种套接字类型,关于套接字类型的介绍可查看Internet 套接字的两种类型 一文,我的理解是设置套接字类型就是设置数据的传送方式,是以流格式传送

11、还是打包后传送。其中流格式套接字只发送一次,保证数据的完整性,且按发送顺序接 受,基于 TCP 协议;而数据报式套接字是打包发送,不保证数据完整性,可重复发送,直到接收方收到为止,基于 UDP 协议。第三个参数是指定使用地址簇相关的协议,比如 af 参数设为 AF_INET 或 PF_INET,就该指定为 TCP/IP 协议,不过我们可以将其值设置为0,让系统自动选择协议类型。如果需要指定协议时,这个值该怎么设置呢?可以用 getprotobyname 函数。socket 函数的使用示例代码如下:10 SOCKET socketServer;11 socketServer=socket(AF_

12、INET,SOCK_STREAM,getprotobyname(tcp);这才刚接触 socket 的第二个函数,就搞得晕晕的了,涉及的知识点不少,比如地址簇、套接字类型等,很明显要学网络编程,还得学好网络方面的理论知识,不过现在用不着花大量时间去打基础了,只要会用一些常用函数,了解函数中各类参数的大概作用就行了。第三步:使用 bind 函数绑定 IP 地址和端口IP 地址和端口的概念接触得比较多,IP 地址是互联网上一台计算机的名称,而端口,我们知道 HTTP 服务默认用的是80端口,FTP 服务用的是21 端口。要进行通信,就必须指定 IP地址和端口,就像要找到一个人必须要知道他的姓名和住

13、址一样。说到网络服务,我们知道的就比较多了,Telnet 服务默 认使用23端口,DNS 服务默认使用53端口,如果更多的了解常见的网络服务和使用的端口可看看以下几篇文章:网络服务常用网络端口列表之 udp 协议端口http:/ 协议端口大全http:/ CMD 命令提示符窗口中使用“netstat-an”命令来看一下自己的电脑当前使用了多少端口。其中1024以下的端口是分配给预定义的服务,如HTTP/FTP 之类的,我们编写网络程序时,要为程序指定1024以上的端口号。现在来看看 bind 函数的声明原型:12 int bind(13SOCKET s,14const struct socka

14、ddr FAR*name,15int namelen16);第一个参数是选择要绑定的套接定。第二个参数是一个结构体,在这个结构体中设置 IP 地址和端口,在这个参数中有一个“FAR”,这个好像是设置内存地址寻址方式,用于 DOS 和16位 Windows,可以无视,还是详细说说sockaddr 这个结构体吧,下面是 sockaddr 结构体的定义:17 struct sockaddr 18unsigned short sa_family;/地址簇19charsa_data14;/分配内存块20;21其中 sa_data14是指定分配14个字节来存储 IP 地址和端口信息,刚开始看到这个参数时有

15、些纳闷,要存储 IP 地址和端口信息,14个字节 怎么够呢,后来才发现这里保存的信息是转换过的,不是原始信息。在这个结构体中你会发现没有设置 IP 地址和端口的结构体成员,这是我们需要使用 sockaddr_in 结构体来接受 IP 地址和端口的设置,然后再将 sockaddr_in结构体转换为 sockaddr 结构体,这样在 sockaddr 结构体的 sa_data14成员分配的内存块中就保存了 IP 地址和端口的数据。下面是 sockaddr_in 结构体的定义:22 struct sockaddr_in23shortsin_family;24unsigned shortsin_por

16、t;25structin_addrsin_addr;26charsin_zero8;27;28在这个结构体中又有一个成员是结构体,in_addr 结构体的定义如下:29 struct in_addr 30union 31struct u_char s_b1,s_b2,s_b3,s_b4;S_un_b;32struct u_short s_w1,s_w2;S_un_w;33u_longS_addr;34 S_un;35;在 in_addr 结构体中,实际上是分别以 unsigned char,unsigned short 和 unsigned long 类型来保存 IP 地址信息,我们通常使用的

17、都是保存在 S_addr 中的值。对于 bind 函数第二个参数的设置,一口气用到了三个结构体,但最终在 bind 函数中设置的应该是一个 sockaddr 结构体。下面来个设置 bind 函数第二个参数的伪代码:36 SOCKADDR_IN addrSrv;37 addrSrv.sin_family=AF_INET;38 addrSrv.sin_port=端口号;39 addrSrv.sin_addr.S_addr=IP 地址;4041 bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR);42之所以写成伪代码,是因为我们不能直接为 addrSr

18、v.sin_port 和 addrSrv.sin_addr.S_addr 赋于类似“218.1.2.120”和“80”这样的字符串值或数值,因为 sin_port 是一个 unsigned short 类型,S_addr 是一个 unsigned long 类型。除此之外,对于这两个值的设置还涉及到一个叫字节顺序的概念。字节顺序是什么?以前从来没接触过这个概念,第一次接触是在 SDK 中,知道了高位字和低位字。对于高位字和低位字,我的理解是一个字节的数据分为低 位字和高位字,即低位字高位字等于一个字节的数据,再说得通俗一点,就像一个人的名字,分为姓和名,组合起来就是一个人的姓名,有了姓和名才是

19、一个有意 义的人名。但不同国家的人,姓名的表示方法也不同,中国人是姓在前,名在后,而外国人是名在前,姓在后。而在计算机中存储一个字节的数据使用不同的 CPU 也有不同的存储顺序,有的是高位字在前,低位字在后,有的是低位字在前,高位字在后,但对于基于 Intel 的 CPU,是低位字在前,高位字在后。这就是所 说的字节顺序。由此可知,不同的计算机其字节顺序也不同,对于计算机字节顺序,我们称之为主机字节顺序;而网络数据在传输时,也要保存数据,这些数据的存储也有字节顺序,其顺序为高位字在前低位字在后,。所以我们在用网络传输数据时,需要首先将主机字节顺序转换为网络字节顺序。好了,回到 bind 函数第

20、二个参数的设置,我们现在需要设置 sin_port 和 S_addr 的值,就得分二个步骤,第一步是将如“218.1.2.120”和“80”这样的字符串或数值转换为 sin_port 和S_addr 相应的数据类型,第二步是将转换后的数据类型中的数据从主 机字节顺序转换为网络字节顺序,不过使用 socket 中的函数可以将这两个步骤合二为一。下面是几个字节顺序转换函数:43 u_long htonl(u_long hostlong);/主机字节转换为网络字节,32比特整数4445 u_short htons(u_short hostshort);/主机字节转换为网络字节,16比特整数4647

21、u_long ntohl(u_long netlong);/网络字节转换为主机字节,32比特整数4849 u_short ntohs(u_short netshort);/网络字节转换为主机字节,16比特整数50515253 unsigned long inet_addr(const char FAR*cp);/将字符串值转换为 unsigned long 的网络字节5455 char FAR*inet_ntoa(struct in_addr in);/与 inet_addr 相反,转换为主机字节56那么设置 S_addr 和 sin_port 中的值该如何转换呢?IP 地址得用字符串来表示,

22、所以得用inet_addr 函数;端口号是一个数值,而 sin_port 是一个 unsigned short 数据类型的值,所以得用 htons 函数来转换。这下 bind 函数我们就可以设置第二个参数的值了,看下面的代码:57 SOCKADDR_IN addrSrv;58 addrSrv.sin_family=AF_INET;59 addrSrv.sin_port=htons(6000);60 addrSrv.sin_addr.S_addr=inet_addr(127.0.0.1);6162 bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR)

23、;费了这么大的劲儿来解释 bind 函数的第二个参数,真累人。bind 函数的第三个参数是设置第二个参数中 SOCKADDR 结构体的大小。第四步:用 listen 函数设置套接字为监听模式listen 函数的作用就好理解了,通过监听模式可以监控 bind 绑定的 IP 地址和端口上是否有数据请求,该函数的声明原型如下:63 int listen(64SOCKET s,65int backlog66);第二个参数是设置同一端口上可同时连接的数目,即同时接受多少个请求。第五步:用 accept 函数接受请求当客户端想要发送一个字符串到服务器端,通过 listen 函数可监听到这个请求,如果要接收

24、客户端发送过来的字符串,就要用 accept 函数来接受客户端的请求。accept 函数的声明原型如下:67 SOCKET accept(68SOCKET s,69struct sockaddr FAR*addr,70int FAR*addrlen71);accept 函数会返回一个对于此次连接的套接字类型,并将客户端的 IP 地址和端口信息存放到 sockaddr 结构体中。第三个参数是给 sockaddr 结构体分配一块内存,accept 函数的使用如下:72 SOCKET sockConn;73 SOCKADDR_IN addrClient;74 int len;7576 sockCon

25、n=accept(sockSrv,(SOCKADDR*)&addrClient,sizeof(SOCKADDR);服务器端接受了客户端的请求,那么两者就建立了连接,即可以进行数据传输了。第六步:用 send/recv 函数来发送和接收数据其函数声明原型如下:77 int send(78SOCKET s,79const char FAR*buf,80int len,81int flags82);8384 int recv(85SOCKET s,86char FAR*buf,87int len,88int flags89);这两个函数的使用就勿须多说了,只是想解释一个小问题。在前面已经说到,通过网

26、络传输数据,需要将主机字节转换为网络字节,那么使用 send 或 recv 函数来发送和接收数据是否也要进行字节顺序转换呢?答案是不。因为 send/recv 是 socket 的函数,会自动将主机字节转换为网络字节,照 目前来看,需要转换的就是 bind 函数中的 IP 地址字符器和端口数值。第七步:(1)用 closesocket 函数关闭套接字(2)用 WSACleanup 函数结束 socket 库的使用我们打开一个文件不用以后需要关闭这个文件,而使用了套接字和 socket 库后也要做个扫尾工作。二、五步实现客户端程序的编写二、五步实现客户端程序的编写第一步:选择 socket 版本

27、,加载 socket 库(使用 WSAStartup 函数)第二步:创建套接字(使用 socket 函数)第三步:接受客户端请求(使用 accept 函数)第四步:发送/接受数据(使用 send/recv 函数)第五步:关闭套接字(使用 closesocket 函数)很明显,客户端程序的编写少了两个步骤,分别是用 bind 函数绑定 IP 地址和端口以及设置套接字的监听模式,为什么?要弄清这个问题,可以回头看看 服务器端的 accept 函数,该函数的第二个参数是一个 sockaddr 结构体,这里面保存的就是客户端程序的 IP 地址和端口,服务器端在接受通信请求时 就获取了客户端的 IP 地址和端口。三、结束语三、结束语这是一个基于 TCP 的 socket 编程例子,弄清这个例子的使用我花了三个晚上,第一个晚上把书上的这部分内容看了一遍,有些犯晕,只了解了大致的 步骤,但弄不清这些函数里面各个参数的作用和设置,记不到脑子里,于是把书上的代码敲了一遍。这段代码敲得比较慢,每到一个函数的使用时都回头看看书中对 该函数的解释,这段代码敲下来后思路清晰些许,但记得还是不牢靠。第二个晚上和第三个晚上就是打字了,打了上面这么一大篇,又把整个过程理了一遍。看来这 写读书笔记确实如人所说,很费时,不过结果是我现在可以把书扔了也能写出这个例子的代码。

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 技术资料 > 其他杂项

本站为文档C TO C交易模式,本站只提供存储空间、用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。本站仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知淘文阁网,我们立即给予删除!客服QQ:136780468 微信:18945177775 电话:18904686070

工信部备案号:黑ICP备15003705号© 2020-2023 www.taowenge.com 淘文阁