实验环境:Dev-C++
1.设计思路
基于流式套接字的时间同步服务器客户端的设计
客户端
1.初始化动态连接库 WSAStartup()
2.设置服务器地址 struct sockaddr_in server_addr;
3.创建套接字 socket()
4.连接服务器 connect()
5.清空缓冲区准备接收数据 meset()
6.while 循环,循环接收数据 recv()
7.关闭套接字 closesocket()
8.释放动态链接库 WSACleanup()
服务器端
1.初始化动态连接库 WSAStartup()
2.设置地址 struct sockaddr_in server_addr;
3.建立监听套接字 sock_listen=socket()
4.监听套接字绑定本地地址 bind()
5.设置监听 listen()
6.while 循环,循环接收客户端请求,返回连接套接字 sock_conn=accept()
7.获取当前时间 time()
8.发送数据 send()
9.关闭连接 shutdown()——>closesocket(sock_conn)
10.WSACleanup()
2.程序
时间程序,循环服务器
//时间服务器 流式套接字
#include <stdio.h>
#include <stdlib.h>
#include <winsock2.h>
#include <time.h>
#include <string.h>
#pragma comment(lib, "ws2_32.lib") //动态链接库
#define MAXLINE 4096//接收缓冲区长度
#define LISTENQ 1024//已完成连接队列以及未完成连接队列之和不可超过 LISTENQ
#define SEVER_PORT 1999//服务器端口号
int main()
{
SOCKET sock_conn;//连接套接字
char buff[MAXLINE];//缓冲区
time_t ticks;//time()函数返回值的数据类型是time_t
int res;//返回值
//初始化动态链接库
WORD w_version =MAKEWORD(2,2);
WSADATA wsa_data;//
res=WSAStartup(w_version,&wsa_data);//成功返回0
if(res!=0)
{
printf("Error:%d\n",WSAGetLastError());
printf("WSAStartup Error");
WSACleanup();
return -1;
}
if(LOBYTE(wsa_data.wVersion)!=2||HIBYTE(wsa_data.wVersion)!=2)//不是需要的版本号
{
printf("Version Error:%d",WSAGetLastError());
WSACleanup();
return -1;
}
//设置地址(转换为网络字节序)
struct sockaddr_in server_addr;//服务器地址结构体 (sockaddr_in IPV4地址结构体)
memset(&server_addr, 0, sizeof(server_addr));//初始化为0
server_addr.sin_family = AF_INET; // 设地址族为ip协议
server_addr.sin_addr.s_addr = INADDR_ANY; // INADDR_ANY =0
server_addr.sin_port = htons(SEVER_PORT); //设置端口
//建立监听套接字
SOCKET sock_listen;
sock_listen=socket(AF_INET, SOCK_STREAM, 0);
if (sock_listen==INVALID_SOCKET)//socket调用失败返回INVALID_SOCKET
{
printf("Socket Error:%d",WSAGetLastError());
WSACleanup();
return -1;
}
//监听套接字绑定本地地址
res=bind(sock_listen,(struct sockaddr*)&server_addr,sizeof(server_addr));
if (res == SOCKET_ERROR)
{
printf("Bind Error:%d",WSAGetLastError());
closesocket(sock_listen);
WSACleanup();
return -1;
}
//设置监听 最长队列长度LISTENQ
res=listen(sock_listen,LISTENQ);
if (res==SOCKET_ERROR)
{
printf("Listen Error:%d",WSAGetLastError());
closesocket(sock_listen);
WSACleanup();
return -1;
}
//循环服务器
while (1)
{
//接受客户端连接请求,返回连接套接字sock_conn
sock_conn=accept(sock_listen,NULL,NULL);
if (sock_conn==INVALID_SOCKET)
{
printf("Accept Error:%d",WSAGetLastError());
closesocket(sock_listen);
WSACleanup();
return -1;
}
//获取当前时间
ticks=time(NULL);
time(&ticks);
memset(buff, 0, sizeof(buff)); //清空缓冲区
strcpy(buff, ctime(&ticks));
// printf("time:%s",buff);???????
//发送数据
res=send(sock_conn,buff,(int)strlen(buff), 0);
if (res==SOCKET_ERROR)
{
printf("Send Error:%d",WSAGetLastError());
closesocket(sock_conn);
WSACleanup();
return -1;
}
puts("Sent Time Data Successfully");
//优雅的关闭连接
res=shutdown(sock_conn, SD_SEND);//关闭写通道
if (res==SOCKET_ERROR)
{
printf("Shutdown Error:%d", WSAGetLastError());
closesocket(sock_conn);
WSACleanup();
return -1;
}
//关闭连接套接字
closesocket(sock_conn);
printf("服务器关闭了连接套接字\n");
}
// 关闭监听连接
closesocket(sock_listen);
puts("服务器关闭了监听套接字");
WSACleanup();
return 0;
}
时间程序,客户端
//时间客户端
#include <stdio.h>
#include <stdlib.h>
#include <winsock2.h>
#include <time.h>
#include <string.h>
#pragma comment(lib, "ws2_32.lib") //动态链接库
#define MAXLINE 4096 //接收缓冲区长度
#define SEVER_PORT 1999 //时间同步服务器端口号
#define server_ip "10.236.239.24" // 服务器ip地址 我主机的IP
int main()
{
int res;//返回值
char recv_data[MAXLINE+1];
//初始化动态链接库
WORD w_version =MAKEWORD(2,2);
WSADATA wsa_data;
res=WSAStartup(w_version,&wsa_data);//成功返回0
if(res!=0)
{
printf("Error:%d\n",WSAGetLastError());
printf("WSAStartup Error");
WSACleanup();
return -1;
}
if(LOBYTE(wsa_data.wVersion)!=2||HIBYTE(wsa_data.wVersion)!=2)//不是需要的版本号
{
printf("Version Error:%d",WSAGetLastError());
WSACleanup();
return -1;
}
// 设置服务器地址
struct 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;
sock_conn=socket(AF_INET, SOCK_STREAM, 0);
if(sock_conn==INVALID_SOCKET)
{
printf("Socket Error:%d\n", WSAGetLastError());
WSACleanup();
return -1;
}
// 连接服务器
res=connect(sock_conn,(struct 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));
do
{
// 接收数据
res=recv(sock_conn,recv_data,MAXLINE,0);
if(res>0)
{
printf("%s\n",recv_data);
}
else
{
if(res==0)
{
printf("服务器连接关闭!\n");
}
else
{
printf("recv error:%d \n", WSAGetLastError());
}
}
} while(res>0);//流式套接字接收到的数据一定大于0
// 清理环境 断开连接 释放DLL
closesocket(sock_conn);
WSACleanup();
return 0;
}
3.用户使用说明(输入 / 输出规定)
时间服务器端以及客户端:
用户都无需输入输出,客户端会自动显示服务器返回的时间,若出错,会显示提示文字并显示WSAGetLastError()返回的错误号。