C/C++网络编程详解(Windows版)_c++网络编程
阿里云国内75折 回扣 微信号:monov8 |
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6 |
文章目录
前言
对学习C/C++感兴趣的同学可以看看我的这篇文章哦C/C++教程
相比于基础网络编程就要复杂一些但其实也有固定格式记住即可。
首先是需要的头文件和库
#include<WinSock2.h>
#pragma comment(lib,"ws2_32.lib")
一、服务器
主要流程及主要函数
- 网络环境初始化WSAStartup
- 创建服务器套接字socket
- 绑定本机IP和端口bind
- 监听客户端listen
- 等待客户端连接accept
- 发送消息send
- 接收消息recv
- 关闭socketclosesocket
- 清除网络环境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:home
- n:network
- s:short
- l:long
htons意思就是本机字节序转到网络字节序short类型的长度
ntohs意思就是网络字节序转到本机字节序short类型的长度
还有htonl,htonll,htonf等也是类似的意思
inet_addrinet_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地址比如我们常见的www.baidu.com的ip地址是多少呢就可以通过这个函数获取
使用方法
//获取主机ip
HOSTENT* host = gethostbyname("www.baidu.com"); //如获取网站IP地址参数填写域名即可不需加"http://"
if (host == NULL)
{
return false;
}
//转化为char*并拷贝返回
cout << inet_ntoa(*(in_addr*)*host->h_addr_list);
注意事项
这些函数都被微软定为不安全函数想正常使用就必须在代码最前面定义宏
#define _WINSOCK_DEPRECATED_NO_WARNINGS