热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Windows网络编程(一)

Windows网络编程(一):创建链接文章目录Windows网络编程(一):创建链接一、使用前的

Windows网络编程(一):创建链接


文章目录

    • Windows网络编程(一):创建链接
      • 一、使用前的准备
      • 二、使用中的初始化
      • 三、错误处理
      • 四、很重要的一个概念——IP寻址
      • 五、很重要的socket连接概念
      • 六、创建一个服务器的监听模式
        • 1. 服务器开启TCP的过程
        • 2. 客户端连接服务器的API函数
        • 3. UDP的数据传输连接过程
      • 七、关于socket编程中INADDR_ANY的理解
      • 八、开始传输数据
        • 两个传输层协议的数据传输
        • (一)TCP的数据传输
          • 1. 发送函数
          • 2. 接收函数
        • (二)UDP的数据传输
          • 1. 发送数据
          • 2. 接收数据
      • 九、错误处理


一、使用前的准备

  必须使用到的头文件是winsock2.hwinsock.h两个头文件中选择一个。

  需要注意的是,单纯的引入这个头文件是不能完成代码的编译的。因为winsock需要使用静态链接库WSOCK32.LIB进行静态链接。因此需要采用以下其中之一的方式将静态库进入,否则在编译的时候会出现以下的错误:

NetDemo-12ac36.o : error LNK2019: 无法解析的外部符号 __imp_WSAStartup,该符号在函数 main 中被引用
NetDemo-12ac36.o : error LNK2019: 无法解析的外部符号 __imp_WSACleanup,该符号在函数 main 中被引用
NetDemo-12ac36.o : error LNK2019: 无法解析的外部符号 __imp_WSAGetLastError,该符号在函数 main 中被引用

  • 第一种方法是在代码中进行连接:

#pragma comment(lib, "WSOCK32")

  • 第二种方法是在编译是进行连接:

clang++ .\NetDemo.cpp -o NetDemo.exe -L F:\uCard\VC6.0green\VC98\Lib\ -lWSOCK32

二、使用中的初始化

  包含头文件之后,需要的做的第二件事就是将winsock进行初始化,初始化的过程主要是使用这个函数加载合适的winsock DLL。然后才能进行使用,使用的初始化函数原型如下:

int WSAStartup(WORD wVersionRequested,LPWSADATA lpWSAData
);

  然后使用winsock进行socket编程,使用完winsock之后,最终需要将socket进行释放资源,使用的是以下的函数原型如下:

int WSACleanup();

本函数原型的使用实例是:

WSAData wsadata;
WSAStartup(MAKEWORD(2, 2), &wsadata);
WSACleanup();

三、错误处理

  在进行编程的时候需要对在网络中出现的问题进行错误检查和错误处理,在这里使用的函数原型如下所示:

int WSAGetLastError();

  这个函数接收的错误必须是在winsock被加载完成之后,才能进行错误的获取,否则不能获取错误。正是因为这个原因,这个函数调用需要在WSAStartup之后获取错误代码。

四、很重要的一个概念——IP寻址

  理解结构体——SOCKADDR_IN,这个结构体主要的是以下结构:

struct sockaddr_in
{short sin_family; // 协议栈u_short sin_port; // 端口号struct in_addr sin_addr; // 自己的IP地址char sin_zero[8];// 用于填充的字节
};

  接下来分别介绍以上结构体中的各字段:

  • sin_familysin\_familysin_family:在IP协议栈中的值为AF_INET
  • sin_portsin\_portsin_port:设置IP地址的端口号
  • sin_addrsin\_addrsin_addr:IP地址的一个结构体:很重要,以下就是针对这个IP地址进行处理。

  主要的问题就是将点分十进制转换成为一个长整型函数,主要的函数原型就是以下的函数:

unsigned long inet_addr(const char FAR * cp);

  这样可能存在一个问题,对于不同的主机厂商,他们设定的IP地址在本机中的序列可能有所不同,即我们所说的大端字节序和小端字节序的区别。然而在同一个网络上就需要将以上的IP地址进行统一,在这里我们统一使用大端字节序作为网络上IP的标准。

