1、在Windows操作系统环境下,使用Windows Sockets API进行网络程序开发时,需要调用Windows操作系统的Windows Sockets动态库 包含Sockets头文件 导入相应库文件;下面是相应的导入
具体用代码实现是这样的:
这是用1.1版本
#include <WINSOCK.h>
#pragma comment(lib, "wsock32.lib");
2.2版本
#include <WINSOCK2.h>
#pragma comment(lib, "WS2_32.lib");
也可在项目属性当中设置库文件,根据下面的步骤在附加依赖项里面写入上面库文件的名称
配置属性-》连接器-》输入-》附加依赖项
2、下面是这个Winsock程序的起始与结束
3、具体函数使用
1)int WSAStartup( WORD wVersionRequested, LPWSADATA lpWSAData );
作用:完成WinSock的初始化任务,协商WinSock的版本支持,并分配必要的资源。
参数:
wVersionRequested参数用于指定准备加载的Winsock库的版本。高位字节指定所需要的Winsock库的副版本,而低位字节则是主版本。可用MAKEWORD(x,y)(其中,x是高位字节,y是低位字节)方便地获得wVersionRequested的正确值
lpWSAData参数是指向WSADATA结构的指针,WSAStartup用其加载的库版本有关的信息填在这个结构中。即返回Winsock的实现信息。
返回:
如果WinSock.dll或底层网络子系统没有被正确初始化或没有被找到,WSAStartup将返回WSASYSNOTREADY。
调用成功WSAStartup将返回0。
此函数允许你的应用程序协商使用某种版本的WinSock规范,如果请求的版本等于或高于DLL所支持的最低版本,WSAData的wVersion成员中将包含你的应用程序应该使用的版本,它是DLL所支持的最高版本与请求版本中较小的那个。反之,如果请求的版本低于DLL所支持的最低版本,WSAStartup将返回WSAVERNOTSUPPORTED。
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, *LPWSADATA;
各个成员说明:
注意:一般不要使用下面这两个字段:
iMaxSockets和iMaxUdpDg,它们是假定同时最多可打开多少套接字和数据报的最大长度。然而,要知道数据报的最大长度应该通过WSAEnumProtocols来查询协议信息。同时最多可打开套接字的数目不是固定的,很大程度上和可用物理内存的多少有关。
2)int WSACleanup() ;
作用:当不需WinSock DLL的服务,释放DLL所使用的资源。
参数:无
返回:调用成功返回0
对应于每一次WSAStartup()调用必须有一个WSACleanup()调用。
3)SOCKET socket( int af, int type, int protocol );
作用:创建不同类型的套接字用来通信
参数:
af:指定地址族,对于TCP/IPv4协议的套接字,它只能是AF_INET(也可写成PF_INET)
type:指定Socket类型,对于1.1版本的Socket,它只支持两种类型的套接字,SOCK_STREAM指定产生流式套接字,SOCK_DGRAM产生数据报套接字
protocol:指定与特定的地址家族相关的协议,如果指定为0,那么它就会根据地址格式和套接字类别,自动选择一个合适的协议。
返回:
如果函数调用成功,它将返回一个新的SOCKET数据类型的套接字描述符。
如果调用失败,这个函数就会返回一个INVALID_SOCKET,错误信息可以通过WSAGetLastError函数返回。
下面是相关参数的对应说明:
4)WSAGetLastError();
作用:调用任何一个WinSock函数之后可用WSAGetLastError函数来获得详细的错误代码 int WSAGetLastError (void);
参数:无
返回值 详细的错误代码(可以通过相应的代码查到相应的错误)
5)int bind( SOCKET s, const struct sockaddr FAR *name, int namelen );
作用:对创建的套接字绑定一个ip,一般用在服务器端
参数:
s:指定要绑定的套接字
name:指定了该套接字的本地地址信息,是指向sockaddr结构的指针变量,由于该地址结构是为所有的地址家族准备的,这个结构可能(通常会)随所使用的网络协议不同而不同,所以,要用
namelen:指定该地址结构的长度。
返回:
如果这个函数调用成功,它将返回0。
如果调用失败,这个函数就会返回一个SOCKET_ERROR,错误信息可以通过WSAGetLastError函数返回。
6)地址结构体解析:
struct sockaddr { u_short sa_family; char sa_data[14]; };
sa_family指定该地址家族,在这里IPv4必须设为AF_INET。
sa_data仅仅是表示要求一块内存分配区,起到占位的作用,该区域中指定与协议相关的具体地址信息。由于实际要求的只是内存区,所以对于不同的协议家族,用不同的结构来替换sockaddr。在IPv4中,我们用sockaddr_in结构替换sockaddr,以方便我们填写地址信息。
除了sa_family外,sockaddr是按网络字节顺序表示的。
sockaddr_in的结构如下:
struct sockaddr_in{ short sin_family; unsigned short sin_port; struct in_addr sin_addr; char sin_zero[8]; };
sin_family表示地址族,对于IPv4地址,sin_family成员将一直是AF_INET。
成员sin_port指定的是将要分配给套接字的端口
成员sin_addr给出的是套接字的主机IP地址。
成员sin_zero只是一个填充数,以使sockaddr_in结构和sockaddr结构的长度一样。
struct in_addr {
union {
struct { u_char s_b1,s_b2,s_b3,s_b4; } S_un_b;
struct { u_short s_w1,s_w2; }S_un_w;
u_long S_addr;
} S_un;
}
下面对sockaddr和sockaddr_in对比:
所以很多时候,我们绑定ip的时候是这样的:
sockaddr_in s;
s.sin_addr.s_un.s_addr = ip;
一般服务器这样设置ip
将IP地址指定为INADDR_ANY,允许套接字向任何分配给本地机器的IP地址发送或接收数据。 将地址指定为INADDR_ANY,允许一个独立应用接受发自多个接口的回应。
如果想要自己设置ip,需要用到下面的函数:
unsigned long inet_addr(constchar FAR *
cp)
作用:该函数将一个点分十进制IP地址字符串转换成32位数字表示的IP地址,适合分配给S_addr的u_long类型的数值
这个函数是上面函数的反函数:
char * inet_ntoa ( struct in_addr in ) in为传入参数,表示一个结构型的IP主机地址,该函数将一个32位数字表示的IP地址转换成点分十进制IP地址字符串
这些结构的宏定义
#define s_addr S_un.S_addr #define s_host S_un.S_un_b.s_b2 #define s_net S_un.S_un_b.s_b1 #define s_imp S_un.S_un_w.s_w2 #define s_impno S_un.S_un_b.s_b4 #define s_lh S_un.S_un_b.s_b3
7)int listen ( SOCKET s, int backlog );
作用:服务器监听某个套接字,以便处理各个连接
参数:
s:要监听的套接字
backlog:等待连接队列所能放置的连接数的最大数
返回:如果这个函数调用成功,它将返回0。如果调用失败,这个函数就会返回一个SOCKET_ERROR,错误信息可以通过WSAGetLastError函数返回。
8)SOCKET accept ( SOCKET s, struct sockaddr FAR* addr, int FAR* addrlen );
作用:用来接收某个连接的套接字
s:接收连接的Socket
addr:建立连接的对方地址信息
addrlen:addr的长度
返回:如果函数调用成功,它将返回一个新的SOCKET数据类型的套接字描述符。 如果调用失败,这个函数就会返回一个INVALID_SOCKET,错误信息可以通过WSAGetLastError函数返回。
9)int send ( SOCKET s, const char FAR * buf, int len, int flags );
作用:向目的主机发送数据
参数:
flags:用于控制数据传输方式,0表示按正常方式发送数据;宏MSG_DONTROUTE说明系统目标主机就在直接连接的本地网络中,无需路由选择;MSG_OOB指出数据是按带外数据发送的
s:发送的套接字
buf:发送数据所在缓冲区的首地址
len:发送数据的大小
返回:如果这个函数调用成功,它将返回发送的字节数。如果调用失败,这个函数就会返回一个SOCKET_ERROR,错误信息可以通过WSAGetLastError函数返回。
10)int recv ( SOCKET s, char FAR* buf, int len, int flags );
作用:接受数据
参数:
flags:指定调用的方式。0表示接收的是正常数据,无特殊行为。MSG_PEEK表示会使有用的数据复制到所提供的接收端缓冲区内,但是没有从系统缓冲区中将数据删除。MSG_OOB表示处理带外数据。
s:接受数据的套接字
buf:接受缓冲区的首地址
len:接受缓冲的大小
返回:如果这个函数调用成功,它将返回接收到的字节数。如果连接被关闭,返回值为0。调用失败,这个函数就会返回一个SOCKET_ERROR,错误信息可以通过WSAGetLastError函数返回。
11)int connect ( SOCKET s, const struct sockaddr FAR* name, int namelen );
作用:在客户端使用该函数请求建立连接时,将激活建立连接的三次握手,用来建立一条到服务器TCP的连接。
参数:
s:要连接的套接字
name:地址结构
namelen:地址结构长度
返回:
如果这个函数调用成功,它将返回0。如果调用失败,这个函数就会返回一个SOCKET_ERROR,错误信息可以通过WSAGetLastError函数返回。
若TCP没有收到SYN分节的响应,则返回WSAETIMEDOUT
若对客户SYN的响应是RST,则表明该服务器主机在我们指定的端口上没有进程在等待与之连接(例如服务器进程也许没有运行) ,客户一收到RST就马上返回WSAECONNREFUSED
若客户发出的SYN在中间的某个路由器上引发了一个目的不可达ICMP错误,则客户主机内核保存该消息,并按一定时间间隔继续发送SYN,若在规定的时间仍未收到响应,则返回错误WSAEHOSTUNREACH 或WSAENETUNREACH
12) int closesocket( SOCKET s );
作用:关闭套接字,释放所占资源
13)int shutdown( SOCKET s, int how );
作用:在一个套接字上的读写操作完成后,应该首先使用shutdown()函数来关闭套接字的读通道、写通道或读写通道,这样做的好处是当双方不再有数据要发送或接收时,可以通知对方, “优雅”地关闭连接。
参数:
s:关闭的套接字
how:用来规定关闭的方式
shutdown函数只关闭读写通道,并不关闭套接字,且套接字所占有的资源将被一直保留到closesocket()调用之前。
UDP接受发送数据与TCP接受数据大致相似,不过由于udp在通信之前不用连接,所以在发送的时候要加入地址信息:
int recvfrom ( SOCKET s, char FAR* buf, int len, int flags, struct sockaddr FAR* from, int FAR* fromlen );
int sendto ( SOCKET s, const char FAR * buf, int len, int flags, const struct sockaddr FAR * to, int tolen );
附送源码(服务器时间同步)实例(客户端):
#include <stdio.h> #include <Winsock2.h> #pragma comment(lib, "WS2_32.lib"); int main() { WORD wVersionRequested; WSADATA wsaData; int err; wVersionRequested = MAKEWORD(2, 2); err = WSAStartup(wVersionRequested,&wsaData); if (err != 0) { printf("WSAStartup failed witherror: %d\n", err); return 0; } SOCKET mySocket = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in s; s.sin_family = AF_INET; s.sin_port = 5555; s.sin_addr.S_un.S_addr = inet_addr("xxx.xxx.xxx.xxx"); err = connect(mySocket, (struct sockaddr*)&s, sizeof(s)); if (SOCKET_ERROR == err) { int err_num = WSAGetLastError(); printf("connect error%d\n", err_num); system("pause"); return 1; } char sendBuff[200]; char recvBuff[200]; memset(recvBuff, 0, 200); memset(sendBuff, 0, 200); while (1) { gets(sendBuff); send(mySocket, (const char*)sendBuff, 200, 0); err = recv(mySocket, recvBuff, 200, 0); if (SOCKET_ERROR == err) { int err_num = WSAGetLastError(); printf("recv′í?ó%d\n", err_num); system("pause"); return 1; } printf("%s", recvBuff); printf("echo once again?\n"); memset(recvBuff, 0, 200); memset(sendBuff, 0, 200); } closesocket(mySocket); WSACleanup(); return 0; }
服务器端
#include <stdio.h> #include <Winsock.h> #include <time.h> #include <ctime> #include <stdlib.h> #pragma comment(lib, "WS2_32.lib"); int main() { WORD wVersionRequested; WSADATA wsaData; int err; wVersionRequested = MAKEWORD(2, 2); err = WSAStartup(wVersionRequested,&wsaData); if (err != 0) { printf("WSAStartup failed witherror: %d\n", err); return 0; } SOCKET mySocket = socket(AF_INET, SOCK_STREAM, 0); SOCKET clientS; struct sockaddr_in s; s.sin_family = AF_INET; s.sin_port = 5555; s.sin_addr.S_un.S_addr = INADDR_ANY; err = bind(mySocket, (struct sockaddr*)&s, sizeof(s)); if (SOCKET_ERROR == err) { int err_num = WSAGetLastError(); printf("bind error %d\n", err_num); system("pause"); return 1; } err = listen(mySocket, 2); if (SOCKET_ERROR == err) { int err_num = WSAGetLastError(); printf("listen error %d\n", err_num); system("pause"); return 1; } printf("Waiting for client connecting!\n"); int size = sizeof(s); clientS = accept(mySocket, (struct sockaddr*)&s, &size); if (INVALID_SOCKET == clientS) { printf("accept error\n");return 1; } char sendBuff[200]; char recvBuff[200]; memset(recvBuff, 0, 200); memset(sendBuff, 0, 200); time_t t; while (1) { err = recv(clientS, recvBuff, 200, 0); if (SOCKET_ERROR == err) { int err_num = WSAGetLastError(); printf("recv error %d\n", err_num); system("pause"); return 1; } t = time(&t); strcat(sendBuff, "echo:"); strcat(sendBuff+sizeof("echo:")-1, ctime(&t)); printf("send:%s", sendBuff+sizeof("echo:")); err = send(clientS, sendBuff, 200, 0); if (SOCKET_ERROR == err) { int err_num = WSAGetLastError(); printf("send error %d \n", err_num); system("pause"); return 1; } memset(sendBuff, 0, 200); } closesocket(mySocket); WSACleanup(); return 0; }