您的位置:首页 > 编程语言 > C语言/C++

WinsockPragramming(1) Server(C++版)

2015-05-31 13:44 375 查看
预准备:

在VC中连接动态链接库WS2_32.DLL(所有Winsock函数均从该库导出)。具体做法:“工程”——>“设置”——>“对象/库模块”,添加“WS2_32.DLL”。

创建服务器流程

1、初始化套接字库

调用函数WSAStartup(),函数原型:

int WSAStartup(WORD wVersionRequested,LPWSADATA lpWSAData);

函数调用成功返回0。

参数wVersionRequested表示当前套接字库的版本号。

WORD wVersionRequested=MAKEWORD(2,0);

表示版本号为2.0

参数lpWSAData是指向结构体WSADATA的指针变量,表示获取到的套接字库的详细信息。

WSADATA结构体定义原型:

typedef struct WSAData{

WORD wVersion;//库文件建议用用程序使用套接字版本

WORD wHighVersion;//库文件支持的最高版本

char szDescription[WSADESCRIPTION_LEN+1];//描述库文件的字符串

char szSystemStatus[WSASYS_STATUS_LEN+1];//系统状态字符串

unsigned short iMaxSockets;//同时支持的最大套接字数

unsigned short iMaxUdpDg;

char FAR * lpVendorInfo;

}

初始化套接字库代码:

WSAData data;//定义WSAData变量
WOED wVersionRequested=MAKEWORD(2,0);//定义套接字库版本号
::WSAStartup(wVersionRequested,&data);//初始化套接字库


2、创建套接字句柄

调用函数socket(),函数原型:

SOCKET socket(int af,int type,int protocol);

参数af指定套接字所使用的地址格式

参数type指定套接字类型

SOCK_STREAM 创建流式套接字(基于TCP)

SOCK_DGRAM 创建数据报套接字(基于UDP)

SOCK_RAW 创建原始套接字

参数protocol:如果参数type已经指定套接字类型为TCP或UDP,则该参数可以置为0.

创建套接字句柄代码:

SOCKET s;//定义套接字句柄
s=::socket(AF_INET,SOCK_STREAM,0);//创建并返回套接字句柄


3、地址结构设置及字节顺序转换

此时我们必须了解两个概念:寻址方式和字节顺序。

寻址方式:套接字在各种协议中使用,所以为了区分网络协议,需设定寻址方式。TCP/IP地址中使用IP地址和端口号确定通信双方。

套接字地址结构定义:

struct sockaddr_in{

short sin_family;//指定地址家族(设置为AF_INET表示应用使用的是TCP/IP)

unsigned short sin_port;//指定端口号

struct in_addr sin_addr;//IP地址

char sin_zero[8];//留做备用,需要指定为0

};

sin_addr表示32位的IP地址结构,定义如下:

struct in_addr{

union{

struct{

unsigned char s_b1,s_b2,s_b3,s_b4;

}S_un_b;//用4个u_char字符描述IP地址

struct{

unsigned short s_w1,s_w2;

}S_un_w;//用两个u_short类型描述IP地址

unsigned long S_addr;//用一个u_long类型描述IP地址

}S_un;

};

通常我们使用1个u_long类型字符描述IP地址。

sockaddr_in addr;

addr.sin_addr.S_un.S_addr=inet_addr(“210.6.132.5”);

字节顺序

在Socket编程中,传输数据的排列顺序以网络字节顺序和主机字节顺序为主。主机向网络发送数据,需要把主机字节数据转化为网络字节顺序;主机接收网络发来的数据时,需要把网络字节顺序转化为主机字节顺序。从数据存储角度来看,网络字节顺序先存储最重要的字节,而主机字节顺序先存储不重要的字节。

在进行字节顺序转换时,会用到字节顺序转换函数

u_short htons(u_short hostshort);//将一个u_short类型IP地址从主机字节顺序转换为网络字节顺序

u_long htonl(u_long hostlong);//将一个u_long类型IP地址从主机字节顺序转换为网络字节顺序

u_long ntohl(u_long netlong);//将一个u_long类型IP地址从网络字节顺序转换为主机字节顺序

