打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
Windows 下的socket(套接字编程)

前一段时间研究了下模拟网页登陆的相关资料,在此记录防遗忘。

Socket 源于unixSocket就像我们使用CDC作图一样,免去了程序直接和设备驱动程序打交道的麻烦。就是提供给我们用来操作底层硬件的接口函数。

工作方式大概描述如下:


Socket也即是我们通常所说的套接字,其存在于通信区域中。通信区域也叫地址族,是一个抽象的概念,主要用于把所有通过套接字通信的进程共有的特性综合在一起,套接字通常之和同一区域的套接字交换数据(当然不同区域的通过转换也能实现)。而Winsows平台下的socket只支持一个通信区域:AF_INET(网际域),这个域被使用网际协议的进程使用。这在后面和socket有关的函数很有体现。

说到socket通信就不得不说下关于字节序的问题了。我们知道现在的硬件平台,对于数据在内存的存储顺序都是低位先存(也就是我们通常所说的主机字节序)。而在socket有关函数的参数都是要去以高位先存的存储方式的数据(网络字节序)。那么就需要一个从主机字节需到网络字节序的转换过程。

套接字有三种类型:

流式套接字(SOCK_STREAM),基于TCP传输协议,面向连接,可靠的传输方式。

数据报式套接字(SOCK_DGRAM,基于UDP,面向无连接,不可靠。

原始套接字(SOCK_RAW)。

Windows socket经历了众多版本的升级变迁,建议使用2以上的版本。

下面就是Windows Socket的编程实现了:

客户端/服务端

,基于TCP(面向连接)socket编程

服务端:

1,加载套接字库(WSAStartUp

2,创建套接字(socket

3,将套接字绑定到本机的一个地址和端口上(bind

4,将套接字设为监听模式,准备接收客户端请求(listen

5,等待客户请求到来;当请求到来后,接收连接请求,返回一个新的对应于此次连接的套接字(accept

6,返回等待另一客户端的请求。

7,关闭套接字释放资源(closesocket

8,卸载套接字库释放资源(WSACleanUp

客户端:

1,加载套接字库(WSAStartUp

2,创建套接字(socket

3,向服务端发送请求(connect

4,和服务端进行通信(send/recv

5,关闭套接字释放资源(closesocket

6,卸载套接字库释放资源(WSACleanUp

图解大致如下:

二,基于UDP(面向无连接)的socket编程

服务端:

1,加载套接字库(WSAStartUp

2,创建套接字(socket

3,将套接字绑定到本机的一个地址和端口上(bind

4,等待接收数据(recvfrom

5,关闭套接字释放资源(closesocket

6,卸载套接字库释放资源(WSACleanUp

客户端:

1,加载套接字库(WSAStartUp

2,创建套接字(socket

3,向服务端发送数据(sento

4,关闭套接字释放资源(closesocket

5,卸载套接字库释放资源(WSACleanUp

三,相关函数讲解:

加载套接字库并进行版本协商

Int WSAStartup(WORD wVersionRequested,

//请求的版本号,低字节代表主版本,高字节代表副版本,一般我们用MAKEWORDx,y//宏来指定版本号,如:MAKEWORD2,1)代表2.1的版本

LPWSADATA lpWSAData

//out,一个WSADATA结构体指针,用于接收实际加载的套接字 库版本号

)

创建套接字

SOCKET socket(int af, //指定协议族,也即网际域,对于windows平台总是AF_INET 或 PF_INET

Int  type,//指定要创建的套接字类型

Int  protocol//指定协议类型,我们一般设为0,让他根据我们前两个参数自动设置

绑定到本机地址和端口

int bind(SOCKET s, //已创建的套接字描叙符

        const struct sockaddr FAR *name, //要绑定的本机地址信息

       Int namelen //第二个参数的长度

其中要注意第二个参数,由于第二个参数适用于所有网络协议,所以我们可以根据需要进行替换,如我们常常这样定义一个AF_INET 的地址信息:

SOCKADDR_IN  SrvAddr;

SrvAddr.family=AF_INET;

SrvAddr.port=hotns(666; //其中666代表端口号

SrvAddr.sin_addr.S_un.S_addr=htonl(192.168.1.101);

//SrvAddr.sin_addr.S_un.S_addr=INADDR_ANY;(指绑定到本机的任一快网卡上,并且////其本身就是网络字节序,故无需转换,我推荐这种写法)

 Inet_addrinet_ntoa函数

Unsigned long Inet_addr(sconst char FAR * cp);

Inet_addr可以把一个点分十进制表示的IP(如:192.168.1.101)转换为unsinged long 类型的数据,且该返回值可以直接赋值给S_addr

Char FAR * inet_ntoa(struct in_addr in);

Inet_ntoa 从他函数的声明就知道他完成一个和inet_addr相反的转换 。

将指定的套接字设为见听听模式

Int listen(SOCKET s,int backlog);

第一个参数 s: 已经创建的套接字描述符

第二个参数 backlog  设置等待连接队伍的最大长度,注意:不是一个端口上可以同时连接的数目。

Accept函数

就收客服端发送的连接请求

SOCKET accept(

SOCKET s,

Struct sockaddr FAR * addr,// 返回请求连接方的IP和端口信息

Int FAR * addrlen 

);

Send函数

通过一个已经建立连接的套接字发送数据

Int send(

SOCKET s,//注意:是以建立连接的套接字

Const char FAR * buf,//目的地IP和端口信息

Int len,

Int falgs//该设置影响发送行为,我们一般设为0

)

Recv 函数

Int recv(

SOCKET s,

Char FAR *buf,//发送数据的缓存地址

Int len,//发送数据长度

Int flags//该设置影响发送行为,我们一般设为0

)

Connect 函数

和一个特定的套接字建立连接

Int connect(

SOCKET s,

Const struct sockaddr FAR * name,//目的地址信息

Int namelen

)

Recvfrom函数

接受一次数据并保存数据源地址信息

Int recvfrom(

SOCKET s,

Char FAR*buf,

Int len,

Int flags,

Struct sockaddr FAR* from//用于保存数据源地址信息

Int FAR* fromlen

);

 注意:最后一个参数是in,out类型,我们要在传参之前赋初始值

如:int len=Sizeof(SOCKADDR);

Sendto函数

向以一个特定的目的方发送数据

Int sendto(

SOCKET s,

Const char FAR * buf,//要发送的数据

Int len,//数据长度

Int flags,

Connect struct sockaddr FAR * to,//目的地址信息

Int tolen

)

前面我们说了在socket通信中都采用网络字节序(高位先存),那么在实际的编程过程中必然少不了转换函数,这里介绍两个,htons和htonl

 U_short htons(u_short hostshort);

 U_long htonl(u_long hostlong);

功能:把一个无符号短型/长性主机字节序的数据转换为网络字节序

好了,基础知识已经准备完毕了,下面是两个例子可以帮助理解

例子一:简单聊天程序(《摘自孙鑫VC++深入详解》)

  1. 服务端  
  2.   
  3. #include <Winsock2.h>   
  4. #include <stdio.h>   
  5.   
  6. void main()  
  7. {  
  8.     WORD wVersionRequested;  
  9.     WSADATA wsaData;  
  10.     int err;  
  11.       
  12.     wVersionRequested = MAKEWORD( 1, 1 );  
  13.       
  14.     err = WSAStartup( wVersionRequested, &wsaData );  
  15.     if ( err != 0 ) {  
  16.         return;  
  17.     }  
  18.       
  19.   
  20.     if ( LOBYTE( wsaData.wVersion ) != 1 ||  
  21.         HIBYTE( wsaData.wVersion ) != 1 ) {  
  22.         WSACleanup( );  
  23.         return;   
  24.     }  
  25.     SOCKET sockSrv=socket(AF_INET,SOCK_DGRAM,0);  
  26.   
  27.     SOCKADDR_IN addrSrv;  
  28.     addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);  
  29.     addrSrv.sin_family=AF_INET;  
  30.     addrSrv.sin_port=htons(6000);  
  31.   
  32.     bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));  
  33.   
  34.     char recvBuf[100];  
  35.     char sendBuf[100];  
  36.     char tempBuf[200];  
  37.   
  38.     SOCKADDR_IN addrClient;  
  39.     int len=sizeof(SOCKADDR);  
  40.   
  41.     while(1)  
  42.     {  
  43.         recvfrom(sockSrv,recvBuf,100,0,(SOCKADDR*)&addrClient,&len);  
  44.         if('q'==recvBuf[0])  
  45.         {  
  46.             sendto(sockSrv,"q",strlen("q")+1,0,(SOCKADDR*)&addrClient,len);  
  47.             printf("Chat end!\n");  
  48.             break;  
  49.         }  
  50.         sprintf(tempBuf,"%s say : %s",inet_ntoa(addrClient.sin_addr),recvBuf);  
  51.         printf("%s\n",tempBuf);  
  52.         printf("Please input data:\n");  
  53.         gets(sendBuf);  
  54.         sendto(sockSrv,sendBuf,strlen(sendBuf)+1,0,(SOCKADDR*)&addrClient,len);  
  55.     }  
  56.     closesocket(sockSrv);  
  57.     WSACleanup();  
  58. }  

  1. 客户端:  
  2.   
  3. #include <Winsock2.h>   
  4.   
  5. #include <stdio.h>   
  6.   
  7. void main()  
  8.   
  9. {  
  10.   
  11. WORD wVersionRequested;  
  12.   
  13. WSADATA wsaData;  
  14.   
  15. int err;  
  16.   
  17. wVersionRequested = MAKEWORD( 1, 1 );  
  18.   
  19. err = WSAStartup( wVersionRequested, &wsaData );  
  20.   
  21. if ( err != 0 ) {  
  22.   
  23. return;  
  24.   
  25. }  
  26.   
  27. if ( LOBYTE( wsaData.wVersion ) != 1 ||  
  28.   
  29.         HIBYTE( wsaData.wVersion ) != 1 ) {  
  30.   
  31. WSACleanup( );  
  32.   
  33. return;   
  34.   
  35. }  
  36.   
  37. SOCKET sockClient=socket(AF_INET,SOCK_DGRAM,0);  
  38.   
  39. SOCKADDR_IN addrSrv;  
  40.   
  41. addrSrv.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");  
  42.   
  43. addrSrv.sin_family=AF_INET;  
  44.   
  45. addrSrv.sin_port=htons(6000);  
  46.   
  47. char recvBuf[100];  
  48.   
  49. char sendBuf[100];  
  50.   
  51. char tempBuf[200];  
  52.   
  53. int len=sizeof(SOCKADDR);  
  54.   
  55. while(1)  
  56.   
  57. {  
  58.   
  59. printf("Please input data:\n");  
  60.   
  61. gets(sendBuf);  
  62.   
  63. sendto(sockClient,sendBuf,strlen(sendBuf)+1,0,  
  64.   
  65. (SOCKADDR*)&addrSrv,len);  
  66.   
  67. recvfrom(sockClient,recvBuf,100,0,(SOCKADDR*)&addrSrv,&len);  
  68.   
  69. if('q'==recvBuf[0])  
  70.   
  71. {  
  72.   
  73. sendto(sockClient,"q",strlen("q")+1,0,  
  74.   
  75. (SOCKADDR*)&addrSrv,len);  
  76.   
  77. printf("Chat end!\n");  
  78.   
  79. break;  
  80.   
  81. }  
  82.   
  83. sprintf(tempBuf,"%s say : %s",inet_ntoa(addrSrv.sin_addr),recvBuf);  
  84.   
  85. printf("%s\n",tempBuf);  
  86.   
  87. }  
  88.   
  89. closesocket(sockClient);  
  90.   
  91. WSACleanup();  
  92.   
  93. }  

例子二:获取网页HTML代码

  1. <P style="MARGIN-TOP: 0pt; MARGIN-BOTTOM: 0pt" class=p0></P><PRE class=cpp name="code"><PRE class=cpp name="code">#include <iostream>  
  2. #include "Winsock2.h"   
  3. #include "Windows.h"   
  4. #pragma comment(lib,"Ws2_32.lib")   
  5. using namespace std;  
  6.   
  7. void main()  
  8. {  
  9.     WORD wVersionRequested;  
  10.     WSADATA wsaData;  
  11.     wVersionRequested = MAKEWORD( 2, 2 );     
  12.     WSAStartup( wVersionRequested, &wsaData );  
  13.     //加载套接字库并进行版本协商   
  14.           
  15.       
  16.     SOCKET sock=socket(AF_INET,SOCK_STREAM,0);  
  17.     //创建套接字   
  18.   
  19.     HOSTENT* phostent;  
  20.     phostent=gethostbyname("www.baidu.com");  
  21.     //通过域名获取百度IP(指定要获取的地址)   
  22.       
  23.     SOCKADDR_IN SrvAddr;  
  24.     SrvAddr.sin_family=AF_INET;  
  25.     SrvAddr.sin_port=htons(80);  
  26.     SrvAddr.sin_addr.S_un.S_addr=*((DWORD*)phostent->h_addr_list[0]);  
  27.   
  28.     connect(sock,(SOCKADDR*)&SrvAddr,sizeof(SOCKADDR));  
  29.   
  30.     char RequireHeader[]="GET / HTTP/1.1\r\nAccept:*/*\r\nHost:www.baidu.com\r\n\r\n";  
  31.     // 定义Http请求头   
  32.   
  33.     send(sock,RequireHeader,strlen(RequireHeader),0);  
  34.     //发送请求   
  35.   
  36.     char recvBuf[1024];  
  37.     //用于接收返回的数据   
  38.     memset(recvBuf,0,1024);  
  39.   
  40.     int retval=1;  
  41.     while(retval)  
  42.     {//分多次接收   
  43.         retval=recv(sock,recvBuf,1024,0);  
  44.         //接收数据   
  45.         cout<<recvBuf;  
  46.         memset(recvBuf,0,1024);  
  47.     }  
  48.     cout<<endl;  
  49. }</PRE><BR>  
  50. <BR>  
  51. <PRE></PRE>  
  52. <BR>  
  53. <BR>  
  54. <P></P>  
  55. <SPAN style="FONT-FAMILY: '宋体'; FONT-SIZE: 10.5pt; FONT-WEIGHT: bold"></SPAN><BR>  
  56. <SPAN style="FONT-STYLE: normal; FONT-FAMILY: '宋体'; FONT-SIZE: 10.5pt; FONT-WEIGHT: bold"></SPAN>  
  57. <PRE></PRE>  
  58. <PRE></PRE>  
  59.   
  60. </PRE>  
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
Windows Socket编程简介_网络技术_程序开发_远播教育网
基于Visual C++的Winsock API研究
跨平台(Windows+Linux)的Socket通讯程序(一)—底层封装(转)
Socket网络编程指导
Windows API 教程(九) 网络编程
[精通WindowsSocket网络开发
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服