于是针对这一需求,就存在大量的函数用于解决这个问题,将主机字节序转换成为网络字节序。下列的四个API都能实现以上的需求:

u_long htonl(u_long hostlong);// 返回值就是最终的数据
int WSAHtonl(SOCKET s, u_long hostlong, u_long FAR * lpnetlong);// 返回值放置在lpnetlong中
u_short htons(u_short hostshort);
int WSAHtons(SOCKET s, u_short hostshort, u_short FAR * lpnetshort);

实现反方向转化的函数为:

u_long htonl(u_long netlong);// 返回值就是最终的数据
int WSAHtonl(SOCKET s, u_long netlong, u_long FAR * lphostlong);// 返回值放置在lpnetlong中
u_short htons(u_short netshort);
int WSAHtons(SOCKET s, u_short netshort, u_short FAR * lphostshort);

敬请期待

五、很重要的socket连接概念

  socket在网络编程中是一个很重要的概念,针对socket这个结构体有以下的结构:

SOCKET socket(int af; // 协议的地址族int type; // 套接字的类型int protocol; // 使用的协议
);

  其实我们不难知道,socket是传输层的一个概念,因此以上的结构体如果我们采用TCP/IP协议族的话,那么以上结构体中各字段的含义如下:

  • afafaf:这个值就设置为和以上相同AF_INET
  • typetypetype:这个值根据TCP和UDP的不同而设置为不同的关键字——对于TCP这个字段的值设置为SOCK_STREAMSOCK\_STREAMSOCK_STREAM,对于UDP这个字段的值设置为SOCK_DGRAMSOCK\_DGRAMSOCK_DGRAM ,其实也不难理解,因为对于TCP的传输就是使用的流式数据传输;而针对UDP采用的是数据包的形式进行数据传输
  • protocolprotocolprotocol:这个就是协议了,根据使用的传输层协议设置这个字段,若采用的是TCP则本字段设置为IPPROTO_TCPIPPROTO\_TCPIPPROTO_TCP,若使用的协议为UDP,那么本字段的值设置为IPPROTO_UDPIPPROTO\_UDPIPPROTO_UDP

六、创建一个服务器的监听模式

  这个过程让我们来梳理一下,我们所说的服务器本质上就是一个进程,如果想要被客户端连接,那么服务器必须在一个已知名称(其实就是一个socket上,我们知道socket就是IP地址+端口号,时刻注意这个需要绑定的地址服务就是服务器自己的IP)上进行监听。那么这样一来整个服务器需要做的工作就比较明了了——

1. 服务器开启TCP的过程


  • 将WinSock所提供的套接字和已知的名称绑定起来。在这里使用的APIbind()bind()bind()

int bind(SOCKET s, // 等待客户连接的套接字,是客户的套接字const struct sockaddr FAR * name, // 就是一个普通的缓冲区。根据使用的协议必须实际地填充一个地址缓冲区,并在调用时,将其转成一个sockaddrint namelen// 由协议决定的要传递的地址结构的长度
);

SOCKET s;
SOCKETADDR_IN tcpaddr;
int port = 5150;s = socket(AF_INET, sOCK_STREAM, IPPROTO_TCP);
tcpaddr.sin_family = AF_INET;
tcpaddr.sin_port = htons(port);
tcpaddr.sin_addr.s_addr = htonl(INADDR_ANY);bind(s, (SOCKADDR *)&tcpaddr, sizeof(tcpaddr));

  • 然后将绑定的已知的名称进行监听——这里使用的APIlisten()listen()listen(),以等待客户端的连接,监听的函数原型是以下:

int listen(SOCKET s, // 这个是被绑定的套接字int backlog // 被阻塞的连接的最大队列长度
);

  • 如果有客户端连接到这个服务器,那么服务器需要做的就是接受这个请求,并和他建立连接,这里使用的APIaccept()accept()accept()

SOCKET accept(SOCKET s,struct sockaddr FAR * addr, // 用来存储请求客户端的IP地址int FAR * addrlen //
);

2. 客户端连接服务器的API函数

