C++ Winsock小结

Socket套接字

寻址方式和字节顺序

寻址方式

Winsock(Socket API)中,套接字地址结构定义如下:

struct sockaddr_in{

short     sin_family;//指定地址家族即地址格式。TCP/IP设置为AF_INET。

unsignedshort  sin_port;//端口号码。

struct in_addr  sin_addr;//IP地址。

char      sin_zero[8];//留作备用,需要指定为0。

};

 

其中结构成员变量sin_addr表示32位的IP地址结构,定义如下:

struct in_addr{

  union{

struct{

  unsigned char s_b1, s_b2, s_b3, s_b4;

}S_un_b;

struct{

  unsigned short s_w1, s_w2;

}S_un_w;

unsigned long S_addr;

 }S_un;

};

 

字节顺序

1、字节顺序格式

网络字节顺序(大端格式):低地址存放高位字节,高地址存放低位字节;

主机字节顺序(小端格式):低地址存放低字节,高地址存放高字节。

 

2、字节顺序转换函数

1.u_short htons(u_short hostshort);//将一个u_short类型的IP地址从主机字节顺序转换到网络字节顺序。

2.u_long htonl(u_long hostlong);//将一个u_long类型的IP地址从主机字节顺序转换到网络字节顺序。

3.u_long ntohl(u_long netlong);//将一个u_long类型的IP地址从网络字节顺序转换到主机字节顺序。

4.u_short ntohs(u_short netshort);//将一个u_short类型的IP地址从网络字节顺序转换到主机字节顺序。

5.unsigned long inet_addr(const char FAR *cp);//将一个字符串IP转换到以网络字节顺序排列的IP地址。

6.char FAR* inet_ntoa(struct in_addr in);//将一个以网络字节顺序排列的IP地址转换为一个字符串IP。

 

MFC中CSocket类中相关函数

1.创建套接字

使用afxsock.h下的CSocket类创建套接字对象本质是调用该类的构造函数进行创建,原型如下:

CSocket::CSocket();

如果用户需要创建套接字对象指针,则应该使用new关键字进行创建,代码如下:

CSocket *sock;//定义套接字指针对象。

sock=new CSocket;//使用new关键字创建套接字对象,返回对象的地址。

 

2.绑定地址信息

调用该类的函数Bind()将套接字对象与服务器地址信息绑定在一起。函数原型如下:

BOOL Bind(const SOCKADDR* lpSockAddr, intnSockAddrLen);

如果函数调用成功,则返回true;否则,返回false。

1.参数lpSockAddr指定将要绑定的服务器地址结构(指针可变,数据不可变);

2.参数nSockAddrLen表示地址结构的长度

 

服务器端,当地址信息绑定套接字成功后,还需要调用函数Listen()在指定端口监听客户端的连接请求。Listen()函数原型如下:

BOOL Listen(int nConnectionBacklog = 5);

参数nConnectionBacklog表示套接字监听客户端请求的最大数目,该参数有效范围是[1,5],默认为5,表示该套接字只能监听5个客户端所发送的连接请求。

 

3.连接服务器

客户端创建套接字成功以后,可以调用函数Connect()向服务器发送连接请求,函数原型如下:

BOOL Connect(const SOCKADDR* lpSockAddr,int nSockAddrLen);

函数调用成功后,返回true;否则,返回false。

1.参数lpSockAddr表示将连接的服务器地址结构。

2.参数nSockAddrLen表示地址结构的长度大小。

 

4.数据交换

无论是服务器,还是客户端都是通过函数Send()和Receive()进行数据交换,函数原型如下:

virtual int Send(const void* lpBuf, intnBufLen, int nFlags = 0);

virtual int Receive(void* lpBuf, intnBufLen, int nFlags = 0);

函数Send()用于发送指定缓冲区的数据;

函数Receive()用于接收对方发送的数据,并将数据存放在指定缓冲区中。

1.参数lpBuf表示数据缓冲区地址;

2.参数nBufLen表示数据缓冲区的大小;

3.参数nFlags表示数据发送或接收的标志,一般情况下,该参数均设置为0

 

5.关闭套接字对象

