在之前的文章中我们介绍到也写到基于TCP和UDP协议的网络通信。本篇我们又闲得无聊把这俩给封装成一个小工具以满足实际应用时多功能网络服务器的需要。
这个NetWork小工具是在Windows下制作的所以别忘了该有的头文件。C++中编写我们就遵循面向对象的思想来。
#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
这里我们要特别注意,在Linux和Windows下进行网络编程包含的头文件有所区别,如下注释掉的是Linux下所包含的头文件,Windows下编程需要加上下面三行头文件:
// #include <sys/socket.h>
// #include <netinet/in.h>
// #include <arpa/inet.h>
#include <winsock2.h>
#include <windows.h>
#include <ws2tcpip.h>
接着来完成定义:
#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);
else
return 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);
else
return 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);
else
return recvfrom(sock, (char*)buf, len, flag, (sockaddr*)&addr, &addrlen);
}
细心的你应该可以发现,我们Windows下写的函数定义好像和上一篇Linux下写的有点区别:
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
{
perror("WSADATA失败");
}
可以发现,我们在创建socket套接字之前有这么几行陌生的代码。这段代码的作用是初始化 Windows Socket API。在使用网络编程之前,需要先调用 WSAStartup
来初始化 Winsock DLL。
over