网络编程
实现了一个简单的基于TCP的Windows服务器,它可以简单的执行一次发送数据和一次接收数据
1、TCP 编程
1.1、前期准备
#include <WinSock32.h> // 包含头文件
#pragma comment(lib, "ws2_32.lib") // 添加静态链接库
int main() {
// 网络环境初始化
WSADATA data;
WORD virsion = MAKEWORD(2, 2);
int err = WSAStartup(virsion, &data);
if (err != 0)
{
printf("WSAStartup errorNum = %d", err);
return err;
}
if (LOBYTE(data.wVersion) != 2 || HIBYTE(data.wVersion) != 2)
{
printf("LOBYTE errorNum = %d", GetLastError());
WSACleanup();
return -1;
}
WSACleanup(); // 清除网络环境
}
1.2、服务器编程
1.2.1、示例代码
int main() {
// 一、初始化网络环境
....;
// 二、创建服务器
SOCKET sockSer = socket(AF_INET, SOCK_STREAM, 0);
if (sockSer == INVALID_SOCKET)
{
printf("socket errorNum = %d\n", GetLastError());
return -1;
}
// 三、分配端口号
SOCKADDR_IN addrSer;
addrSer.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
addrSer.sin_family = AF_INET; // 始终设置为 AF_INET
addrSer.sin_port = htons(5201);
if (bind(sockSer, (SOCKADDR*)&addrSer, sizeof(SOCKADDR)) == SOCKET_ERROE)
{
printf("bind errorNum = %d\n", GetLastError());
WSACleanup();
return -1;
}
// 四、监听套接字
if (listen(sockSer, 5) == SOCKET_ERROR)
{
printf("listen errorNum = %d\n", GetLastError());
WSACleanup();
return -1;
}
// 五、分机处理
SOCKADDR_IN addrCli;
int len = sizeof(SOCKADDR);
while (TRUE)
{
// 六、收发数据
SOCKET sockConn = accept(sockSer, (SOCKADDR*)&addrCli, &len);
char sendBuff[100] = { 0 };
...; // 将要传输的数据传入sendBuff
int iLen = send(sockConn, sendBuff, strlen(sendBuff), 0);
if (iLen == SOCKET_ERROR)
{
printf("send errorNum = %d\n", GetLastError());
WSACleanup();
return -1;
}
char recvBuff[100] = { 0 };
iLen = recv(sockConn, recvBuff, 100, 0);
if (iLen <= 0)
{
printf("recv errorNum = %d\n", GetLastError());
WSACleanup();
return -1;
}
printf("recv:%s", recvBuff);
closesocket(sockConn); // 关闭分机
}
// 关闭总机
closesocket(sockSer);
WSACleanup();
return 0;
}
1.2.2、主要函数
-
创建服务器套接字:
socket()
SOCKET WSAAPI socket(int af, int type, int protocol );
第一个参数为 地址系列规范,使用IPv4 为 AF_INET,使用IPv6为AF_INET6
第二个参数为 套接字的类型规范,地址系列为为IPv4或IPv6的情况下,TCP协议则为SOCK_STREAM,UDP协议为SOCK_DGRAM
第三个参数为 要使用的协议,为 0 则调用方不希望指定协议,服务提供商将选择要使用的 协议 。
-
绑定本机 IP 和端口:
bind()
int bind(
SOCKET s, // 创建的socket
sockaddr * name, // 包含地址和端口的结构体 int namelen // sockaddr 结构长度
);
// 返回值:返回SOCKET_ERROR失败,该宏被定义为-1,否则成功,返回值为0
-
监听客户端:
listen()
int listen(
SOCKET s, // 要监听的socket
int backlog // 等待连接的最大队列长度
);
//返回值:返回SOCKET_ERROR失败,该宏被定义为-1,否则成功,返回值为0
-
等待客户端连接:
accept()
SOCKET accept(
SOCKET s, //接收的socket
sockaddr* addr, //接收到客户端的地址信息
int * addrlen //地址信息长度
);
//返回值:返回INVALID_SOCKET失败,该宏定义为-1,否则成功返回客户端的套接字,可进行发送和接收消息
-
发送信息:
send()
-
接收信息:
recv()
int send (
SOCKET s, char * buf, //要发送的内容
int len, //内容长度
int flags //一般为0
);
//返回值:-1(或宏SOCKET_ERROR)表示发送失败,否则返回发送成功的字节数
int recv (
SOCKET s, //套接字
char * buf, //接受数据的缓存区
int len, //缓存区大小
int flags //标志,一般填0
);
//返回值:小于等于0都表示出错,大于0则表示接收成功的数据大小
-
网络环境初始化:
WSAStartup()
-
关闭 socket :
closesocket()
-
清除网络环境:
WSACleanup()
1.3、客户端编程
1.3.1、示例代码
#include <WS2tcpip.h> // 配置套接字地址新函数的头文件
int main() {
// 一、初始化网络环境
....;
// 二、创建套接字
SOCKET sockCli = socket(AF_INET, SOCK_STREAM, 0);
if (sockCli == INVALID_SOCKET)
{
printf("socket errorNum = %d\n", GetLastError());
return -1;
}
// 三、配置要连接的服务器
SOCKADDR_IN addrCli;
inet_pton(AF_INET, "客户端的ip地址", &addrCli.sin_addr.S_un.S_addr);
addrCli.sin_family = AF_INET;
addrCli.sin_port = htons(5201);
// 四、连接服务器
if (connect(sockCli, (SOCKADDR*)&addrCli, sizeof(SOCKADDR) == SOCKET_ERROR))
{
printf("connect errorNum = %d\n", GetLastError());
WSACleanup();
return -1;
}
// 五、收发数据
char recvBuff[100] = { 0 };
int iLen = recv(sockCli, recvBuff, 100, 0);
if (iLen <= 0)
{
printf("recv errorNum = %d\n", GetLastError());
WSACleanup();
return -1;
}
printf("recv:%s", recvBuff);
char sendBuff[100] = { 0 };
...; // 写入自己想发送的数据
iLen = send(sockCli, sendBuff, strlen(sendBuff), 0);
if (iLen == SOCKET_ERROR)
{
printf("send errorNum = %d\n", GetLastError());
WSACleanup();
return -1;
}
// 六、关闭客户机
closesocket(sockCli);
WSACleanup();
system("pause");
return 0;
}
1.3.2、主要函数
-
连接服务器函数:
connect()
int connect(
SOCKET s, //与服务器连接的socket
sockaddr* name, //服务器的地址端口
int namelen //上个参数结构体的长度
);
//返回值:-1失败,否则成功