Windows Socket编程基础
摘要
在学习服务器的过程中需要使用到winsock以及Socket编程,这里记录一下学到的Socket基础
Socket套接字
Socket就是一种通过(IP + Port)并遵循TCP/UDP等协议的进程间通信的技术,类似于UNIX中的管道,详细的教程度娘上很多。
Socket主要有两类:
(1) 标准Socket,又称为Berkeley Socket,主要用于Linux/UNIX
(2) Winsock,主要用于Win平台Socket函数
以Winsock为例,有一系列基本的Socket函数用来实现Socket通信
(1) Socket // Socket 函数用于创建一个Socket套接字,函数原型如下 SOCKET socket(int af, //使用的协议族int type, //Socket类型int protocol //使用的协议地址 ); // Socket 协议族在计算机里表示为一个整数,可以取值AF_INET // Socket 类型有两种:SOCK_STREAM 和 SOCK_DGRAM,代表流Socket(Tcp)和数据报Socket(UDP)// 如果函数成功,返回一个Socket套接字,否则,返回INVALID_SOCKET,在建立Socket连接之前,必须先创建所需的Socket套接字 SOCKET s; s = socket(AF_INET, SOCK_STREAM, 0);
在QT使用socket函数的时候出现’undefined reference to `_imp__socket’ 错误,解决办法是在PRO文件中加入如下语句:
CONFIG += c++11 // 以防万一 LIBS += -lpthread libwsock32 libws2_32
而且在头文件中需要引入
<windows.h>
或者<winsock2.h>
让我们接着Socket往下说
(2) Connect // Connect函数用于尝试与远端建立一个Socket连接,函数原型如下: int connect(SOCKET s, //Socket描述字const struct sockaddr* name, //远端的地址int namelen //远端地址的长度 ); // 在进行连接时,远端地址是一个SOCKADDR的结构,定义为: struct sockaddr_in {short sin_family, //Socket组u_short sin_port, // 端口struct in_addr sin_addr, //IP地址char sin_zero[8] //结构的长度 } // 如果连接成功,返回0,否则返回SOCKET_ERROR。 // 对于非阻塞模式的Socket连接,返回结果通常都是SOCKET_ERROR, // 并且错误代码为WSAEWOULDBLOCK,表示连接正在进行,而不是一个真正的错误//建立连接通常都是由客户端发出连接请求 SOCKET s; SOCKADDR_IN ServerAddr; ServerAddr.sin_falimy = AF_INET; ServerAddr.sin_port = htons(80); //也可以自定义其他Port ServerAddr.sin_addr.s_addr = inet_addr("127.0.0.1"); connect(s, (SOCKADDR*)&ServerAddr, sizeof(ServerAddr));
在QT使用connect函数的时候,会与QT本身的connect函数冲突,解决办法是加::域分隔符,如下:
::connect(s,(SOCKADDR*)&ServerAddr, sizeof(ServerAddr));
让我们接着Connect往下说
(3) Send // Send函数用于在某个Socket上向远端发送数据,原型为: int send(SOCKET s,const char* buf, //存放发送数据的缓冲区int len, //将要发送的数据长度int flags //发送时使用的附加参数 ) // 如果发送成功,返回成功发送的字节数,否则返回SOCKET_ERROR(4) Recv int recv(SOCKET s,char* buf, //存放接受数据的缓冲区int len, //接受缓冲区的大小int flags //接受时使用的附加标志 )(5) Closesocket // 用于关闭不在需要的Socket int closesocket(SOCKET s //Socket套接字 ) // 成功返回0,否则返回SOCKET_ERROR(6) Listen//Listen用于在某个Socket上建立监听,函数原型为:int listen(SOCKET s,int backlog //缓存队列的长度)// 成功返回0,否则返回SOCKET_ERROR// 关于backlog参数,一般理解为协议层已经开始或已经完成连接的建立,但是应用程序还没有开始接受数据时,连接的数量未知。// 以前通常都是设为5,因为BSD4.2最大支持5,但是现在都是取SOMAXCONN,表示最大(7) Accept// 用于接受一条新的连接,注意:是接受连接而不是接受数据,原型为SOCKET accept(SOCKET s, //监听中的Socketstruct sockaddr* addr, //表示地址结构体的指针int* addrlen //地址结构体的长度)// 成功返回一个新的Socket套接字,否则返回SOCKET_ERROR(8) Bind//用于给一个Socket套接字分配一个本地协议地址,原型为int bind(SOCKET s,const struct sockaddr* name, //表示地址结构体的指针in namelen //地址结构体的程度)// 成功返回0,否则返回SOCKET_ERROR(9) Select// 用于检测Socket状态,主要用于高级的网络通信模型:int select(int nfds, //Winsock中此函数无意义fd_set* readfds, //进行可读检测的Socketfd_set* writefds,//进行可写检测的Socketfd_set* exceptfds, //进行异常检测的Socketconst struct timeval* timeout // 非阻塞模式中设置最大等待时间)// 成功返回0,否则返回SOCKET_ERROR
IP地址转换
主要有三种IP地址
(1) 无符号整数地址:127.0.0.1
(2) ASCII地址:“127.0.0.1”
(3) 域名地址:“localhost”转换方法:
(1) 整数地址到ASCII地址的转换#include <arpa/inet.h>// ASCII => INT int inet_aton(const char *straddr, struct in_addr* adrp); // 0表示失败,1表示成功// INT => ASCII char *inet_ntoa( struct in_addr inaddr); //NULL表示失败,其他任何值表示成功(2) 域名地址与整数地址的相互转换# include <netdb.h>// DOMAIN => INT struct hostent *gethostbyname( const char * name);// INT => DOMAIN struct hostent *gethostbyaddr( const char *addr, int len, int family);
字节转换
由于大端对齐和小端对齐的原因,不同的计算机系统在进行网络通信时需要一个统一的格式,也就是Big Endian格式,称之为“网络字节顺序”(network byte order),相对的本地计算机使用的数据格式为“本机字节顺序”(host byte order)
关于大小端对齐的问题,计算机专业的童鞋应该不会陌生,不清楚的可以看看这篇blog:
http://blog.csdn.net/yangcs2009/article/details/39698997在发送到网络之前,必须先转化为网络字节顺序,系统提供了一些相应的函数,用于字节顺序的转换,这些函数命名有一定的规律:h代表字节顺序(host或理解为本机顺序);n代表网络顺序(network)
u_long PASCAL FAR hton1 ( IN u_long hostlong); //本地字节转化为网络字节顺序(长整数) u_short PASCAL FAR htons(IN u_short hostshort); //本地字节转化为网络字节顺序(短整数) u_long PASCAL FAR ntoh1 (IN u_long netlong); //网络字节转化为本地字节顺序(长整数) u_short PASCAL FAR ntohs ( IN u_short netshort); //网络字节转化为本地字节顺序(短整数)
Q: 为什么在数据结构sockaddr_in中的sin_addr和sin_port要转换成网络字节顺序,而sin_family不需要呢?
A: 答案是:sin_addr和sin_port分别封装在包的IP 和 UDP层,但是sin_family只是被本机使用来决定数据结构中包含什么类型的地址,没有被发送到网络上基本Socket通信
服务器端基本流程:[初始化监听Socket]->[接受新的客户端连接]->[收发数据]->[关闭连接]
//!!注意:在Windows下必须包含头文件ws2tcpip.h ,否则会报错:socklen_t was not declared in this scope//!!注意:在Windows下执行socket相关操作前,必须初始化WSADATA!!!! WSADATA wsaData; int nRet; if((nRet = WSAStartup(MAKEWORD(2,2),&wsaData)) != 0){exit(0); }
(一) 初始化监听Socket
// (1) 初始化Socket SOCKET s; s = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP) // 使用流Socket,基于TCP协议// (2)绑定Socket SOCKADDR_IN ServerAddr; ServerAddr.sin_family = AF_INET; ServerAddr.sin_port = htons(9000); ServerAddr.sin_addr.s_addr = inet_addr("127.0.0.1");bind(s, (LPSOCKADDR)&ServerAddr, sizeof(ServerAddr));// (3)开始监听 listen(s, SOMAXCONN);
(二) 建立连接
// (1) 检测Socket状态:函数select可以检测相应的Socket状态,从而决定是否需要建立新的连接 fd_set readset; timeval timeout; timeout.tv_sec = 0; timeout.tv_usec = 0; FD_ZERO(&readset); FD_SET(s, &readset); int ret = select(FD_SETSIZE, &readset, NULL, NULL, &timeout); if(ret>0 && FD_ISSET(s, &readset)) {//新连接 }// (2) 建立新的连接:如果客户端有新的连接请求介入,则建立一个新的连接 // 注意:使用accept建立新的连接时,需要使用新的socket进行通信(temp) SOCKADDR_IN ServerAddr; int len = sizeof(ServerAddr); SOCKET temp; temp = accept(s, (SOCKADDR*) &ServerAddr, (socklen_t*)&len); if(temp == INVALID_SOCKET){//链接失败 }
(三) 收发数据
// (1) 检测读入数据 fd_set readset; timeval timeout; timeout.tv_sec = 0; timeout.tv_usec = 0; FD_ZERO(&readset); FD_SET(s, &readset); int ret = select(FD_SETSIZE, &readset, NULL, NULL, &timeout); if(ret>0 && FD_ISSET(s, &readset)) {//有新数据 } // (2) 接收数据 char buf[1024]; in ret; ret = recv(s, buf, 1024, 0);// (3) 检测发送数据 fd_set sendset; timeval timeout; timeout.tv_sec = 0; timeout.tv_usec = 0; FD_ZERO(&sendset); FD_SET(s, &sendset); int ret = select(FD_SETSIZE, &sendset, NULL, NULL, &timeout); if(ret>0 && FD_ISSET(s, &sendset)) {//可以发送新数据 } // (4) 发送数据 char buf[1024]; int ret; ret = send(s, buf, 1024, 0);
(四) 关闭连接
closesocket(s)
客户端基本流程:[初始化监听Socket]->[建立连接]->[收发数据]->[关闭连接]
(一) 初始化监听Socket
// (1) 初始化Socket SOCKET s; s = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP) // 使用流Socket,基于TCP协议// (2)绑定端口 SOCKADDR_IN ServerAddr; ServerAddr.sin_family = AF_INET; ServerAddr.sin_addr_s_addr = htonl(INADDR_ANY); addr.sin_port = htons(0); bind(s, (LPSOCKADDR)&ServerAddr, sizeof(ServerAddr)); // 客户端的IP地址和端口号都不需要固定,可以由系统分配,因此可以不适用bind函数绑定端口和协议// (3) 建立连接 SOCKADDR_IN ServerAddr; ServerAddr.sin_family = AF_INET; ServerAddr.sin_port = htons(9000); ServerAddr.sin_addr.s_addr = inet_addr("127.0.0.1"); connect(s, (SOCKADDR*)&ServerAddr, sizeof(ServerAddr));
Windows Socket编程基础相关推荐
- Windows黑客编程基础
俗话说:"万事开头难",编程也不例外,初学者如何入门关键要有一份正确的理论作指 导,下面的这篇文章虽不能说是至理名言,但我相信通过作者细腻的分析.讲解和引导, 定能给初学者起到启蒙 ...
- Windows Socket编程笔记之最简单的小Demo
Windows Socket编程的大致过程: 服务器端: ----过程-------------对应的API------- 0.初始化 | WSAStartup() 1.创建So ...
- Windows驱动编程基础教程
前言 本书非常适合熟悉Windows应用编程的读者转向驱动开发.所有的内容都从最基础的编程方法入手.介绍相关的内核API,然后举出示范的例子.这本书只有不到70页,是一本非常精简的小册子.所以 ...
- Windows驱动编程基础教程 (转)
Windows驱动编程基础教程(转) 我经常在网上遇到心如火燎的提问者.他们碰到很多工作中的技术问题,是关于驱动开发的.其实绝大部分他们碰到的"巨大困难"是被老牛们看成初级得 ...
- 计算机网络原理 实验1 《Windows Socket编程》
计算机网络原理 实验1 <Windows Socket编程> 一.实验目的 通过实验,熟悉并掌握计算机Windows 编程的基本知识,进一步加深对课堂所学基本内容的理解,掌握基本的Wind ...
- 楚狂人Windows驱动编程基础教程
版权声明 本书是免费电子书.作者保留一切权利.但在保证本书完整性(包括版权声明.前言.正文内容.后记.以及作者的信息),并不增删.改变其中任何文字内容的前提下,欢迎任何读者以任何形式(包括各种 ...
- 计算机图形编程基础,Windows图形编程基础.ppt
Windows图形编程基础 软件教研室 计算机图形学 第三章 Window图形编程基础 一.设备描述表DC及相关的MFC类 二.图形设备接口(GDI : Graphics device Interfa ...
- windows图形编程基础
windows图形编程基础 2010年08月30日 图形设备接口(GDI,Graphics Device Interface)的主要目标之一是支持在输出设备(如视频显示器.打印机和绘图仪)上的与设备无 ...
- Java从零开始学四十五(Socket编程基础)
一.网络编程中两个主要的问题 一个是如何准确的定位网络上一台或多台主机,另一个就是找到主机后如何可靠高效的进行数据传输. 在TCP/IP协议中IP层主要负责网络主机的定位,数据传输的路由,由IP地址可 ...
- 你得学会并且学得会的Socket编程基础知识
这一篇文章,我将图文并茂地介绍Socket编程的基础知识,我相信,如果你按照步骤做完实验,一定可以对Socket编程有更好地理解. 本文源代码,可以通过这里下载 http://files.cnblog ...
最新文章
- 完全基于Transformer的目标检测器,ICLR匿名论文实现视觉、检测统一
- 如何下载指定版本的torchtext
- css3 transform rotate-2D旋转
- ofstream、ifstream、fstream
- python 计量_距离度量以及python实现(二)
- 文本文件的读写(字符流)
- python笔记之function函数
- vue中根据搜索内容跳转到页面指定位置
- 【Elasticsearch】es 提高 搜索速度
- linux计划任务一小时,linux,计划任务,每小时执行一次(共7篇).docx
- Infosys:印度信息技术巨头公司
- 设置背景图片自动适应屏幕
- pythonnumpy矩阵详解_基于Python Numpy的数组array和矩阵matrix详解_python
- 思维导图使用技巧:手把手教你怎么画思维导图 #CSDN博文精选# #系统化学习# #IT技术# #知识图谱#
- java jnlp_Java Web Start实践:动态生成JNLP
- 真_u3d程序员,基本脚本语法篇
- 十五数码难题 A*算法及深度优先算法实现
- 【华为OD机试真题 JAVA】找城市
- 华为面试题c/c++
- android程序包不存在,Android应用开发Android studio 错误: 程序包 不存在
热门文章
- MOSFET | 如何看懂MOSFET手册?①
- 中新赛克数据可视化_大道至简,OceanEye大数据可视化工具的高效之道
- Laravel渴求式加载
- Vue中点击复制文本功能
- linux更新字体库失败,wps for linux 字体库缺失问题的解决办法
- 逢看必会的三子棋小游戏:原来可以这么简单
- 【人月神话】浅谈人月神话0.2什么是“人月”,为什么是“神话”?
- CentOS7+华为交换机+Winserver2012配置KMS服务器开机自启和自动激活
- 吊打本地搜索神器everthing,最快 最强的电脑本地搜索神器!
- 《暗时间》读书笔记及读后感