Windows Socket 编程
一、 Windows sockets开发概述
网络开发可被理解为:基于互联网,利用网络开发技术,开发能运行在网络上的软件,如网络游戏、聊天软件等。
Windows sockets是一套在Microsoft Windows环境下的网络编程接口,它包含一组库函数,使开发人员可以利用Windows消息驱动机制进行网络应用程序开发
Windows sockets 主要有 Windows sockets 1.1(1.0)和Windows sockets 2.2(2.0) 两个版本。
版本 | 头文件 | 库文件 | 动态库 |
1.1 | WINSOCK.h | wsock32.lib | WINSOCK.DLL |
2.2 | WINSOCK2.h | WS2_32.lib | WS2_32.dll |
Socket1.1版本中用到的库函数主要有:
Accept(), bind(), closesocket(), connect(), getpeername(), getsockname(), htonl(), htons(), inet_addr(), inet_ntoa(), listen(), ntohl(), ntohs(), recv(), recvfrom(), select(), send(), sendto(), shutdown(), socket()等
Socket1.1版本中对Windows扩展的函数主要有:
WSAStartup(), WSACleanup(), WSAGetLastError(), WSAIsBlockint()等
Socket2.0版本中对Windows扩展的函数主要有:
WSAACCEPT(), WSACONNECT(), WSAHTONL(), WSAHTONS(), WSANTOHL(), WSANTOHS(), WSARECV(), WSARECVFROM(), WSASEND(), WSASENDTO(), WSASOCKET()等
二、TCP/IP 简介
& TCP/IP协议
TCP/IP协议是网络通信中最常用的协议
OSI标准 | TCP/IP模型 | 协议 |
应用层 | 应用层 | HTTP/FTP |
表示层 |
|
|
会话层 |
|
|
传输层 | 传送层 | TCP/UDP |
网络层 | 网络层 | IP/ARP(地址解析协议) |
数据链路层 | 数据链路层 |
|
物理层 |
|
|
套接字:应用层到传送层的接口
& 端口:
0-1023:通用端口号
1024-49151:应用程序使用
49152-65535:动态、私有端口
三、WINDOWS SOCKETS 基础
(一)套接字
套接字:网络应用程序借口,应用层与传送层得接口
(二)TCP,UDP 连接过程
Tcp |
| Udp | ||
Server | Client |
| Server | Client |
创建套接字 | 创建套接字 |
| 创建套接字 | 创建套接字 |
绑定 |
|
| 绑定 | 发送数据 |
监听 | 连接 |
| 接受数据 |
|
接受连接 |
|
| 关闭 | 关闭 |
收发数据 | 收发数据 |
|
|
|
关闭 | 关闭 |
|
|
|
对应tcp、udp,至少有两种套接字可供编程使用,即流套接字和数据包套接字
在Windows sockets中有一个新的数据类型SOCKET表示套接字,声明如下:
Typedef unsigned int u_int;
Typedef u_int SOCKET;
即用一个特殊的整型变量表示套接字
(三)sockets中用到的函数
& 开始sockets编程
利用vc 开发sockets程序时,需首先导入库文件,导入库文件的方法:
1.头文件中添加代码:#pragma comment(lib,"wsock32.lib");或"WS2_32.lib"
2.菜单栏"project"—"project settings"对话框---"link"选项卡---"category"下拉框中选择"input"----"object/library modules"文本框中添加"wsock32.lib"(前面加空格)
推荐使用第一种方法,因第二种方法在每次打开工程时,都需从新执行添加步骤。
Socket2版本兼容socket1版本,但如果程序中用到socket2版本中的函数时,需导入socket2的库文件。
& WSAStartup()
功能:对Winsock服务的初始化。应用程序或DLL只能在一次成功的WSAStartup()调用之后才能调用进一步的Windows Sockets API函数。
声明:WSAStartup(
__in WORD wVersionRequested, //程序请求使用的Socket版本
__out LPWSADATA lpWSAData) //操作系统返回支持的Socket的版本
返回:成功时返回0
wVersionRequested:准备加载的动态库版本,高字节指定副版本,低字节指定主版本
LpWSAData:指针,WSADATA结构,返回被加载的动态库特征
用到的其他知识:
wVersionRequested对象可用MAKEWORD函数进行赋值e.g.MAKEWORD(2.0)
wsaData返回后,可用LOBYTE(wsaData),HIBYTE(wsaData)查看返回的主版本号与副版本号
例如:
WORD wVersionRequest MAKEWORD(2,2);
WSADATA wsaData;
int errCode=WSAStartup(wVersionRequest, &wsaData); //初始化
if(errCode != 0){......} //error
if(LOBYTE(wsaData)!=2 || HIBYTE(wsaData)!=2){……}; //主、副版本号
& socket()
功能:创建套接字,利用socket1.1版本中的socket()函数或socket2.2版本中的WSASocket()函数可实现此功能
声明:SOCKET socket(
__in Int af, //协议的地址家族
__in Int type, //协议的套接字类型
__in Int protocol) //协议
af:地址家族,创建tcp或udp连接时,此参数为AF_INET(Address family地址家族)
type:套接字类型,可选项有:
SOCK_STREAM(流式套接字,tcp使用)
SOCK_DGRAM(数据报套接字,udp使用)
SOCK_RAM(原始套接字,直接处理ip协议)
protocol:对于SOCK_STREAM,该字段为IPPROTO_TCP或0(自动匹配)
对于SOCK_DGRAM,该字段为IPPROTO_UDP或0(自动匹配)
返回:socket 类型,失败时返回INVALID_SOCKET
用到的其他知识:
Socket类型:
定义:typedef unsigned int u_int;
Typedef u_int SOCKET;
Socket对象也就是一个特殊的整数
取值范围:0到(INVALID_SOCKET - 1)
创建时,若返回值为INVALID_SOCKET,则创建失败。
#define INVALID_SOCKET (SOCKET)(~0)
例如:
SOCKET s=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
If(INVALID_SOCKET == s) {……} //创建失败
& bind()
功能:套接字绑定到地址
声明:int bind(
__in SOCKET s, //要绑定的套接字
__in const struct sockaddr FAR* name , //绑定到的地址
__in int namelen); //地址的长度
返回:失败时为SOCKET_ERROR;
例如:
Struct sockaddr_in servAddr;
servAddr.sin_family=AF_INET;
servAddr.sin_port=htons(4999);
servAddr.sin_addr.s_addr=htonl(INADDR_ANY);
//servAddr.sin_addr.s_addr=inaddr_any
//servAddr.sin_addr.s_addr=inet_addr("127.0.0.1");
int errorCode=bind(s, (SOCKET*)&servAddr, sizeof(servAddr)):
if(SOCKET_ERROR==errorCode) {……} //error
& listen()启动监听
功能:将套接字设置为监听模式
声明:int listen(
__in socket s, //要设置为监听的套接字
__in int backlog); //等待队列最大长度
返回:成功时返回0,失败时返回 SOCKET_ERROR
例如:
Int Val=listen(s, 5);
If(SOCKET_ERROR==Val) {……} //error
& accept()
功能:接受一个连接请求
声明:socket accept(
__in socket s, //监听套接字
__out struct sockaddr FAR* addr, //地址
__out int FAR* addrlen) //地址长度
返回:代表客户端的socket ,失败时返回 INVALID_SOCKET;
例如:
SOCKET sAccept;
Sockaddr_in addrClient;
Int addrClientLen=sizeof(addrClient);
sAccept=accept(s,(SOCKADDR*)&addrClient,&addrClientLen);
if(INVALID_SOCKET==sAccept) {……} //error
& recv() 或 WSARecv()
功能:接收数据
声明:int recv(
__in socket s, //套接字
__in char FAR* buf, //缓冲区
__in int len, //缓冲区长度
__in int flags) //标志,0:无特殊行为
返回:成功时,返回接受的字节数,失败时,返回SOCKET_ERROR
例如:
Char buf[BUF_LEN];
Int readLen;
readLen=recv(sAccept,buf,BUF_LEN,0);
if(SOCKET_ERROR==readLen) {……} //error
& send() 或 WSASend()
功能:发送数据
声明:int send(
__in socket s, //套接字
__in const char FAR* buf, //缓冲区
__in int len , //缓冲区长度
__in int flags) //标志,0:无特殊行为
返回:失败时,SOCKET_ERROR
例如:
Char buf[BUF_LEN];
Int writeLen;
writeLen= send (sAccept,buf,BUF_LEN,0);
if(SOCKET_ERROR==writeLen) {……} //error
& Closesocket()
功能:关闭套接字,释放资源
声明:int closesocket(
__in s); //要关闭的套接字
返回:WSAENOTSOCK错误
& Shutdown()
功能:关闭套接字连接
声明:int shutdown(
__in socket s, //套接字
__in int how); // SD_RECEIVE:不接受,SE_SEND:不发送,SD_BOTH:都不
返回:int
& connect()
功能:连接到服务器
声明:int connect(
__in socket s, //套接字
__in const struct sockaddr FAR* name , //服务器地址
__in int namelen) //服务器地址长度
返回:失败时,返回SOCKET_ERROR
例如:
SOCKET sHost; //连接服务器的套接字
SOCKEADDR_IN addrServerAddr;//服务器地址
sHost=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
addrServerAddr.sinfamily=AF_INET;
addrServerAddr.sin_port=htons(serverPort);
addrServerAddr.sin_addr.s_addr=htohl(serverIP);
int Val= connect (sHost, (LPSOCKADDR)&addrServerAddr, sizeof(addrServerAddr));
if(SOCKET_ERROR==Val) {……} //error
& sendto()
功能:发送数据
定义:int sendto(
SOCKET s,
const char FAR* buf,
int len,
int flags,
const struct sockaddr FAR* to,
int tolen);
& recvfrom()
功能:接受数据,返回发送数据主机的地址
声明:int recvfrom(
__in SOCKET s, //接受套接字
__in char FAR* buf; //接受缓冲区
__in len, //接受缓冲区大小
__out struct sockaddr FAR* from; //返回发送主机地址
__out int FAR* fromlen) //地址长度
返回:成功时,返回接受数据的长度;失败时,返回SOCKET_ERROR
例如:
char buf[BUF_LEN];
SOCKADDR_IN addrClient;
Int nAddrClient=sizeof(addrClient);
recvfrom(s,buf,BUF_SIZE,0,(SOCKADDR*)&c addrClient,& nAddrClient)
& Socketaddr_in 结构
Struct sockaddr | Struct sockaddr_in | |
{ unsigned short sa_family; har sa_data[14] } | { short sin_family; Unsigned short sin_port; Struct in_addr sin_addr; Unsigned char sin_zero[8]; } | |
Sa_family:地址家族, 大多为AF_INET, 表示ip协议簇 Sa_data:协议地址 | Sin_family:地址家族, 大多为AF_INET,表示ip协议簇 Sin_port:端口号 Sin_addr:存储ip地址 Sin_zero:填充用 | |
| 在linux下 | 在windows下 |
| Typedef struct in_addr { unsigned long s_addr; } | Typedef struct in_addr { Union s_un{ Struct { char s_b1, s_b2, s_b3, s_b4;} s_un_b; Struct { unsigned short s_w1,s_w2;}s_un_w; Unsigned long s_addr; }s_un }in_addr |
| S_addr:存储的ip地址 |
|
代码:
/*
*socket编程--tcp连接--服务器端
*/
#include <iostream>
using namespace std;
#include <winsock2.h>
#pragma comment(lib,"ws2_32.lib")
#define DEFAULT_PORT 9090
int main()
{
//==========初始化socket库环境===========
WSADATA wsaData;
if(WSAStartup(MAKEWORD(2,2),&wsaData)!=0)
{
cout<<"WSAStartup failed"<<endl;
return -1;
}
//===========创建套接字=================
SOCKET sServer; //服务器套接字
sServer=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(INVALID_SOCKET==sServer)
{
cout<<"socket failed"<<endl;
WSACleanup();
return -1;
}
//===========绑定套接字=================
SOCKADDR_IN addrServer;
addrServer.sin_family=AF_INET;
addrServer.sin_port=htons(DEFAULT_PORT);
addrServer.sin_addr.s_addr=INADDR_ANY;
//addrServer.sin_addr.s_addr=htonl(INADDR_ANY);
//addrServer.sin_addr.s_addr=inet_addr("127.0.0.1");
int val=bind(sServer,(LPSOCKADDR)&addrServer,sizeof(SOCKADDR_IN));
if(SOCKET_ERROR==val)
{
cout<<"bind failed"<<endl;
closesocket(sServer);
WSACleanup();
return -1;
}
//===========监听=======================
val=listen(sServer,SOMAXCONN);
if(SOCKET_ERROR==val)
{
cout<<"listen failed"<<endl;
closesocket(sServer);
WSACleanup();
return -1;
}
//===========接受客户端连接==============
SOCKET sClient;
sockaddr_in addrClient;
int addrClientLen=sizeof(addrClient);
sClient=accept(sServer,(sockaddr FAR*)&addrClient,&addrClientLen);
if(INVALID_SOCKET==sClient)
{
cout<<"accept failed"<<endl;
closesocket(sServer);
WSACleanup();
return -1;
}
//=========接收数据==========================
char buf[1024];
ZeroMemory(buf,1024);
val=0;
val=recv(sClient,buf,1024,0);
if(SOCKET_ERROR==val)
{
cout<<"recv failed"<<endl;
closesocket(sServer);
closesocket(sClient);
WSACleanup();
return -1;
}
cout<<buf<<endl;
//===========退出===========================
closesocket(sServer);
closesocket(sClient);
WSACleanup();
return 0;
}
/*
*socket编程--tcp连接--客户端
*/
#include <iostream>
using namespace std;
#include <winsock2.h>
#pragma comment(lib,"ws2_32.lib")
#define DEFAULT_PORT 9090
#define BUF_SIZE 1024
int main()
{
SOCKET sHost; //服务器套接字
SOCKADDR_IN servAddr; //服务器地址
char buf[BUF_SIZE]; //接受数据缓冲区
int retVal;
//=========创建套接字====================
WSADATA wsd; //WSADATA变量
if(WSAStartup(MAKEWORD(1,0),&wsd)!=0)
{
cout<<"WSAStartup failed!"<<endl;
return -1;
}
if((int)LOBYTE(wsd.wVersion)!=1 || (int)HIBYTE(wsd.wVersion)!=0)
{
cout<<"version error!"<<endl;
return -1;
};
cout<<"WSAStartup ok......"<<endl;
//===========建立套接字=================
sHost=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(INVALID_SOCKET==sHost)
{
cout<<"socket failed!"<<endl;
WSACleanup();
return -1;
}
cout<<"socket ok......"<<endl;
//=============连接服务器=================
servAddr.sin_family =AF_INET;
servAddr.sin_addr .s_addr=inet_addr("127.0.0.1");
servAddr.sin_port =htons(DEFAULT_PORT);
retVal=connect(sHost,(LPSOCKADDR)&servAddr,sizeof(servAddr));
if(SOCKET_ERROR==retVal)
{
cout<<"connect failed!"<<endl;
closesocket(sHost);
WSACleanup();
return -1;
}
cout<<"connect success!"<<endl;
//==============发送数据===================
ZeroMemory(buf,BUF_SIZE);
strcat(buf,"hello");
retVal=send(sHost,buf,strlen(buf),0);
if(SOCKET_ERROR==retVal)
{
cout<<"send failed!"<<endl;
closesocket(sHost);
WSACleanup();
return -1;
}
cout<<"send "<<retVal<<" bytes."<<endl;
cout<<"they are:"<<buf<<endl;
ZeroMemory(buf,BUF_SIZE);
//=============接收数据==============
retVal=recv(sHost,buf,BUF_SIZE,0);
if(SOCKET_ERROR==retVal)
{
printf("recv failed!\n");
closesocket(sHost);
WSACleanup();
return -1;
}
cout<<"recv access,recv "<<retVal<<" bytes,they are:"<<endl;
cout<<buf<<endl;
//--------退出
shutdown(sHost,SD_BOTH);
closesocket(sHost);
WSACleanup();
cout<<"closed"<<endl;
return 0;
}