服务器和客户端简单通信的流程,做一个简单的复习:
1.服务器创建的流程
代码如下,各个重要函数已经写注释:
#include <iostream>
// 推荐加上宏定义
#define WIN32_LEAN_AND_MEAN
#include <winsock2.h>
#include <Windows.h>
#pragma comment(lib, "ws2_32")
using namespace std;
int main()
{
auto _is_good = [](bool flag, string str)
{
if (!flag)
cout << str << " successfully" << endl;
else
cout << str << " failure" << endl;
};
char info[] = "hello";
// 创建 Windows Sockets 2.x版本
WORD ver = MAKEWORD(2, 2);
// 初始化 Windows Sockets 库
// WSAStartup() 函数用于初始化 Windows Sockets 库。
// ver 参数指定 Windows Sockets 版本号,dat 参数用于接收 Windows Sockets 库的初始化信息。
WSADATA dat; // Socket的各种数据结构
WSAStartup(ver, &dat);
//---------------------------------
// 1socket API建立简易Tcp服务端
// 建立一个socket数据类型
// AF_NET是ipv4,SOCK_STREAM代表流式socket,IPPROTO_TCP代表了TCP协议
SOCKET _sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
// 2bind绑定用于接受客户端连接的网络端口
sockaddr_in _sin = {}; // 表示socket地址,储存端口号和ip
_sin.sin_family = AF_INET; // 设置地址族为ipv4
_sin.sin_port = htons(4567); // 设置端口号4567,需要htons将主机字节顺序转换成为网络字节顺序
_sin.sin_addr.S_un.S_addr = INADDR_ANY; // 设置地址为任意地址
// 将套接字绑定在指定的地址和端口上
bool flag = (bind(_sock, (sockaddr *)&_sin, sizeof(_sin)) == SOCKET_ERROR);
_is_good(flag, "bind");
// 3listen监听网络端口
// 让套接字进入监听状态,缓冲区最大排队数量为5
flag = listen(_sock, 5) == SOCKET_ERROR;
_is_good(flag, "listen");
sockaddr_in clientAddr = {}; // 创建客户端ip
int nAddrLen = sizeof(sockaddr_in);
SOCKET _cSock = INVALID_SOCKET; // 给客户端创建一个套接字,客户端套接字句柄初始化为无效套接字
while (1)
{ // 4accept等待接受客户端连接
_cSock = accept(_sock, (sockaddr *)&clientAddr, &nAddrLen); // 返回值给客户端socket
flag = (INVALID_SOCKET == _cSock); // 判断是否接受到无效客户端socket
_is_good(flag, "accept");
cout << "新客户端加入:IP=" << inet_ntoa(clientAddr.sin_addr) << endl;
// 5send向客户端发送一条数据
send(_cSock, info, strlen(info) + 1, 0);
// send函数
// ssize_t send(int socket, const void *buf, size_t len, int flags);
// 参数说明:
// socket:指定发送端套接字描述符。
// buf:指向一个存放应用程序要发送数据的缓冲区。
// len:指定要发送的数据长度。
// flags:指定发送数据的标志。
// send函数的返回值:
// 成功时,返回实际发送的数据长度。
// 失败时,返回SOCKET_ERROR。
}
// 6关闭套接字close socket
closesocket(_sock);
//---------------------------------
// 清理 Windows Sockets 库 WSACleanup() 函数用于清理 Windows Sockets 库。
WSACleanup();
return 0;
}
2.客户端创建的流程
#include <iostream>
// 推荐加上宏定义
#define WIN32_LEAN_AND_MEAN
#include <winsock2.h>
#include <Windows.h>
#pragma comment(lib, "ws2_32")
using namespace std;
int main()
{
auto func = [](bool flag, string str)
{
if (!flag)
cout << str << " successfully" << endl;
else
cout << str << " failure" << endl;
};
char ipaddr[] = "127.0.0.1";
WORD ver = MAKEWORD(2, 2);
WSADATA dat;
WSAStartup(ver, &dat);
// 创建一个socket
SOCKET _sock = socket(AF_INET, SOCK_STREAM, 0);
bool flag = _sock == INVALID_SOCKET;
func(flag, "socket");
// 创建连接到服务器端的地址
sockaddr_in _sin = {};
_sin.sin_family = AF_INET;
_sin.sin_port = htons(4567);
_sin.sin_addr.S_un.S_addr = inet_addr(ipaddr);
// 连接服务器端
int ret = connect(_sock, (sockaddr *)&_sin, sizeof(sockaddr_in));
flag = ret == SOCKET_ERROR;
func(flag, "connect");
// 接受服务器消息
char recvBuf[256] = {};
int recvLen = recv(_sock, recvBuf, 256, 0);
/*
recv函数
recv函数用于从TCP连接的另一端接收数据。
recv函数的函数原型如下
C
ssize_t recv(int socket, void *buf, size_t len, int flags);
参数说明:
socket:指定接收端套接字描述符。
buf:指向一个存放应用程序要接收数据的缓冲区。
len:指定要接收的数据长度。
flags:指定接收数据的标志。
recv函数的返回值:
成功时,返回实际接收到的数据长度。
失败时,返回SOCKET_ERROR。
*/
if (recvLen > 0)
cout << recvBuf << endl;
// 关闭socket
closesocket(_sock);
WSACleanup();
return 0;
}
3简单演示:
服务器的启动
双击客户端程序
服务器端接受到相应
多点击几次客户端后服务器端