irpas技术客

C/C++网络编程详解(Windows版)_余识-_c++网络编程

网络投稿 6196

文章目录 前言一、服务器WSAStartup函数正常使用函数详解 socket函数bind函数listen函数accept函数send函数recv函数closesocket函数WSACleanup函数 二、客户端connect函数 三、其它网络相关函数htons,ntohs等inet_addr,inet_ntoagethostbyname注意事项


前言

相比于基础,网络编程就要复杂一些,但其实也有固定格式,记住即可。

需要的头文件和库:

#include<WinSock2.h> #pragma comment(lib,"ws2_32.lib") 一、服务器

主要流程及主要函数:

网络环境初始化:WSAStartup创建服务器套接字:socket绑定本机IP和端口:bind监听客户端:listen等待客户端连接:accept发送消息:send接收消息:recv关闭socket:closesocket清除网络环境:WSACleanup WSAStartup函数 正常使用

函数原型:

int WSAStartup( WORD wVersionRequested, //版本号,使用MAKEWORD宏生成 LPWSADATA lpWSAData //数据 ); //返回值:0代表成功,否则失败

使用方法:

#include<WinSock2.h> #include<iostream> #pragma comment(lib,"ws2_32") using namespace std; int main() { WSADATA data; int ret=WSAStartup(MAKEWORD(2,2),&data); if (ret) { cout << "初始化网络错误!" << endl; return -1; } }

该函数用于初始化网络环境,参数基本上是固定写法,记住即可,必须要有。

函数详解

该函数实则是用来加载Windows Socket动态库的

wVersionRequested参数用来指定准备加载动态库的版本号,高字节为库文件的副版本,低字节指定主版本,MAKEWORD(X,Y)宏用于生成该参数,其中X为高字节,Y为低字节

lpWSAData 为指向WSADATA结构体的指针,该参数用于返回被加载动态库的有关信息

typedef struct WSAData { WORD wVersion; //期望调用者使用的socket版本(或实际返回的socket版本,可根据此参数判断返回的版本号是否正确,可通过HIBYTE宏取得高字节,LOBYTE宏取得低字节) WORD wHighVersion; //本机Dll支持的最高版本 unsigned short iMaxSockets;//一个进程最多可以打开的套接字数量(2.0版本后忽略此值) unsigned short iMaxUdpDg; //一个进程发送或接收的最大数据报长度 char FAR * lpVendorInfo; //厂商专有信息(2.0版本后忽略此值) char szDescription[WSADESCRIPTION_LEN+1]; //DLL的描述信息 char szSystemStatus[WSASYS_STATUS_LEN+1];//DLL的状态信息 socket函数

函数原型:

SOCKET socket( int af, //地址类型,常用IPv4地址:AF_INET,和IPv6地址:AF_INET6 int type, //套接字类型,常用TCP协议:SOCK_STREAM,UDP协议:SOCK_DGRAM int protocol //协议类型,一般填0,自动选择即可 ); //返回值,INVALID_SOCKET失败,该宏实则定义为-1,否则成功

使用:

SOCKET sock=socket(AF_INET,SOCK_STREAM,0); if (sock == -1) { cout << "创建套接字失败"; return -1; }

该代码创建了IPv4类型的地址,TCP协议的套接字

完整视图:

bind函数

函数原型:

int bind( SOCKET s, //创建的socket sockaddr * name, //包含地址和端口的结构体 int namelen //sockaddr 结构长度 ); //返回值:返回SOCKET_ERROR失败,该宏被定义为-1,否则成功,返回值为0

使用:

#define _WINSOCK_DEPRECATED_NO_WARNINGS //vs环境下必须定义,否则无法使用inet_addr函数 sockaddr_in addr; addr.sin_family = AF_INET; //地址为IPv4协议 addr.sin_port = htons(9999); //端口为9999 addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); //具体绑定本机的地址 ret=bind(sock,(sockaddr*)&addr, sizeof(addr)); //绑定 if (ret == -1) { cout << "绑定地址端口失败"; return -1; }

完整视图:

listen函数

函数原型:

int listen( SOCKET s, //要监听的socket int backlog //等待连接的最大队列长度 ); //返回值:返回SOCKET_ERROR失败,该宏被定义为-1,否则成功,返回值为0

使用:

ret=listen(sock,5); if (ret == -1) { cout << "监听套接字失败"; return -1; }

完成代码:

accept函数

函数原型:

SOCKET accept( SOCKET s, //接收的socket sockaddr* addr, //接收到客户端的地址信息 int * addrlen //地址信息长度 ); //返回值:返回INVALID_SOCKET失败,该宏定义为-1,否则成功返回客户端的套接字,可进行发送和接收消息

使用:

sockaddr addrCli; int len = sizeof(addrCli); SOCKET sockCli=accept(sock,&addrCli,&len); if (sockCli == -1) { cout << "接收客户端连接失败"; return -1; }

完整代码视图:

send函数

函数原型:

int send( SOCKET s, char * buf,//要发送的内容 int len, //内容长度 int flags //一般为0,拷贝到程序中就立即删除内核中的数据,或MSG_DONTROUTE:要求传输层不要将数据路由出去,MSG_OOB:标志数据应该被带外发送 ); //返回值:-1(或宏SOCKET_ERROR)表示发送失败,否则返回发送成功的字节数

使用:

char buf[0xFF] = "我是服务器"; ret=send(sockCli, buf, strlen(buf),0); if (ret == -1) { cout << "发送信息失败"; }

整体视图:

recv函数

函数原型:

int recv( SOCKET s, //套接字 char * buf, //接受数据的缓存区 int len, //缓存区大小 int flags //标志,一般填0,将消息拷贝到应用程序中,将内核中的数据删除,还可以填MSG_PEEK,只取数据,不从内核中删除数据,MSG_OOB:处理带外数据 ); //返回值:小于等于0都表示出错,大于0则表示接收成功的数据大小

使用:

ret=recv(sockCli,buf,0xFF,0); if (ret <= 0) { cout << "接受客户端数据失败"; return -1; }

完整视图:

closesocket函数 int closesocket( SOCKET s //要关闭的socket );

该函数就是关闭不用的socket,释放资源

WSACleanup函数

无任何参数,直接调用即可

WSACleanup();

按理说尽量在应用程序退出时都要进行清理,否则下次启动可能出现错误

修改后的代码视图: 完整代码:

#define _WINSOCK_DEPRECATED_NO_WARNINGS #include<WinSock2.h> #pragma comment(lib,"ws2_32") #include<iostream> using namespace std; int main() { WSADATA data; int ret=WSAStartup(MAKEWORD(2,2),&data); if (ret) { cout << "初始化网络错误!" << endl; WSACleanup(); return -1; } SOCKET sock=socket(AF_INET,SOCK_STREAM,0); if (sock == -1) { cout << "创建套接字失败"; WSACleanup(); return -1; } sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(9999); addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); ret=bind(sock,(sockaddr*)&addr, sizeof(addr)); if (ret == -1) { cout << "绑定地址端口失败"; WSACleanup(); return -1; } ret=listen(sock,5); if (ret == -1) { cout << "监听套接字失败"; WSACleanup(); return -1; } sockaddr addrCli; int len = sizeof(addrCli); SOCKET sockCli=accept(sock,&addrCli,&len); if (sockCli == -1) { cout << "接收客户端连接失败"; WSACleanup(); return -1; } char buf[0xFF] = "我是服务器"; ret=send(sockCli, buf, strlen(buf),0); if (ret == -1) { cout << "发送信息失败"; WSACleanup(); return -1; } ret=recv(sockCli,buf,0xFF,0); if (ret <= 0) { cout << "接受客户端数据失败"; WSACleanup(); return -1; } WSACleanup(); }

至此,一个最简单的windows服务器就写好了,可以简单执行一次发送数据和一次接受数据。

二、客户端

主要流程和函数:

初始化网络环境:WSAStartup创建套接字:socket连接服务器:connect发送数据:send接收数据:recv清理网络环境:WSACleanup

其它三个函数与服务器一样,只是多出个connect函数,使用方法也与bind函数类似

connect函数

函数原型:

int connect( SOCKET s, //与服务器连接的socket sockaddr* name, //服务器的地址端口 int namelen //上个参数结构体的长度 ); //返回值:-1失败,否则成功

使用方法:

sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(9999); addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); int ret = connect(sock, (sockaddr*)&addr, sizeof(addr)); if (ret == -1) { cout << "连接服务器失败" << endl; return -1; }

完整代码视图: 完整代码:

#define _WINSOCK_DEPRECATED_NO_WARNINGS #include<WinSock2.h> #include<iostream> #pragma comment(lib,"ws2_32.lib") using namespace std; int main() { WSADATA data; int ret = WSAStartup(MAKEWORD(2, 2), &data); if (ret) { cout << "初始化网络错误!" << endl; WSACleanup(); return -1; } SOCKET sock = socket(AF_INET, SOCK_STREAM, 0); sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(9999); addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); int ret = connect(sock, (sockaddr*)&addr, sizeof(addr)); if (ret == -1) { WSACleanup(); cout << "连接服务器失败" << endl; return -1; } char buf[0xFF]; ret=recv(sock,buf,sizeof(buf),0); if (ret <= 0) { WSACleanup(); cout << "接收服务器数据失败" << endl; return -1; } cout << "服务器:" << buf << endl; ret=send(sock,buf,ret,0); //将接收到的数据发回服务器 if (ret <= 0) { WSACleanup(); cout << "发送服务器数据失败" << endl; return -1; } WSACleanup(); } 三、其它网络相关函数 htons,ntohs等

这种函数名有固定的意义:

h:homen:networks:shortl:long

htons:意思就是本机字节序转到网络字节序,short类型的长度 ntohs:意思就是网络字节序转到本机字节序,short类型的长度

还有htonl,htonll,htonf等也是类似的意思

inet_addr,inet_ntoa inet_addr:负责将我们平时看到的网络地址127.0.0.1等转化为网络字节序inet_ntoa:负责将网络字节序还原为我们平时看到的字符串127.0.0.1等

使用方法:

sockaddr_in addr; addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); //将127.0.0.1转换为网络字节序 char* c_IP = inet_ntoa(addr.sin_addr);//将网络字节序转换为127.0.0.1字符串 gethostbyname

通过域名获取ip地址,比如我们常见的·的ip地址是多少呢?就可以通过这个函数获取

使用方法:

//获取主机ip HOSTENT* host = gethostbyname("·"); //如获取网站IP地址,参数填写域名即可,不需加"http://" if (host == NULL) { return false; } //转化为char*并拷贝返回 cout << inet_ntoa(*(in_addr*)*host->h_addr_list); 注意事项

这些函数都被微软定为不安全函数,想正常使用就必须在代码最前面定义宏:

#define _WINSOCK_DEPRECATED_NO_WARNINGS


1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,会注明原创字样,如未注明都非原创,如有侵权请联系删除!;3.作者投稿可能会经我们编辑修改或补充;4.本站不提供任何储存功能只提供收集或者投稿人的网盘链接。

标签: #c网络编程