Sockets_server.cpp
初始化套结字动态库WSAStartup()->创建套接字socket()->绑定套接字bind()->监听(listen())->接受客户端请求accept()->接收客户端数据recv()->关闭套接字closesocket()->释放套接字资源WSACleanup()
#include "stdafx.h"
#define BUF_SIZE 64
#include "winsock2.h"
#pragma comment(lib, "ws2_32.lib")
int _tmain(int argc, _TCHAR* argv[])
{
WSADATA wsd; //存放windows socket初始化信息
SOCKET sServer; //服务端套接字
SOCKET sClient; //客户端套接字
SOCKADDR_IN addrServ; //服务器地址
char buf[BUF_SIZE]; //接收数据缓冲区
int retVal;
/*
#define MAKEWORD(a,b) ((WORD) (((BYTE) (a)) | ((WORD) ((BYTE) (b))) << 8))
makeword是将两个byte型合并成一个word型,一个在高8位(b),一个在低8位(a)。
在WSAStartup里面,MAKEWORD(2, 2)指Windows Sockets版本号,高位字节指出副版本(修正)号,低位字节指明主版本号。
*/
//初始化套接字动态库
if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
{
printf("WSAStartup failed!\n");
return 1;
}
/*
AF 表示ADDRESS FAMILY 地址族,PF 表示PROTOCOL FAMILY 协议族,
但这两个宏定义是一样的,所以使用哪个都没有关系。
Winsock2.h中#define AF_INET 2,#define PF_INET AF_INET,
所以在windows中AF_INET与PF_INET完全一样。而在Unix/Linux系统中,
在不同的版本中这两者有微小差别。对于BSD,是AF,对于POSIX是PF。
UNIX系统支持AF_INET,AF_UNIX,AF_NS等,而DOS,
Windows中仅支持AF_INET,它是网际网区域。
PF_INET, AF_INET: Ipv4网络协议;
PF_INET6, AF_INET6: Ipv6网络协议。
*/
/*
IPPROTO_TCP:指使用tcp协议, #define IPPROTO_TCP 6 // tcp。
*/
/*
SOCK_STREAM:提供面向连接的稳定数据传输,即TCP协议。
*/
//创建套接字
// 第一个参数,指定地址簇(TCP/IP只能是AF_INET,也可写成PF_INET)
// 第二个,选择套接字的类型(流式套接字)
// 第三个,特定地址家族相关协议(0为自动)
sServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (INVALID_SOCKET == sServer)
{
printf("socket failed!\n");
WSACleanup();//释放套接字资源
return -1;
}
/*
htons在Windows和Linux网络编程时需要用到的,用来将主机字节顺序转化为网络字节顺序,
功能是将一个无符号短整型的主机数值转换为网络字节顺序,即大尾顺序(big-endian),即高位字节存放在内存的低地址处。
网络字节顺序是TCP/IP中规定好的一种数据表示格式,它与具体的CPU类型、操作系统等无关,从而可以保证数据在不同主机之间传输时能够被正确解释,网络字节顺序采用big-endian排序方式。
我们常用的 x86 CPU (intel, AMD) 电脑是 little-endian,也就是整数的低位字节放在内存的低字节处。
而little-endian指,地址的低位存储值的低位。
*/
//设置服务器套接字地址
addrServ.sin_family = AF_INET;
addrServ.sin_port = htons(4999);
addrServ.sin_addr.s_addr = INADDR_ANY;
// 套接字sockSrv与本地地址相连
// int bind(SOCKET s, const struct sockaddr* name, int namelen);
// 第一个参数,指定需要绑定的套接字;
// 第二个参数,指定该套接字的本地地址信息,该地址结构会随所用的网络协议的不同而不同
// 第三个参数,指定该网络协议地址的长度
// PS: struct sockaddr{ u_short sa_family; char sa_data[14];};
// sa_family指定该地址家族, sa_data起到占位占用一块内存分配区的作用
// 在TCP/IP中,可使用sockaddr_in结构替换sockaddr,以方便填写地址信息
//
// struct sockaddr_in{ short sin_family; unsigned short sin_port; struct in_addr si n_addr; char sin_zero[8];};
// sin_family表示地址族,对于IP地址,sin_family成员将一直是AF_INET。
// sin_port指定将要分配给套接字的端口。
// sin_addr给出套接字的主机IP地址。
// sin_zero[8]给出填充数,让sockaddr_in与sockaddr结构的长度一样。
// 将IP地址指定为INADDR_ANY,允许套接字向任何分配给本地机器的IP地址发送或接收数据。
// 如果想只让套接字使用多个IP中的一个地址,可指定实际地址,用inet_addr()函数。
retVal = bind(sServer, (LPSOCKADDR)&addrServ, sizeof(SOCKADDR_IN));
if (SOCKET_ERROR == retVal)
{
printf("bind failed!\n");
closesocket(sServer);//关闭套接字
WSACleanup();
return -1;
}
// 将套接字设置为监听模式(连接请求), listen()通知TCP服务器准备好接收连接
// int listen(SOCKET s, int backlog);
// 第一个参数指定需要设置的套接字,第二个参数为(等待连接队列的最大长度)
retVal = listen(sServer, 1);
if (SOCKET_ERROR == retVal)
{
printf("listen failed!\n");
closesocket(sServer);
WSACleanup();
return -1;
}
//接受客户端请求
sockaddr_in addrClient;
int addrClientlen = sizeof(addrClient);
// 接收连接,等待客户端连接
// SOCKET accept( SOCKET s, struct sockaddr* addr, int* addrlen);
// 第一个参数,接收一个处于监听状态下的套接字
// 第二个参数,sockaddr用于保存客户端地址的信息
// 第三个参数,用于指定这个地址的长度
// 返回的是向与这个监听状态下的套接字通信的套接字
sClient = accept(sServer, (sockaddr FAR*)&addrClient, &addrClientlen);
if (INVALID_SOCKET == sClient)
{
printf("accept failed!\n");
closesocket(sServer);
WSACleanup();
return -1;
}
//接收客户端数据
ZeroMemory(buf, BUF_SIZE);
retVal = recv(sClient, buf, BUF_SIZE, 0);
if (SOCKET_ERROR == retVal)
{
printf("recv failed!\n");
closesocket(sServer);
closesocket(sClient);
WSACleanup();
return -1;
}
printf("%s\n", buf);
//关闭套接字
closesocket(sServer);
closesocket(sClient);
//释放套接字资源
WSACleanup();
return 0;
}
Sockets_Client.cpp
初始化套结字动态库WSAStartup()->创建套接字socket()->连接服务器connect()->向服务器发送数据send()->关闭套接字closesocket()->释放套接字资源WSACleanup()
#include "stdafx.h"
#define BUF_SIZE 64
#include "winsock2.h"
#pragma comment(lib, "ws2_32.lib")
int _tmain(int argc, _TCHAR* argv[])
{
WSADATA wsd; //存放windows socket初始化信息
SOCKET sHost; //服务器套接字
SOCKADDR_IN servAddr; //服务器地址
char buf[BUF_SIZE]; //接收数据缓冲区
int retVal;
//初始化套结字动态库
if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
{
printf("WSAStartup failed!\n");
return -1;
}
//创建服务器套接字
sHost = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (INVALID_SOCKET == sHost)
{
printf("socket failed!\n");
WSACleanup();
return -1;
}
//设置服务器地址
servAddr.sin_family = AF_INET;
servAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
servAddr.sin_port = htons((short)4999);
int nServAddlen = sizeof(servAddr);
//连接服务器
retVal = connect(sHost, (LPSOCKADDR)&servAddr, sizeof(servAddr));
if (SOCKET_ERROR == retVal)
{
printf("connect failed!\n");
closesocket(sHost);
WSACleanup();
return -1;
}
ZeroMemory(buf, BUF_SIZE);
strcpy_s(buf, "MyTCP");
//向服务器发送数据
// 第一个参数,需要发送信息的套接字,
// 第二个参数,包含了需要被传送的数据,
// 第三个参数是buffer的数据长度,
// 第四个参数,一些传送参数的设置
retVal = send(sHost, buf, strlen(buf), 0);
if (SOCKET_ERROR == retVal)
{
printf("send failed!\n");
closesocket(sHost);
WSACleanup();
return -1;
}
//关闭套接字和释放资源
closesocket(sHost);
WSACleanup();
return 0;
}