客户端创建socketsocketsocket连接服务器的主要过程是以下几步:

  1. 创建一个socketsocketsocket

  2. 建立一个SOCKADDRSOCKADDRSOCKADDR的地址结构,这个地址是准备连接到服务器的IP地址,在TCP/IPTCP/IPTCP/IP协议中,这个地址结构就是监听服务器的IP地址和端口号

  3. 使用connectconnectconnect初始化客户机与服务器的连接

int connect(SOCKET s,const struct sockaddr FAR * name,int namelen
);

3. UDP的数据传输连接过程

对于UDPUDPUDP连接的serverserverserver的主要工作的流程是以下的几步:

1)初始化socketsocketsocket

2)将socketsocketsocket绑定自己的ipipip地址和端口

3)recvfromrecvfromrecvfrom以获取客户端的ipipip地址,然后进行通信

4)sendtosendtosendto发送数据

对于UDPUDPUDP连接的clientclientclient的主要步骤是以下几步:

1)初始化socketsocketsocket

2)使用sendtosendtosendto向服务器的socketsocketsocket发送数据

存在的疑问就是:QQ用户即使在对方离线的情况下,是怎样能够收取对方在自己离线时发送给自己的信息

七、关于socket编程中INADDR_ANY的理解

  实际上这个INADDR_ANY转换成为点分十进制是0.0.0.0,这个地址让人感觉就比较迷茫。在网络地址中设置0.0.0.0这个地址的用意实际上是这样的——

  我们知道一台主机可能有多个IP地址,就比如我们之前学到的在进行环回测试时候的保留地址127.0.0.1,这个地址是不可能出现在任何公网情况下的,当我们的主机连接网络时,就会获取路由器为我们分配的一个IP地址,这样一看,我们的就存在两个IP。还有其他更多的情况是可能服务器有多个网卡什么的可能会有更多的地址,因此一台主机的IP地址可能存在多个

  即然存在以上的问题,(需求)那么我们当然希望访问主机的任意一个IP地址都能够得到服务器的响应,这其实就是存在的问题。如果我们按照原来的思路,将服务器的socket只绑定在一个IP上,那么结果就是——客户端只能通过这个唯一的IP访问服务器,剩下的IP就不能进行访问了。按照原来的绑定方式,那么只能将每个IP地址都绑定一个socket进行管理,这样就会相当繁琐,因此有以的方法——

  为了解决这个问题,于是设置将服务器的socket与0.0.0.0进行绑定,这个特殊的IP地址表示的是本机的所有IP地址,这样理解的话,也就是说我们使用一个socket就监听了好多地址,而且最后只需要管理一个socket。这就大大简化了最后的管理过程。

八、开始传输数据


两个传输层协议的数据传输


(一)TCP的数据传输


1. 发送函数

在已经建立连接的套接字上发送数据的主要API是sendsendsend,这个函数的原型是以下的方式:

int send(SOCKET s,const char FAR * buf,//指向缓冲区的指针int len,//缓冲区的大小int flags
);

2. 接收函数

主要的套接字函数接收是主要是API是recvrecvrecv,这个函数的原型是以下的方式:

int recv(SOCKET s,char FAR * buf,int len,int flags
);

(二)UDP的数据传输


1. 发送数据

在发送的过程中,使用的接口是sendtosendtosendto,这个函数的原型是以下的方式:

int sendto(SOCKET s,const char FAR * buf,int len, int flags,const struct sockaddr * FAR * to,int len
);

2. 接收数据

在接收的过程当中,使用的接口是recvfromrecvfromrecvfrom,这个函数主要是以下的方式,通过这个函数可以获取发送方的IP地址和端口号.

int recvfrom(SOCKET s,char FAR * buf,int len, int flags,struct sockaddr FAR * from,int FAR * fromlen
);

九、错误处理

windowssocketwindows\quad socketwindowssocket编程当中,也不是所有的都是一帆风顺的,因此对于错误的处理也应当足够引起我们的重视,一般来说,我们可以通过函数来获取winsockwinsockwinsock的错误类型,从而定位错误。

int WSAGetLastError(void);