当服务器和客户端的通信完成以后,用户还必须调用函数Close()将套接字对象关闭。否则,程序可能在退出时发生错误。该函数原型如下:

virtual void Close();

 

Winsock编程

1.初始化和释放套接字库

所有的Winsock函数均是从动态链接库WS2_32.DLL中导出的,在头文件下边使用#pragma comment(lib, “LibName.lib”)显式链接LibName.lib库。语句如下:

#pragma comment(lib, “ws2_32.lib”)  //链接ws2_32.lib库,没有分号。

 

调用WSAStartup()对该库进行初始化,函数原型如下:

int WSAStartup(WORD  wVersionRequested, LPWSADATA  lpWSAData);

该函数调用成功,将返回0;否则,调用函数失败。

1.参数wVersionRequested表示当前套接字库的版本号;

2.参数lpWSAData指向结构体WSADATA的指针变量,表示获取到的套接字详细信息。

 

程序退出时,用户还应该调用函数WSACleanup()释放该套接字库,如下:

::WSACleanup();

 

2.创建套接字句柄

在Socket  API中,创建套接字句柄的函数是socket()。函数原型如下:

SOCKET socket(int af, int type, intprotocol);

1.参数af指定套接字所使用的地址格式,本章只能设置成AF_INET;

2.参数type表示套接字类型,SOCK_STREAM表示流式套接字(TCP),SOCK_DGRAM表示数据包套接字(UDP),SOCK_RAW表示原始套接字(未提及);

3.参数protocol:如果参数type指定TCP或UDP,该参数可以设置为0。

 

3.绑定地址信息

对于服务器而言,套接字创建成功后,还应该使用bind()函数将套接字与地址结构信息相关联,函数原型如下:

int bind(SOCKET s, const struct sockaddrFAR* name, int namelen);

1.参数s表示套接字句柄;

2.参数name表示地址结构信息的变量指针;

3.参数namelen表示地址结构的大小。

函数调用成功,则返回0;否则函数调用失败。

 

当服务器程序将套接字句柄绑定套接字地址成功后,调用函数listen()实现监听端口的功能,函数原型如下:

int listen(SOCKET s, int backlog);

1.参数s表示实现监听功能的套接字句柄;

2.参数backlog指定监听的最大连接数量。

注:该函数仅被用于流式套接字,如果多个客户端同时向服务器发送连接请求,并且超过了最大监听数,则客户端将返回错误代码

 

4.连接

客户端程序连接服务器使用函数connect()实现,函数原型如下:

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

1.参数s表示套接字句柄;

2.参数name表示将要连接的服务器地址结构信息的变量指针;

3.参数namelen表示地址结构的大小。

 

如果服务器接收到客户端的连接请求,则调用函数accept()接受该请求,函数原型如下:

SOCKET accept(SOCKET s, struct sockaddrFAR* addr, int FAR* addrlen);

1.参数s表示套接字句柄;

2.参数addr表示地址结构信息的指针变量;

3.参数addrlen表示地址结构的大小。

如果函数调用成功,则返回一个新的套接字句柄用于通信双方数据的传输

 

5.数据收发

调用函数send()和recv()进行数据的发送和接收,函数原型如下:

int send(SOCKET s, const char FAR* buf, intlen, int flags);

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

1.如果服务器使用上面的函数进行数据手法,则参数s应该为监听函数返回的新套接字句柄;如果客户端使用以上函数进行数据收发,则参数s应该为客户端创建的套接字句柄

2.send函数仅仅是把buf中的数据copy到s的发送缓冲区的剩余空间里,copy数据成功后,返回copy的字节数。

3.recv函数仅仅是把s的接收缓冲中的协议接收的数据copy到buf中,返回值为copy的字节数。即返回值<0:出错;返回值=0:连接关闭;返回值>0:接收的字节数。

 

6.关闭套接字

当套接字使用完毕或程序退出时,用户应该调用函数closesocket()关闭套接字句柄,函数原型如下:

int closesocket(SOCKET s);

参数s表示即将关闭的套接字句柄。

 

7.服务器端源码

#include <Winsock2.h>

#include <cstdio>

#include<iostream>

#pragma comment(lib,"ws2_32.lib")

