之前在Linux系统下介绍了多种实现网络通信的方式,从本文开始后面的文章将在Windows系统下用C++为大家介绍技术,敬请期待~。
话不多说,直接进入正文,我们知道,要完成网络通信要用到非常多的函数,并且函数的参数比较复杂,所以小编在想封装一个network的工具方便后面要写通信的时候直接调用。工具包括一个头文件network.h和一个 .cpp 文件network.cpp。头文件中定义一个NetWork类,成员变量包含了通信要用到的诸如描述符和通信地址的信息;成员函数就是通信过程中用到的函数。
network.h头文件如下:
#ifndef NETWORK_H
#define NETWORK_H#include <iostream>
#include <sys/types.h>
// #include <sys/socket.h>
// #include <netinet/in.h>
// #include <arpa/inet.h>
#include <winsock2.h>
#include <windows.h>
#include <ws2tcpip.h>
#include <cstring>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>class NetWork
{int sock; //socket对象描述符int type; //协议类型sockaddr_in addr; //通信地址socklen_t addrlen; //地址结构字节数bool is_svr; //是否为服务器端
public:NetWork(void);NetWork(int type,const char *ip,short port,bool is_svr=false);~NetWork(void);bool open(void);NetWork* accept(void);int send(const char *buf,int flag=0);int send(const void *buf,size_t len,int flag=0);int recv(void *buf,size_t len,int flag=0);
};#endif // NETWORK_H
下面要在network.cpp文件中去一一定义NetWork的成员函数,也是工具的主要逻辑所在。为了通用性考虑,我们在NetWork类中专门定义了协议类型type去区分TCP通信和UDP通信协议以及定义了is_svr去区分调用服务器端和客户端,并在函数定义中去判断从而不影响各自的网络通信步骤。
#include "network.h"
using namespace std;NetWork::NetWork(void)
{addrlen = sizeof(addr);type = SOCK_STREAM;is_svr = false;
}NetWork::NetWork(int type, const char *ip, short port, bool is_svr):type(type), is_svr(is_svr)
{// 不在构造函数中创建socket,因为socket创建可能失败,而构造函数是没有返回值的,不能在此创建addr.sin_family = AF_INET;addr.sin_port = htons(port);addr.sin_addr.s_addr = inet_addr(ip);addrlen = sizeof(addr);
}NetWork::~NetWork(void)
{close(sock);
}bool NetWork::open(void)
{WSADATA wsaData;if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0){perror("WSADATA失败");}//创建socket对象sock = socket(AF_INET, type, 0);if (sock < 0){perror("socket");return false;}//根据type和is_svr执行以下流程if(is_svr){if(bind(sock, (sockaddr*)&addr, addrlen)){perror("bind");return false;}if(SOCK_STREAM == type && listen(sock, 50)){perror("listen");return false;}}else if(SOCK_STREAM == type && connect(sock, (sockaddr*)&addr, addrlen)){perror("connect");return false;}return true;
}NetWork *NetWork::accept(void)
{if(SOCK_STREAM!= type || !is_svr){puts("只有type为SOCK_STREAM且为服务端才能调用该函数\n");return NULL;}NetWork *nw = new NetWork;nw->sock = ::accept(sock, (sockaddr*)&nw->addr, &nw->addrlen);if(nw->sock < 0){perror("accept");delete nw;return NULL;}return nw;
}int NetWork::send(const char *buf, int flag)
{if(SOCK_STREAM == type)return ::send(sock, buf, strlen(buf)+1, flag);elsereturn sendto(sock, buf, strlen(buf)+1, flag, (sockaddr*)&addr, addrlen);
}int NetWork::send(const void *buf, size_t len, int flag)
{if(SOCK_STREAM == type)return ::send(sock, (const char*)buf, len, flag);elsereturn sendto(sock, (const char*)buf, len, flag, (sockaddr*)&addr, addrlen);
}int NetWork::recv(void *buf, size_t len, int flag)
{if(SOCK_STREAM == type)return ::recv(sock, (char*)buf, len, flag);elsereturn recvfrom(sock, (char*)buf, len, flag, (sockaddr*)&addr, &addrlen);
}
为了测试工具是否具备以上介绍的功能,我们准备一个TCP客户端和一个服务器进行通信以进行验证:
服务端:
#include "network.h"
using namespace std;#define BUF_SIZE (4096)void *run(void *arg)
{NetWork *cnw = static_cast<NetWork *>(arg);char *buf = new char[BUF_SIZE];while (1){int ret = cnw->recv(buf, BUF_SIZE);if (ret <= 0 || 0 == strcmp(buf, "quit")){puts("客户端退出!\n");delete cnw;delete buf;return NULL;}printf("recv:%s bits:%d\n", buf, ret);strcat(buf, ":return");ret = cnw->send(buf);if (ret <= 0){puts("客户端退出!\n");delete cnw;delete buf;return NULL;}}
}int main(int argc, const char *argv[])
{if (3 != argc){printf("User: server <ip> <port>");return 0;}// WSADATA wsaData;// if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)// {// perror("WSADATA失败");// }NetWork *snw = new NetWork(SOCK_STREAM, argv[1], atoi(argv[2]), true);if (!snw->open()){delete snw;return -1;}while (1){NetWork *cnw = snw->accept();if (NULL == cnw){continue;}pthread_t tid;pthread_create(&tid, NULL, run, (void *)cnw);}return 0;
}
客户端:
#include "network.h"
using namespace std;int main(int argc, const char *argv[])
{// WSADATA wsaData;// if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)// {// perror("WSADATA失败");// }NetWork *cnw = new NetWork(SOCK_STREAM, argv[1], atoi(argv[2]), false);if (!cnw->open()){delete cnw;return -1;}char buf[4096];size_t buf_size = sizeof(buf);while (1){cout << ">>>>>" << endl;cin.getline(buf, buf_size);int ret = cnw->send(buf, strlen(buf) + 1, 0);if (ret <= 0 || 0 == strcmp("quit", buf)){printf("通信结束!\n");delete cnw;break;}ret = cnw->recv(buf, buf_size, 0);if (ret <= 0){delete cnw;break;}printf("recv:%s bits:%d\n", buf, ret);}return 0;
}
下面我们先启动服务器再运行客户端进行测试,测试结果如下:
上图中,左边是服务器端,右边是客户端,可以看到,由客户端发送消息给服务端,服务端可以正常接收消息并响应客户端返回,客户端也可以接收服务器的响应,由此完成通信。
over