【Windows编程】Windows Socket API介绍
01、目录
目录
- 01、目录
- 02、浅谈Socket(套接字)
- 03、浅谈TCP/IP七层模型
- 04、浅谈三次握手,四次挥手
- 05、Socket API介绍
- 5.1 WSAStartup
- 5.2 WSAClearup
- 5.3 socket
- 5.4 closesocket
- 5.5 bind
- 5.6 Listen
- 5.7 accept
- 5.8 connect
- 5.9 send
- 5.10 recv
- 06、附录
- 07、总结
02、浅谈Socket(套接字)
socket(插座)编程是一门技术,它主要是在网络通信中经常用到。英文单词意思是“插座”。
为啥不是套接字呢?不是都叫套接字嘛?
插座:意为连接两端的枢纽,插座与电器相连,然后他们可以通电。
既然是一门技术,由于现在是面向对象的编程,一些计算机行业的大神通过抽象的理念,在现实中通过反复的理论或者实际的推导,提出了抽象的一些通信协议,基于tcp/ip协议,提出大致的构想,一些泛型的程序大牛在这个协议的基础上,将这些抽象化的理念接口化,针对协议提出的每个理念,专门的编写制定的接口,与其协议一一对应,形成了现在的socket标准规范,然后将其接口封装成可以调用的接口,供开发者使用
目前,开发者开发出了很多封装的类来完善socket编程,都是更加方便的实现刚开始socket通信的各个环节,所以我们首先必须了解socket的通信原理,只有从本质上理解socket的通信,才可能快速方便的理解socket的各个环节,才能从底层上真正的把握
03、浅谈TCP/IP七层模型
要理解socket必须的得理解tcp/ip,它们之间好比送信的线路和驿站的作用,比如要建议送信驿站,必须得了解送信的各个细节。
TCP/IP协议不同于iso的7个分层,它是根据这7个分层,将其重新划分,好比打扫卫生,本来有扫帚,垃圾斗,抹布,涂料,盆栽等就好比OSI的标准几个分层,tcp/ip根据用途和功能,将扫帚,垃圾斗放到粗略整理层,抹布涂料放到中度整理层,盆栽放到最终效果层。这里TCP/IP也对OSI的网络模型层进行了划分:大致如下:
osi模型:
TCP/IP协议参考模型把所有的TCP/IP系列协议归类到四个抽象层中
应用层:TFTP,HTTP,SNMP,FTP,SMTP,DNS,Telnet 等等
传输层:TCP,UDP
网络层:IP,ICMP,OSPF,EIGRP,IGMP
数据链路层:SLIP,CSLIP,PPP,MTU
每一抽象层建立在低一层提供的服务上,并且为高一层提供服务,看起来大概是这样子的:
通过上面的图形,由于底一层的需要向高一层的提供服务,我们大致的理解应用程序需要传输层的tcp和网络层的ip协议提供服务,但是我们这章要分析的socket它是在tcpip协议的那一部分呢,就好比,我们的通讯线路已经有明确的规定,我们的驿站要设计在哪个地方一样
到目前为止,大致的了解了应用程序和tcpip协议的大致关系,我们只是知道socket编程是在tcp/IP上的网络编程,但是socket在上述的模型的什么位置呢。这个位置被一个天才的理论家或者是抽象的计算机大神提出并且安排出来
我们可以发现socket就在应用程序的传输层和应用层之间,设计了一个socket抽象层,传输层的底一层的服务提供给socket抽象层,socket抽象层再提供给应用层,问题又来了,应用层和socket抽象层之间和传输层,网络层之间如何通讯的呢,了解这个之前,我们还是回到原点
要想理解socket编程怎么通过socket关键词实现服务器和客户端通讯,必须得实现的了解tcp/ip是怎么通讯的,在这个的基础上在去理解socket的握手通讯
04、浅谈三次握手,四次挥手
TCP/IP的三次握手与四次挥手,这里只做简单了解,不深入,本人能力有限,确实不适合深入探讨。
第一次握手:客户端尝试连接服务器,向服务器发送syn包,syn=j,客户端进入SYN_SEND状态等待服务器确认
第二次握手:服务器接收客户端syn包并确认(ack=j+1),同时向客户端发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手
根据TCP/IP的三次握手,socket也定义了三次握手,也许是参考tcp的三次握手,一些计算机大神们画出了socket的三次握手的模型图:
在上面图的基础上,如果我们得到上面的图形,需要我们自己开发一些接口,来满足上面的通讯的三次握手,问题就出来了,我们会需要开发哪些函数?这个等下说,先看一下四次挥手!
TCP/IP四次挥手如下图所示:
1、a 时刻,A 向 B 发出 FIN 包,表示自己没有数据要发送了
2、b 时刻,B 收到 FIN 包,回复 FIN ACK,表示收到了 A 的 FIN 包,不会再接收 A 的数据了
3、B 在发完 FIN ACK 后,可能还有数据要发给 A,这个数据是不能停止发送的,有数据还是需要继续发送
4、d 时刻,B 发完了数据,也发出 FIN 包,告诉 A 自己的数据发完了,不再发送数据了
5、e 时刻,A 收到了 B 的 FIN 包,知道 B 也没有数据要发送了,回复 FIN ACK。此时,连接可以断开了
建连只需要交互三次,断连却需要四次,这是为什么呢?其实断开连接和建立连接还是不一样的。建连的时候,只要双方都告知对方自己准备好了就可以,但是断连的时候,一方提出要断开连接,不再发数据,另一方不能立即断开,因为这一方可能还有数据要发送,直到数据全部发送完成后才能确认断开。
扯了这么多,其实就是为了开始下面的内容,有一个大概的了解,在某些地方,可以说的一般点,不用太细的优化语言。
05、Socket API介绍
介绍了套接字、三次握手、四次挥手、TCP/IP七层模型,大概也知道socket在这里面担当这什么角色了吧。
5.1 WSAStartup
int WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData );
函数说明:
使用Socket的程序在使用Socket之前必须调用WSAStartup函数。该函数的第一个参数指明程序请求使用的Socket版本,其中高位字节指明副版本、低位字节指明主版本;操作系统利用第二个参数返回请求的Socket的版本信息。当一个应用程序调用WSAStartup函数时,操作系统根据请求的Socket版本来搜索相应的Socket库,然后绑定找到的Socket库到该应用程序中。以后应用程序就可以调用所请求的Socket库中的其它Socket函数了。该函数执行成功后返回0。
5.2 WSAClearup
int WSACleanup (void);
函数说明:
应用程序在完成对请求的Socket库的使用后,要调用WSACleanup函数来解除与Socket库的绑定并且释放Socket库所占用的系统资源。
5.3 socket
SOCKET socket(int af, int type, int protocol );
函数说明:
应用程序调用socket函数来创建一个能够进行网络通信的套接字。第一个参数指定应用程序使用的通信协议的协议族,对于TCP/IP协议族,该参数置PF_INET;第二个参数指定要创建的套接字类型,流套接字类型为SOCK_STREAM、数据报套接字类型为SOCK_DGRAM;第三个参数指定应用程序所使用的通信协议。该函数如果调用成功就返回新创建的套接字的描述符,如果失败就返回INVALID_SOCKET。套接字描述符是一个整数类型的值。每个进程的进程空间里都有一个套接字描述符表,该表中存放着套接字描述符和套接字数据结构的对应关系。该表中有一个字段存放新创建的套接字的描述符,另一个字段存放套接字数据结构的地址,因此根据套接字描述符就可以找到其对应的套接字数据结构。每个进程在自己的进程空间里都有一个套接字描述符表但是套接字数据结构都是在操作系统的内核缓冲里。
5.4 closesocket
int closesocket(SOCKET s );
函数说明:
closesocket函数用来关闭一个描述符为s套接字。由于每个进程中都有一个套接字描述符表,表中的每个套接字描述符都对应了一个位于操作系统缓冲区中的套接字数据结构,因此有可能有几个套接字描述符指向同一个套接字数据结构。套接字数据结构中专门有一个字段存放该结构的被引用次数,即有多少个套接字描述符指向该结构。当调用closesocket函数时,操作系统先检查套接字数据结构中的该字段的值,如果为1,就表明只有一个套接字描述符指向它,因此操作系统就先把s在套接字描述符表中对应的那条表项清除,并且释放s对应的套接字数据结构;如果该字段大于1,那么操作系统仅仅清除s在套接字描述符表中的对应表项,并且把s对应的套接字数据结构的引用次数减1。
closesocket函数如果执行成功就返回0,否则返回SOCKET_ERROR。
5.5 bind
int bind(SOCKET s, const struct sockaddr FAR *name, int namelen );
函数说明:
当创建了一个Socket以后,套接字数据结构中有一个默认的IP地址和默认的端口号。一个服务程序必须调用bind函数来给其绑定一个IP地址和一个特定的端口号。客户程序一般不必调用bind函数来为其Socket绑定IP地址和断口号。该函数的第一个参数指定待绑定的Socket描述符;第二个参数指定一个sockaddr结构,该结构是这样定义的:
struct sockaddr { u_short sa_family; char sa_data[14]; };
sa_family指定地址族,对于TCP/IP协议族的套接字,给其置AF_INET。当对TCP/IP协议族的套接字进行绑定时,我们通常使用另一个地址结构:
struct sockaddr_in {short sin_family;u_short sin_port;struct in_addr sin_addr;char sin_zero[8];};
其中sin_family置AF_INET;sin_port指明端口号;sin_addr结构体中只有一个唯一的字段s_addr,表示IP地址,该字段是一个整数,一般用函数inet_addr()把字符串形式的IP地址转换成unsigned long型的整数值后再置给s_addr。有的服务器是多宿主机,至少有两个网卡,那么运行在这样的服务器上的服务程序在为其Socket绑定IP地址时可以把htonl(INADDR_ANY)置给s_addr,这样做的好处是不论哪个网段上的客户程序都能与该服务程序通信;如果只给运行在多宿主机上的服务程序的Socket绑定一个固定的IP地址,那么就只有与该IP地址处于同一个网段上的客户程序才能与该服务程序通信。我们用0来填充sin_zero数组,目的是让sockaddr_in结构的大小与sockaddr结构的大小一致。
5.6 Listen
int listen( SOCKET s, int backlog );
函数说明:
服务程序可以调用listen函数使其流套接字s处于监听状态。处于监听状态的流套接字s将维护一个客户连接请求队列,该队列最多容纳backlog个客户连接请求。假如该函数执行成功,则返回0;如果执行失败,则返回SOCKET_ERROR。
5.7 accept
SOCKET accept(SOCKET s, struct sockaddr FAR *addr, int FAR *addrlen );
函数说明:
服务程序调用accept函数从处于监听状态的流套接字s的客户连接请求队列中取出排在最前的一个客户请求,并且创建一个新的套接字来与客户套接字创建连接通道,如果连接成功,就返回新创建的套接字的描述符,以后与客户套接字交换数据的是新创建的套接字;如果失败就返回INVALID_SOCKET。该函数的第一个参数指定处于监听状态的流套接字;操作系统利用第二个参数来返回新创建的套接字的地址结构;操作系统利用第三个参数来返回新创建的套接字的地址结构的长度。
5.8 connect
int connect(SOCKET s, const struct sockaddr FAR *name, int namelen );
函数说明:
客户程序调用connect函数来使客户Socket s与监听于name所指定的计算机的特定端口上的服务Socket进行连接。如果连接成功,connect返回0;如果失败则返回SOCKET_ERROR。
5.9 send
int send(SOCKET s, const char FAR *buf, int len, int flags
不论是客户还是服务器应用程序都用send函数来向TCP连接的另一端发送数据。客户程序一般用send函数向服务器发送请求,而服务器则通常用send函数来向客户程序发送应答。该函数的第一个参数指定发送端套接字描述符;第二个参数指明一个存放应用程序要发送数据的缓冲区;第三个参数指明实际要发送的数据的字节数;第四个参数一般置0。这里只描述同步Socket的send函数的执行流程。当调用该函数时,send先比较待发送数据的长度len和套接字s的发送缓冲区的长度,如果len大于s的发送缓冲区的长度,该函数返回SOCKET_ERROR;如果len小于或者等于s的发送缓冲区的长度,那么send先检查协议是否正在发送s的发送缓冲中的数据,如果是就等待协议把数据发送完,如果协议还没有开始发送s的发送缓冲中的数据或者s的发送缓冲中没有数据,那么send就比较s的发送缓冲区的剩余空间和len,如果len大于剩余空间大小send就一直等待协议把s的发送缓冲中的数据发送完,如果len小于剩余空间大小send就仅仅把buf中的数据copy到剩余空间里(注意并不是send把s的发送缓冲中的数据传到连接的另一端的,而是协议传的,send仅仅是把buf中的数据copy到s的发送缓冲区的剩余空间里)。如果send函数copy数据成功,就返回实际copy的字节数,如果send在copy数据时出现错误,那么send就返回SOCKET_ERROR;如果send在等待协议传送数据时网络断开的话,那么send函数也返回SOCKET_ERROR。要注意send函数把buf中的数据成功copy到s的发送缓冲的剩余空间里后它就返回了,但是此时这些数据并不一定马上被传到连接的另一端。如果协议在后续的传送过程中出现网络错误的话,那么下一个Socket函数就会返回SOCKET_ERROR。(每一个除send外的Socket函数在执行的最开始总要先等待套接字的发送缓冲中的数据被协议传送完毕才能继续,如果在等待时出现网络错误,那么该Socket函数就返回SOCKET_ERROR)
注意:在Unix系统下,如果send在等待协议传送数据时网络断开的话,调用send的进程会接收到一个SIGPIPE信号,进程对该信号的默认处理是进程终止。
5.10 recv
int recv(SOCKET s, char FAR *buf, int len, int flags );
不论是客户还是服务器应用程序都用recv函数从TCP连接的另一端接收数据。该函数的第一个参数指定接收端套接字描述符;第二个参数指明一个缓冲区,该缓冲区用来存放recv函数接收到的数据;第三个参数指明buf的长度;第四个参数一般置0。这里只描述同步Socket的recv函数的执行流程。当应用程序调用recv函数时,recv先等待s的发送缓冲中的数据被协议传送完毕,如果协议在传送s的发送缓冲中的数据时出现网络错误,那么recv函数返回SOCKET_ERROR,如果s的发送缓冲中没有数据或者数据被协议成功发送完毕后,recv先检查套接字s的接收缓冲区,如果s接收缓冲区中没有数据或者协议正在接收数据,那么recv就一直等待,只到协议把数据接收完毕。当协议把数据接收完毕,recv函数就把s的接收缓冲中的数据copy到buf中(注意协议接收到的数据可能大于buf的长度,所以在这种情况下要调用几次recv函数才能把s的接收缓冲中的数据copy完。recv函数仅仅是copy数据,真正的接收数据是协议来完成的),recv函数返回其实际copy的字节数。如果recv在copy时出错,那么它返回SOCKET_ERROR;如果recv函数在等待协议接收数据时网络中断了,那么它返回0。
注意:在Unix系统下,如果recv函数在等待协议接收数据时网络断开了,那么调用recv的进程会接收到一个SIGPIPE信号,进程对该信号的默认处理是进程终止。
关于Windows 这边socket的相关API就大概这些。然后下面做下总结。
06、附录
上面图片来源百度,侵权即删!
本文API 借鉴博文:Windows Socket API介绍
07、总结
TCP/IP 服务器、客户端,分为几个固定步骤,这里我总结一下,并用API表示出来。
服务器
(Linux)
1、创建套接字(socket)
2、初始化结构体
3、绑定(bind)
4、监听(listen)
5、接受客户端连接(accept)
6、数据交互(recv/send)
7、断开连接(close)
8、关闭套接字(close)(windows)
1、初始化socket包(WSAStartup)
2、创建套接字(socket)
3、绑定(bind)
4、监听(listen)
5、接受客户端连接(accept)
6、数据交互(recv/send)
7、断开连接(closesocket)
8、关闭套接字(closesocket)
客户端
(Linux)
1、创建套接字(socket)
2、初始化结构体
3、连接服务器(connect)
4、数据交互(recv/send)
5、关闭套接字(close)(Windows)
1、初始化socket包(WSAStartup)
2、创建套接字(socket)
3、连接服务器(connect)
4、数据交互(recv/send)
5、关闭套接字(closesocket)
本篇文章只是简单介绍下socket API,并没有示例,但是按照上面我总结的步骤来做是没有问题的,不管是Linux 还是 Windows上面,博主都是亲手编程的。
如果上面内容有问题,欢迎在评论或者私我告知下,感谢。
版权声明:转载请注明出处,谢谢!
【Windows编程】Windows Socket API介绍相关推荐
- Windows编程—Windows驱动中定时器的使用
文章目录 Windows编程-Windows驱动中定时器的使用 前言 代码 简单版 升级版 程序效果 Windows编程-Windows驱动中定时器的使用 前言 定时器操作是应用编程中非常常见的操作, ...
- windows编程常用系统API函数
windows编程常用API的函数 1. API之网络函数 2. API之消息函数 3. API之文件处理函数 4. API之打印函数 5. API之文本和字体函数 6. API之菜单函数 7. AP ...
- Windows编程—Windows驱动开发环境搭建
文章目录 前言 步骤 步骤一 步骤二 步骤三 连接测试 步骤四 步骤五 总结 前言 作为一个编写Windows程序的开发人员,对Windows驱动开发 并非必需要掌握,但是掌握 Windows驱动开发 ...
- windows中hid操作api介绍
1.获取设备信息,在建立连接时 HidD_GetAttributes HidD_GetHidGuid HidD_GetIndexedString HidD_GetManufacturerString ...
- Windows编程 Windows程序的生与死(下)
再谈程序之"死" 记得在第二回中我对程序的"死"只是一句话带过,因为我还没有铺垫好,好了现在我们可以详细的分析一下这个过程了. 这还要从while消息循环说起, ...
- C/C++:Windows编程—Windows RPC 传递自定义数据类型、自定义数据类型数组、指针数组
前言 该篇博文不是讲Windows rpc入门的.是笔者在实际使用Windows RPC时 所遇到的问题,以及解决方法. 笔者有这样的需求,需要从RPC Server获取大量数据,而且该数据是动态分配 ...
- Windows编程(2)
文章目录 windows有关的重要概念 句柄 窗口 Windows的消息机制 消息结构体 宽字符与Unicode 宽字符串的长度 ASCII 和 Unicode 兼容性问题 Windows数据类型 w ...
- Windows API编程(一)最基础的知识介绍:Windows编程基础
主要内容简介: 1. Windows 编程基础:开发环境和开发过程. 2. Windows事件驱动模型和消息机制. 3. Windows的资源:图标.光标.菜单.位图等. 4. Windows绘图:图 ...
- Windows下的socket编程
前言 经过一周的时间,我又回来啦,这周我主要学习的是Windows下的socket网络编程.本篇博客的内容包括socket的简介.TCP/IP协议的讲解.TCP socket编程实例.UDP sock ...
- 视频教程-精通Win32 API编程-Windows图形界面编程-C/C++
精通Win32 API编程-Windows图形界面编程 黄强老师,国家软件设计师,软件开发工程师,项目经理.产品经理.培训讲师. 创业合伙人,多年C.C++开发经验,尤擅长移动互联网项目的开发! 黄强 ...
最新文章
- springboot-嵌入式Servlet容器(Tomcat)源码分析以及容器切换
- 常用JQuery插件整理
- 计算机卡在无法显示网页,我的电脑上网上银行一直“无法显示网页”
- linux 忽略大小写的查找
- One River CEO:从长远来看比特币可能达到每枚50万美元
- java----监听器的作用_一、理解监听器的作用
- Oracle - TRUNC, ROUND, CEIL, FLOOR
- C#利用委托跨线程更新UI数据
- 「mac软件教程」:在 Mac 上免费减小 PDF 文件大小
- 爬虫进阶 -- 爬虫相关定义、反爬机制及其破解
- 这也许是你不曾留意过的 Mybatis 细节 1
- RocketMQ 集群部署模式 理论介绍
- windows 7 动态分区转基本分区绿色工具(Conver to basic disk)
- 线性回归 + 基础优化算法 动手学深度学习v2 pytorch
- 逝者:Django贡献最多的核心开发者Malcolm Tredinnick
- 东方欲晓,莫道君行早
- 网上买保险靠谱吗?线上保险和线下保险的区别在哪?
- java之实现简单的传入一个小数和一个整数,根据传入的整数,输出一个有整数位个小数的小数
- 苹果三星好“基友”:专利诉讼不影响元件供应
- Android 如何判断分屏模式是否开启或者多窗口显示
热门文章
- 程序员的终极幻想(三):做一只小小的蜗牛
- 计算机桌面不能显示器,电脑显示屏亮但是主机已开机无法显示桌面
- 李开复给中国大学生的第五封信—你有选择的权利
- Python使用百度地图API实现地点信息转换及房价指数热力地图
- 计算机文件夹里没有显示桌面,桌面没有图标显示的解决方法
- 求陈敏老师所著的《OPNET物联网仿真》中的模型IOT_Simulation.rar,万分感谢!
- atomic java_Java中Atomic类的使用分析
- 清理C盘空间,无需命令行,可清理几十G内存,实测有效
- 什么样的文案才算是好文案?
- 【手游】手游行业专业相关知识储备