u_short ntohs(u_short netshort);//将一个u_short类型IP地址从网络字节顺序转换为主机字节顺序

unsigned long inet_addr(const char FAR*cp);//将一个字符串IP转换成以网络字节顺序排列的IP

char FAR*inet_ntoa(struct in_addr in);//讲一个以网络字节顺序排列的IP地址转换成一个字符串IP

地址结构设置及字节顺序转换代码:

sockaddr_in addr;//定义套接字地址结构变量
in_addr in_add;//定义IP地址结构变量
addr.sin_fimily=AF_INET;//指定地址家族为TCP/IP
addr.sin_port=htons(80);//指定端口号
addr.sin_addr.S_un.S_addr=inet_addr('127.0.0.1');//将字符串IP转换为网络字节顺序排列的IP
char address[]=inet_ntoa(addr.sin_addr.S_un.S_addr)//将网络字节顺序排列的IP转换为字符串IP


4、绑定地址信息

调用bind()函数,其函数原型:

int bind(SOCKET s,const struct sockaddr FAR* name,int namelen);

函数调用成功则返回0。

绑定地址信息代码:

::bind(s,(sockaddr)&addr,sizeof(addr));


5、监听套接字(仅用于流式套接字)

调用listen()函数,其原型为:

int listen(SOCKET s,int backlog);

参数backlog指定监听的最大连接数。

监听套接字代码:

::listen(s,5);//在套接字上监听,并且指定最大连接数为5.


6、接收客户端连接请求

客户端调用connect()函数发送连接请求,服务器调用accept()接收该请求。accept()函数原型:

SOCKET accept(SOCKET s,struct sockaddr FAR* addr,int FAR* addrlen);

接收客户端连接请求代码:

s1=::accept(s,(sockaddr*)&addr,&n);//接受连接请求


7、数据收发

调用send()函数和recv()函数,原型:

int send(SOCKET s,const char FAR *buf,int len,int flags);

int recv(SOCKET s,char FAR *buf,int len,int flags)

参数buf是指向数据缓冲区的指针变量,参数flags通常设置为0.

数据收发代码:

::send(s1,sztext,sizeof(sztext),0);
::recv(s,sztext,sizeof(sztext),0);


8、关闭套接字。

关闭套接字代码

::closesocket(s);


9、释放套接字库

释放套接字库

::WSACleanup();


完整源码:

/*Server.cpp*/

#include <winsock2.h>//包含头文件
#include <stdio.h>
#include <windows.h>
#pragma comment(lib,"WS2_32.lib")//显式连接套接字库

int main(){

//初始化套接字库
WSADATA data;//WSADATA结构体对象
WORD w=MAKEWORD(2,0);//定义版本号
::WSAStartup(w,&data);//初始化套接字库

char sztext[]="欢迎你\r\n";//定义并初始化发送到客户端的字符数组

//创建套接字句柄
SOCKET s,s1;//定义连接套接字和数据收发套接字句柄
s=::socket(AF_INET,SOCK_STREAM,0);//创建TCP套接字

//地址结构设置及字节转换
sockaddr_in addr,addr2;//定义套接字地址结构
int n=sizeof(addr2);//获取套接字地址结构大小
addr.sin_family=AF_INET;//初始化地址结构
addr.sin_port=htons(75);//设置端口号
addr.sin_addr.S_un.S_addr=INADDR_ANY;

//绑定套接字
::bind(s,(sockaddr*)&addr,sizeof(addr));

//监听套接字
::listen(s,5);

printf("服务器已经启动\r\n");//输出提示信息
while(true){

//接受连接请求
s1=::accept(s,(sockaddr*)&addr2,&n);

if(s1!=NULL){
printf("%s已经连接上\r\n",inet_ntoa(addr2.sin_addr));
//向客户端发送字符数组
::send(s1,sztext,sizeof(sztext),0);
}

//关闭套接字句柄
::closesocket(s);
::closesocket(s1);

//释放套接字库
::WSACleanup();

if(getchar()){
return 0;
}else{
::Sleep(100);//应用休眠0.1秒
}

}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: