实验一、基于流式套接字的时间同步服务器设计
0x00 实验内容
要求使用流式套接字编程,实现时间同步服务器和客户端,该服务器能够接收客户端的查询请求,获取本地时间,并将结果发送回客户端,客户端将该时间显示出来。过程描述如下图所示:
0x01 实现过程
Server 端
// server.cpp
#include <stdio.h>
#include <stdlib.h>
#include <winsock2.h>
#include <time.h>
#include <string.h>
#include <iostream>
using namespace std;
#pragma comment(lib, "ws2_32.lib") //引入动态链接库
#define MAXLINE 4096 //接收缓冲区长度
#define LISTENQ 1024 //监听队列长度
#define SEVER_PORT 13131 //时间同步服务器端口号
int main()
{
SOCKET sock_conn;
char buff[MAXLINE]; //缓冲区
time_t ticks; //时间
int res;
//初始化Windows Sockets DLL,协议版本号
WORD w_version = MAKEWORD(2, 2);
WSADATA wsa_data;
res = WSAStartup(w_version, &wsa_data);
if (res != 0)
{
cout << WSAGetLastError() << "WSAStartup Error!" << endl;
return -1;
}
if (LOBYTE(wsa_data.wVersion) != 2 || HIBYTE(wsa_data.wVersion) != 2)
{
cout << WSAGetLastError() << "Version Error!" << endl;
WSACleanup();
return -1;
}
// 设置地址
sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET; // 设置ip协议
server_addr.sin_addr.s_addr = htonl(INADDR_ANY); // INADDR_ANY -- 0.0.0.0
server_addr.sin_port = htons(SEVER_PORT); //设置端口
//创建流式套接字
SOCKET sock_listen = socket(AF_INET, SOCK_STREAM, 0);
if (sock_listen == INVALID_SOCKET)
{
cout << WSAGetLastError() << "Socket Error!" << endl;
WSACleanup();
return -1;
}
//绑定地址
res = bind(sock_listen, (struct sockaddr *)&server_addr, sizeof(server_addr));
if (res == SOCKET_ERROR)
{
cout << WSAGetLastError() << "Bind Addr Error!" << endl;
closesocket(sock_listen);
WSACleanup();
return -1;
}
//设置监听 最长队列长度LISTENQ
res = listen(sock_listen, LISTENQ);
if (res == SOCKET_ERROR)
{
cout << WSAGetLastError() << "Listen Error!" << endl;
closesocket(sock_listen);
WSACleanup();
return -1;
}
// 循环服务器 -- 循环处理用户的连接请求
while (true)
{
//接受客户端连接请求,返回连接套接字sock_conn
sock_conn = accept(sock_listen, nullptr, nullptr);
if (sock_conn == INVALID_SOCKET)
{
cout << WSAGetLastError() << "Accept Error!" << endl;
closesocket(sock_listen);
WSACleanup();
return -1;
}
//获取当前时间
ticks = time(nullptr);
time(&ticks);
memset(buff, 0, sizeof(buff)); //清空缓冲区
strcpy(buff, ctime(&ticks)); // 将数据转换成字符串 准备发送
cout << "Now time is: " << buff << endl;
//发送数据
res = send(sock_conn, buff, (int)strlen(buff), 0);
if (res == SOCKET_ERROR)
{
cout << WSAGetLastError() << "Send Error!" << endl;
closesocket(sock_conn);
WSACleanup();
return -1;
}
cout << "Sent Time Data Successfully." << endl;
//停止连接,不再发送数据
res = shutdown(sock_conn, SD_SEND);
if (res == SOCKET_ERROR)
{
cout << WSAGetLastError() << "Shutdown Error!" << endl;
closesocket(sock_conn);
WSACleanup();
return -1;
}
//关闭套接字
closesocket(sock_conn);
cout << "Server Disconnect" << endl;
}
// 关闭连接
closesocket(sock_listen);
WSACleanup();
return 0;
}
Client端
// client.cpp
#include <stdio.h>
#include <stdlib.h>
#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib") //引入动态链接库
#define MAXLINE 4096 //接收缓冲区长度
#define SEVER_PORT 13131 //时间同步服务器端口号
#define server_ip "127.0.0.1" // 服务器ip地址
int main()
{
int res;
char recv_data[MAXLINE + 1];
// 初始化Windows Sockets DLL,协议版本号
WORD w_version = MAKEWORD(2, 2);
WSADATA wsa_data;
res = WSAStartup(w_version, &wsa_data);
if (res != 0)
{
printf("%d WSAStartup Error!\n", WSAGetLastError());
return -1;
}
if (LOBYTE(wsa_data.wVersion) != 2 || HIBYTE(wsa_data.wVersion) != 2)
{
printf("Version Error!\n");
WSACleanup();
return -1;
}
// 设置地址
sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET; // 设置ip协议
server_addr.sin_addr.S_un.S_addr = inet_addr(server_ip); //设置ip
server_addr.sin_port = htons(SEVER_PORT); //设置端口
//创建流式套接字
SOCKET sock_conn = socket(AF_INET, SOCK_STREAM, 0);
if (sock_conn == INVALID_SOCKET)
{
printf("%d Socket Error!\n", WSAGetLastError());
WSACleanup();
return -1;
}
// 请求连接服务器
res = connect(sock_conn, (sockaddr *)&server_addr, sizeof(server_addr));
if (res == SOCKET_ERROR)
{
printf("%d Connect Error!\n", WSAGetLastError());
closesocket(sock_conn);
WSACleanup();
return -1;
}
// 清空接收缓冲区 准备接收数据
memset(recv_data, 0, sizeof(recv_data));
printf("Recv Data Is:");
do
{
// 接收数据
res = recv(sock_conn, recv_data, MAXLINE, 0);
if (res > 0)
{
printf("%s\n", recv_data);
}
else
{
if (res == 0)
{
printf("Server is closed!\n");
}
else
{
printf("%d Recv Error!\n", WSAGetLastError());
}
}
} while (res > 0);
// 清理环境 断开连接 释放DLL
closesocket(sock_conn);
WSACleanup();
return 0;
}