using namespace std;

int main()

{

       /*加载winsock库*/

       WSADATAwsaData;

 

    //参数:版本号,指向WSADATA结构体的指针

       WSAStartup(MAKEWORD(2,2), &wsaData);

 

       SOCKETsockData;

 

       /*构造监听socket,流式socket

*参数:套接字所使用的地址格式,套接字类型,协议编号

*AF_INET是IPv4 网络协议的套接字类型

*/

       SOCKETsockServer = socket(AF_INET, SOCK_STREAM, 0);

 

       /*配置监听地址和端口*/

       SOCKADDR_INaddrServer;

       addrServer.sin_port= htons(6000); //绑定端口6000

       addrServer.sin_addr.S_un.S_addr= htonl(INADDR_ANY);//INADDR_ANY表示任何IP,传递unsigned long类型的IP地址

       addrServer.sin_family= AF_INET; //指定地址格式,表示TCP/IP格式

 

       /*绑定监听socket*/

       bind(sockServer,(SOCKADDR*)&addrServer, sizeof(SOCKADDR));

 

       //Listen监听端

       listen(sockServer,5);//5为等待连接数目

       cout<< "服务器已启动!" << endl <<"监听中..." << endl;

       intlen = sizeof(SOCKADDR);

 

       charsendBuf[100] = "hello!";//发送至客户端的字符串

       charrecvBuf[100];//接受客户端返回的字符串

 

       //阻塞进程,等待客户端接入,直到有客户端连接上来为止

       sockData= accept(sockServer, (SOCKADDR*)&addrServer, &len);

 

       while(1){

              //接收并打印客户端数据

              recv(sockData,recvBuf, sizeof(recvBuf), 0);

              if(strcmp(recvBuf, "q") == 0){

                     cout<< "对方已关闭连接!" << endl;

                     break;

              }

              cout<< "recv:" << recvBuf << endl;

 

              //输入并发送数据

              cout<< "send:";

              cin>> sendBuf;

              send(sockData,sendBuf, strlen(sendBuf) + 1, 0);

              if(strcmp(sendBuf, "q") == 0){

                     cout<< "主动关闭连接!" << endl;

                     break;

              }

       }

 

       //关闭socket

       closesocket(sockData);

       closesocket(sockServer);

 

       //释放winsock库

       WSACleanup();

 

       return0;

}

 

8.客户端源码

#include <Winsock2.h>

#include <cstdio>

#include<iostream>

#pragma comment(lib,"ws2_32.lib")

using namespace std;

int main()

{

       /*加载winsock库*/

       WSADATAwsaData;

      

       //MAKEWORD(2,2)生成WORD类型参数,表示版本为2.2

       WSAStartup(MAKEWORD(2,2), &wsaData);

 

       //新建客户端通讯socket,流式socket

       SOCKETsockClient = socket(AF_INET, SOCK_STREAM, 0);

 

       //定义要连接的服务端地址

       SOCKADDR_INaddrServer;  //服务端地址

       addrServer.sin_port= htons(6000);//连接端口6000

       addrServer.sin_addr.S_un.S_addr= inet_addr("127.0.0.1");//目标IP(127.0.0.1是回送地址)

       addrServer.sin_family= AF_INET;

 

       //连接到服务端

       connect(sockClient,(SOCKADDR*)&addrServer, sizeof(SOCKADDR));

      

       charsendBuf[20]= "Hello Socket!";

       charrecvBuf[20];

 

       do{

              //发送数据

              send(sockClient,sendBuf, strlen(sendBuf) + 1, 0);

              if(strcmp(sendBuf, "q") == 0){

                     cout<< "主动关闭连接!" << endl;

                     break;

              }

 

              //接收数据

              recv(sockClient,recvBuf, sizeof(recvBuf), 0);

              if(strcmp(recvBuf, "q") == 0){

                     cout<< "对方已关闭连接!" << endl;

                     break;

              }

              cout<< "recv:" << recvBuf << endl;

              cout<< "next send:";

              cin>> sendBuf;

       }while (1);

 

       //关闭socket

       closesocket(sockClient);

 

       //释放winsock

       WSACleanup();

 

       return0;

}


  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值