栗子1:
//-----------------------------------test.h
#ifndef _TEST1_H_
#define _TEST1_H_
#include "iostream"
using namespace std;
class oval_shaped
{
public:
oval_shaped();
void trabzd();
void outputl();
void outputs();
void mianji();
private:
double axis_a,axis_b; //长短半轴
double h,xk_answer,answer,xk[101];
double s;
protected:
double fun(double x);
};
#endif
//-----------------------------------test.cpp
#include "test1.h"
oval_shaped::oval_shaped()
{
cout<<"输入椭圆的长半轴长: a= ";
cin>>axis_a;
cout<<"短半轴长: b= "<<endl;
cin>>axis_b;
xk_answer=0;
h=3.14159/100;
answer=0;
xk[0]=0;
xk[100]=3.14159;
}
void oval_shaped::trabzd() //划分坐标,数据相加
{
int i;
for(i=1;i<100;i++)
{
xk[i]=i*h;
xk[i]=fun(xk[i]);
}
for(i=1;i<100;i++)
xk_answer=xk_answer+xk[i];
answer=0.5*(xk[0]+xk[100]+xk_answer)*h;
}
void oval_shaped::outputl()
{
cout<<"该椭圆的周长为:"<<answer<<"."<<endl;
}
void oval_shaped::outputs()
{
cout<<"该椭圆的面积为:"<<s<<"."<<endl;
}
double oval_shaped::fun(double x)
{
return 2*sqrt(axis_a*axis_a*sin(x)*sin(x)+axis_b*axis_b*cos(x)*cos(x));
}
void oval_shaped::mianji()
{
s=3.14159*axis_a*axis_b;
}
//-----------------------------------mine.cpp
#include "test1.h"
using namespace std;
char interface()
{
char ch;
cout<<"请选择"<<endl<<"1.计算椭圆的周长,请按【C】"<<endl\
<<"2.计算椭圆的面积,请按【S】"<<endl\
<<"3.退出,请按其他任意键 "<<endl;
cin>>ch;
return ch;
}
int main()
{
for(;;)
{
oval_shaped os;
char choose=interface();
switch(choose)
{
case 'c': //求周长时用定积分方法
os.trabzd();
os.outputl();
break;
case 's': //求面积时用的是最简单的公式,当然前面的周长也可以用公式
os.mianji();
os.outputs();
break;
default:
cout<<"输入选择错误,请重新输入!"<<endl;
break;
}
}
}
栗子2:
//-----------------------------------MyHttp.h
#ifndef _MYHTTP_H_
#define _MYHTTP_H_
#include "iostream"
#include "cstring"
#include "cstdio"
#include "cstdlib"
#include "fcntl.h"
#include "io.h"
#include "winsock.h"
using namespace std;
class CMyHttp
{
public:
void GetData();
void CloseHttp();
bool OpenHttp();
CMyHttp();
CMyHttp(LPCSTR lpServerName,LPCSTR lpFileName);
virtual ~CMyHttp();
private:
char server[200]; //存放服务器名
char filename[200]; //存放服务器文件名
IN_ADDR iaHost;
LPHOSTENT lpHostEntry;
SOCKET Socket;
LPSERVENT lpServEnt;
SOCKADDR_IN saServer;
char szBuffer[1024];
};
#endif
//-----------------------------------MyHttp.cpp
#include "MyHttp.h"
CMyHttp::CMyHttp()
{
strcpy(this->server,"localhost");
strcpy(this->filename,"/");
}
CMyHttp::CMyHttp(LPCSTR lpServerName,LPCSTR lpFileName)
{
strcpy(this->server,"lpServerName");
strcpy(this->filename,"lpFileName");
//重载构造函数使用this指针表示给当前建立的对象提供一特定的服务器名和服务器文件名
}
/*
1.strcpy(字符数组1,字符串2);//将字符串2复制到字符数组1中去(包括字符串结尾符'\0')
2.this:
this指针是某个类对象(通常指当前操作的对象)的起始地址,通过这个地址可以获得该对象的数据和成员函数
可以这样理解,this指针是隐含于每一个成员函数(包括构造函数和析构函数,但不包括静态成员函数)中特殊的指针,他用于指向正在被成员函数操作的对象。
*/
CMyHttp::~CMyHttp()
{}
bool CMyHttp::OpenHttp() //建立与http的连接
{
WORD wVersionRequested=MAKEWORD(1,1); //使用1.1版本的套接字
WSADATA wsaData;
int nRet;
nRet=WSAStartup(wVersionRequested,&wsaData);
if(nRet)
{
cerr<<endl<<"WSAStartup()"<<nRet<<endl;
WSACleanup();
return false;
}
if(wsaData.wVersion!=wVersionRequested) //检查WinSock的版本
{
cerr<<endl<<"WinSock version not supported"<<endl;
WSACleanup();
return false;
}
iaHost.s_addr=inet_addr(server); //使用inet_addr()函数检查主机是名称还是ip地址
if(iaHost.s_addr==INADDR_NONE)
{
//不是ip地址
lpHostEntry=gethostbyname(server);
}
else
{
//是ip地址
lpHostEntry=gethostbyaddr((const char *)&iaHost,sizeof(struct in_addr),AF_INET);
}
if(lpHostEntry==NULL)
{
cerr<<"gethostbyname()";
return false;
}
Socket=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(Socket==INVALID_SOCKET)
{
cerr<<"socket()";
return false;
}
//寻找Http服务上的tcp端口号
lpServEnt=getservbyname("http","tcp");
if(lpServEnt==NULL)
saServer.sin_port=htons(80); //设置端口号为80;
else
saServer.sin_port=lpServEnt->s_port;
//填充服务器地址结构的剩余部分
saServer.sin_family=AF_INET;
saServer.sin_addr=*((LPIN_ADDR)*lpHostEntry->h_addr_list);
//连接socket
nRet=connect(Socket,(LPSOCKADDR)&saServer,sizeof(SOCKADDR_IN));
if(nRet==SOCKET_ERROR)
{
cerr<<"connect()";
closesocket(Socket);
return false;
}
//格式化http请求
sprintf(szBuffer,"GET %s\n",filename);
nRet=send(Socket,szBuffer,strlen(szBuffer),0);
if(nRet==SOCKET_ERROR)
{
cerr<<"send()";
closesocket(Socket);
return false;
}
return true;
}
void CMyHttp::CloseHttp()
{
//关闭socket
closesocket(Socket);
//释放winsock
WSACleanup();
}
void CMyHttp::GetData()
{
int nRet;
//获取文件内容并输出到标准输出设备
while(1)
{
//等待接接收
nRet=recv(Socket,szBuffer,sizeof(szBuffer),0);
if(nRet==SOCKET_ERROR)
{
cerr<<"recv()";
break;
}
//判断服务器是否关闭了连接
if(nRet==0)
{
break;
}
//输出文件内容
cout.write(szBuffer,nRet);
}
}
//-----------------------------------gethttp.cpp
#include "MyHttp.h"
#include "iostream"
#pragma comment(lib,"ws2_32.lib")
int main(int argc,char **argv)
{
int nRet;
if(argc!=3)
{
cerr<<endl<<"用法:GetHTTP ServerName FullPathName"<<endl;
// return;
}
CMyHttp myHttp(argv[1],argv[2]);
_setmode(_fileno(stdout),_O_BINARY); //设置输出流方式为二进制模式
if(myHttp.OpenHttp())
{
myHttp.GetData();
myHttp.CloseHttp();
}
return 0;
}
解释:
1)
int WSAStartup ( WORD wVersionRequested, LPWSADATA lpWSAData );
//WSA(Windows Sockets Asynchronous,Windows异步套接字)的启动命令.
//当一个应用程序调用WSAStartup函数时,操作系统根据请求的Socket版本来搜索相应的Socket库,
//然后绑定找到的Socket库到该应用程序中。以后应用程序就可以调用所请求的Socket库中的其它Socket函数了。
⑴ wVersionRequested:一个WORD(双字节)型数值,指明程序请求使用的Socket版本,其中高位字节指明副版本,低位字节指明主版本。
⑵lpWSAData 指向WSADATA数据结构的指针,用来接收Windows Sockets[1] 实现的细节,表明请求的Socket的版本信息。结构体如下:
//WSADATA结构被用来存储被WSAStartup函数调用后返回的Windows Sockets数据。
typedef struct WSAData {
WORD wVersion;
WORD wHighVersion;
#ifdef _WIN64
unsigned short iMaxSockets;
unsigned short iMaxUdpDg;
char FAR * lpVendorInfo;
char szDescription[WSADESCRIPTION_LEN+1];
char szSystemStatus[WSASYS_STATUS_LEN+1];
#else
char szDescription[WSADESCRIPTION_LEN+1];
char szSystemStatus[WSASYS_STATUS_LEN+1];
unsigned short iMaxSockets;
unsigned short iMaxUdpDg;
char FAR * lpVendorInfo;
#endif
} WSADATA;
typedef WSADATA FAR *LPWSADATA;
a. iMaxSockets
单个进程能够打开的socket的最大数目。Windows Sockets的实现能提供一个全局的socket池,可以为任何进程分配;或者它也可以为socket分配属于进程的资源。这个数字能够很好地反映Windows Sockets DLL或网络软件的配置方式。应用程序的编写者可以通过这个数字来粗略地指明Windows Sockets的实现方式对应用程序是否有用。例如,X Windows服务器在第一次启动的时候可能会检查iMaxSockets的值:如果这个值小于8,应用程序将显示一条错误信息,指示用户重新配置网络软件(这是一种可能要使用szSystemStatus文本的场合)。显然无法保证某个应用程序能够真正分配iMaxSockets个socket,因为可能有其它WindowsSockets应用程序正在使用。
b. iMaxUdpDg
Windows Sockets应用程序能够发送或接收的最大的用户数据包协议(UDP)的数据包大小,以字节为单位。如果实现方式没有限制,那么iMaxUdpDg为零。在Berkeley sockets的许多实现中,对于UDP数据包有个固有的限制(在必要时被分解),大小为8192字节。Windows Sockets的实现可以对碎片重组缓冲区的分配作出限制。对于适合的WindowsSockets 实现,iMaxUdpDg的最小值为512。注意不管iMaxUdpDg的值是什么,都不推荐你发回一个比网络的最大传送单元(MTU)还大的广播数据包。(Windows Sockets API 没有提供发现MTU的机制,但是它不会小于512个字节)。WinSock2.0版中已被废弃。
2)
int PASCAL FAR WSACleanup (void);
应用程序或DLL在使用Windows Sockets服务之前必须要进行一次成功的WSAStartup()调用.当它完成了Windows Sockets的使用后,应用程序或DLL必须调用WSACleanup()将其从Windows Sockets的实现中注销,并且该实现释放为应用程序或DLL分配的任何资源.
3)
先来看
typedef struct in_addr {
union {
struct { UCHAR s_b1,s_b2,s_b3,s_b4; } S_un_b;
struct { USHORT s_w1,s_w2; } S_un_w;
ULONG S_addr;
} S_un;
#define s_addr S_un.S_addr /* can be used for most tcp & ip code */
//IPv4地址占32位.注:联合体的内存由最大内存空间成员所占的空间决定
#define s_host S_un.S_un_b.s_b2 // host on imp
#define s_net S_un.S_un_b.s_b1 // network
#define s_imp S_un.S_un_w.s_w2 // imp
#define s_impno S_un.S_un_b.s_b4 // imp #
#define s_lh S_un.S_un_b.s_b3 // logical host
} IN_ADDR, *PIN_ADDR, FAR *LPIN_ADDR;
in_addr_t inet_addr(const char *cp);
//是将一个点分十进制的IP转换成一个长整数型数(u_long类型)
参数:字符串,一个点分十进制的IP地址.(即是我们常见的ip:带点的十位字符串)
返回值:如果正确执行将返回一个无符号长整数型数。如果传入的字符串不是一个合法的IP地址,将返回INADDR_NONE。
详见http://roclinux.cn/?p=1160
插播
typedef struct hostent FAR *LPHOSTENT;
/*
* Structures returned by network data base library, taken from the
* BSD file netdb.h. All addresses are supplied in host order, and
* returned in network order (suitable for use in system calls).
*/
struct hostent {
char FAR * h_name; /* official name of host */
char FAR * FAR * h_aliases; /* alias list */
short h_addrtype; /* host address type */
short h_length; /* length of address */
char FAR * FAR * h_addr_list; /* list of addresses */
#define h_addr h_addr_list[0] /* address, for backward compat */
};
4)
详见http://andylin02.iteye.com/blog/662965
struct hostent* gethostbyname(const char *name);
//返回对应于给定主机名的包含主机名字和地址信息的hostent结构指针。结构的声明与gethostbyaddr()中一致。
struct hostent * gethostbyaddr(const char * addr, socklen_t len, int family);
//返回一个指向hostent结构指针。
addr参数实际上不是char * 类型,而是一个指向存放IPv4地址的某个in_addr结构的指针;
len参数是这个结构的大小;
family参数为AF_INET.
5)
int socket( int af, int type, int protocol);
//创建一个能够进行网络通信的套接字
//若无错误发生,socket()返回引用新套接口的描述字。否则的话,返回INVALID_SOCKET错误.
af:一个地址描述。目前仅支持AF_INET格式,也就是说ARPA Internet地址格式。表示套接字要使用的协议簇,协议簇的在“linux/socket.h”里有详细定义,常用的协议簇:
AF_UNIX(本机通信)
AF_INET(TCP/IP – IPv4)
AF_INET6(TCP/IP – IPv6)
type:指定socket类型。新套接口的类型描述类型,如TCP(SOCK_STREAM)和UDP(SOCK_DGRAM)。常用的socket类型有,SOCK_STREAM(流套接字)、SOCK_DGRAM(数据报套接字)、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等等。
protocol:指定套接口所用的协议。如调用者不想指定,可用0。常用的协议有,IPPROTO_TCP、IPPROTO_UDP、IPPROTO_STCP、IPPROTO_TIPC等,它们分别对应TCP传输协议、UDP传输协议、STCP传输协议、TIPC传输协议。
再说明:
SOCK_STREAM 提供有序的、可靠的、双向的和基于连接的字节流,使用带外数据传送机制,为Internet地址族使用TCP。
SOCK_DGRAM 支持无连接的、不可靠的和使用固定大小(通常很小)缓冲区的数据报服务,为Internet地址族使用UDP。
SOCK_STREAM类型的套接口为全双向的字节流。对于流类套接口,在接收或发送数据前必需处于已连接状态。用connect()调用建立与另一套接口的连接,连接成功后,即可用send()和recv()传送数据。当会话结束后,调用closesocket()。带外数据根据规定用send()和recv()来接收。
实现SOCK_STREAM类型套接口的通讯协议保证数据不会丢失也不会重复。如果终端协议有缓冲区空间,且数据不能在一定时间成功发送,则认为连接中断,其后续的调用也将以WSAETIMEOUT错误返回。
SOCK_DGRAM类型套接口允许使用sendto()和recvfrom()从任意端口发送或接收数据报。如果这样一个套接口用connect()与一个指定端口连接,则可用send()和recv()与该端口进行数据报的发送与接收。
6)
struct servent FAR * PASCAL FAR getservbyname(const char
Far * name, const char FAR *proto);
//返回与给定服务名对应的包含名字和服务号信息的servent结构指针
//如果没有错误发生,getservbyname()返回如上所述的一个指向servent结构的指针,
//否则,返回一个空指针。
name: 一个指向服务名的指针。
proto: 指向协议名的指针。
再解释:
A.先看 LPSERVENT(指向该结构的指针)
typedef struct servent FAR *LPSERVENT;
struct servent {
char FAR * s_name; /* official service name */
char FAR * FAR * s_aliases; /* alias list */
#ifdef _WIN64
char FAR * s_proto; /* protocol to use */
short s_port; /* port # */
#else
short s_port; /* port # */
char FAR * s_proto; /* protocol to use */
#endif
};
成员 用途
s_name 正规的服务名。
s_aliases 一个以空指针结尾的可选服务名队列。
s_port 连接该服务时需要用到的端口号,返回的端口号是以网络字节顺序排列的。
s_proto 连接该服务时用到的协议名。
返回的指针指向一个由Windows Sockets实现分配的结构。应用程序不应该试图修改这个结构或者释放它的任何部分。此外,每一线程仅有一份这个结构的拷贝,所以应用程序应该在发出其他Windows Scokets API调用前,把自己所需的信息拷贝下来.
下面插播htons()
/***
htons是将整型变量从主机字节顺序转变成网络字节顺序,
就是整数在地址空间存储方式变为:高位字节存放在内存的低地址处。
例1
假定你的数据是0x1234
在网络字节顺序里 这个数据放到内存中就应该显示成
addr addr+1
0x12 0x34
而在x86电脑上,数据0x1234放到内存中实际是:
addr addr+1
0x34 0x12
htons 的用处就是把实际主机内存中的整数存放方式调整成网络字节顺序。
例2
我们在Intel机器下,执行以下程序
voidmain()
{
inta=16,b;
b=htons(a);
cout<<"a="<<a<<endl;
cout<<"b="<<b<<endl;
}
结果a=16.b=4096.
数字16的16进制表示为0x0010,数字4096的16进制表示为0x1000。
由于Intel机器是小尾端,存储数字16时实际顺序为1000,存储4096时实际顺序为0010。
因此在发送网络包时为了报文中数据为0010,需要经过htons进行字节转换。
***/
B.再看SOCKADDR_IN
typedef struct sockaddr_in SOCKADDR_IN;
/*
* Socket address, internet style.
*/
struct sockaddr_in {
short sin_family;
u_short sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};
sin_family表示地址类型,对于基于TCP/IP传输协议的通信,该值只能是AF_INET;
sin_prot表示端口号,例如:21 或者 80 或者 27015,总之在0 ~ 65535之间;
sin_addr表示32位的IP地址,例如:192.168.1.5 或 202.96.134.133;
sin_zero表示填充字节,一般情况下该值为0;
7)
int connect(int s, const struct sockaddr * name, int namelen);
//建立与指定socket的连接
s:标识一个未连接socket
name:指向要连接套接字的sockaddr结构体的指针
namelen:sockaddr结构体的字节长度
8)
int PASCAL FAR send( SOCKET s, const char FAR* buf, int len, int flags);
//向一个已经连接的socket发送数据,如果无错误,返回值为所发送数据的总数,否则返回SOCKET_ERROR。
s:一个用于标识已连接套接口的描述字。
buf:包含待发送数据的缓冲区。
len:缓冲区中数据的长度。
flags:调用执行方式。常为0.
不论是客户还是服务器应用程序都用send()来向TCP连接的另一端发送数据。客户程序一般用send()向服务器发送请求,而服务器通常用send()产生应答。
见http://blog.sina.com.cn/s/blog_732784f70100t0ti.html
9)
int setmode(int handle, unsigned mode);
//设置打开文件方式.若返回值为-1,则表示不成功.