在连接过程当中发生错误之后,可以调用这个函数,就可以获取返回的错误的整数代码。这些值可能存在于WINSOCK1.HWINSOCK1.HWINSOCK1.HWINSOCK2.HWINSOCK2.HWINSOCK2.H中。


推荐阅读
  • 本文深入探讨了Linux系统中网卡绑定(bonding)的七种工作模式。网卡绑定技术通过将多个物理网卡组合成一个逻辑网卡,实现网络冗余、带宽聚合和负载均衡,在生产环境中广泛应用。文章详细介绍了每种模式的特点、适用场景及配置方法。 ... [详细]
  • UNP 第9章:主机名与地址转换
    本章探讨了用于在主机名和数值地址之间进行转换的函数,如gethostbyname和gethostbyaddr。此外,还介绍了getservbyname和getservbyport函数,用于在服务器名和端口号之间进行转换。 ... [详细]
  • MQTT技术周报:硬件连接与协议解析
    本周开发笔记重点介绍了在新项目中使用MQTT协议进行硬件连接的技术细节,涵盖其特性、原理及实现步骤。 ... [详细]
  • 360SRC安全应急响应:从漏洞提交到修复的全过程
    本文详细介绍了360SRC平台处理一起关键安全事件的过程,涵盖从漏洞提交、验证、排查到最终修复的各个环节。通过这一案例,展示了360在安全应急响应方面的专业能力和严谨态度。 ... [详细]
  • 优化联通光猫DNS服务器设置
    本文详细介绍了如何为联通光猫配置DNS服务器地址,以提高网络解析效率和访问体验。通过智能线路解析功能,域名解析可以根据访问者的IP来源和类型进行差异化处理,从而实现更优的网络性能。 ... [详细]
  • 本文详细介绍了Java中org.neo4j.helpers.collection.Iterators.single()方法的功能、使用场景及代码示例,帮助开发者更好地理解和应用该方法。 ... [详细]
  • Explore how Matterverse is redefining the metaverse experience, creating immersive and meaningful virtual environments that foster genuine connections and economic opportunities. ... [详细]
  • Explore a common issue encountered when implementing an OAuth 1.0a API, specifically the inability to encode null objects and how to resolve it. ... [详细]
  • 本文详细介绍如何使用Python进行配置文件的读写操作,涵盖常见的配置文件格式(如INI、JSON、TOML和YAML),并提供具体的代码示例。 ... [详细]
  • 本文详细介绍了如何在Linux系统上安装和配置Smokeping,以实现对网络链路质量的实时监控。通过详细的步骤和必要的依赖包安装,确保用户能够顺利完成部署并优化其网络性能监控。 ... [详细]
  • 本文介绍了如何在具备多个IP地址的FTP服务器环境中,通过动态地址端口复用和地址转换技术优化网络配置。重点讨论了2Mb/s DDN专线连接、Cisco 2611路由器及内部网络地址规划。 ... [详细]
  • 深入理解Cookie与Session会话管理
    本文详细介绍了如何通过HTTP响应和请求处理浏览器的Cookie信息,以及如何创建、设置和管理Cookie。同时探讨了会话跟踪技术中的Session机制,解释其原理及应用场景。 ... [详细]
  • 深入理解 SQL 视图、存储过程与事务
    本文详细介绍了SQL中的视图、存储过程和事务的概念及应用。视图为用户提供了一种灵活的数据查询方式,存储过程则封装了复杂的SQL逻辑,而事务确保了数据库操作的完整性和一致性。 ... [详细]
  • 本文详细介绍了Java编程语言中的核心概念和常见面试问题,包括集合类、数据结构、线程处理、Java虚拟机(JVM)、HTTP协议以及Git操作等方面的内容。通过深入分析每个主题,帮助读者更好地理解Java的关键特性和最佳实践。 ... [详细]
  • 邮件(带附件,模拟文件上传,跨服务器)发送核心代码1.测试邮件发送附件接口***测试邮件发送附件*@parammultipartFile*@return*@RequestMappi ... [详细]
author-avatar
拍友2502882547
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有