Socket基础二:基于流式套接字的网络程序(时间同步服务器设计)
作者:刘磊 2020.4.26 参考书目:《Windows网络编程》刘琰等著
一、流式套接字
流式套接字是基于可靠的数据流传输服务,特点是面向连接(一对一传输),可靠,流式套接字编程与TCP协议原理关系密切。适合大数据量的数据传输应用,可靠性要求高的传输应用。
二、TCP – 传输控制协议
TCP是一个面向连接的传输层协议,提供高可靠性字节流传输服务,主要用于一次传输要交换大量报文的情形。为了保证传输可靠,TCP增加了许多开销:确认,流量控制,计时器以及连接管理等。
建立连接的三次握手:
第一次,客户端发信息给服务器,服务器收到了,确认了客户端发送正常。
第二次,服务器发信息给客户端,客户端收到了,确认了服务器接收和发送都正常。
第三次,客户端发信息给服务器,服务器周到了,确认了客户端接收正常。
关闭连接的四次挥手:
第一次,客户端没有信息要发了,便会告诉服务器我要关闭了。
第二次,服务器发送一个信号给客户端,告诉客户端,我知道你要关闭了。
第三次,服务器自己也没有信息要发送,便会告诉客户端我要关闭了。 第四次,客户端发送一个信号给服务器,告诉服务器,我知道你要关闭了。
三、流式套接字通信过程
1、服务器基本通信过程:
1)Windows Sockets DLL初始化,协商版本号。
2)创建套接字,指定使用TCP通信。
3)指定本地地址和通信端口。
4)等待客户端的连接请求。
5)进行数据传输。
6)关闭套接字。
7)结束Windows Sockets DLL的使用,释放资源
2、客户端基本通信过程:
1)Windows Sockets DLL初始化,协商版本号。
2)创建套接字,指定使用TCP通信。
3)指定服务器地址和通信端口。
4)向服务器发起连接请求。
5)进行数据传输。
6)关闭套接字。
7)结束Windows Sockets DLL的使用,释放资源。
四、基于流式套接字的时间同步服务器设计
1、服务器编程步骤
1)引用头文件。
#include <time.h> #include <winsock.h> #include <stdio.h> #pragma comment(lib,"ws2_32.lib"); #define MAXLINE 4096 //接收缓冲区长度 #define LISTENQ 1024 //监听队列长度 #define SERVER_PORT 13 //时间同步服务器端口号
2)创建流式套接字。
if ((ListenSocket = socket(AF_INET, SOCK_STREAM, 0)) < 0) { printf("socket函数调用失败,错误号:%d\n",WSAGetLastError()); WSACleanup(); return 1; } memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1 "); servaddr.sin_port = htons(SERVER_PORT);
3)绑定服务器的时间服务端口到已创建的套接字。
iResult = bind(ListenSocket, (struct sockaddr *) & servaddr, sizeof(servaddr)); if (iResult == SOCKET_ERROR) { printf("bind 函数调用错误,错误号:%d\n", WSAGetLastError()); closesocket(ListenSocket); WSACleanup(); return 1; }
4)把套接字变换成监听套接字。
iResult = listen(ListenSocket, LISTENQ); if (iResult == SOCKET_ERROR) { printf("listen 函数调用错误,错误号:%d\n", WSAGetLastError()); closesocket(ListenSocket); WSACleanup(); return 1; }
5)接收客户连接,获取本地时间,发送应答。
//接收客户端连接请求,返回连接套接字ClientSocket ClientSocket = accept(ListenSocket, NULL, NULL); if (ClientSocket == INVALID_SOCKET) { printf("accept 函数调用错误,错误号:%d\n", WSAGetLastError()); closesocket(ListenSocket); WSACleanup(); return 1; } //获取当前时间 ticks = time(NULL); memset(buff, 0, sizeof(buff)); sprintf(buff, "%.24s\r\n", ctime(&ticks)); printf("获取当前时间:%s\n",buff); //发送时间 iSendResult = send(ClientSocket, buff, strlen(buff), 0); if (iSendResult == SOCKET_ERROR) { printf("send 函数调用错误,错误号:%d\n", WSAGetLastError()); closesocket(ListenSocket); WSACleanup(); return 1; } printf("向客户端发送时间成功\n");
6)主动中止连接。
iResult = shutdown(ClientSocket, SD_SEND); if (iResult == SOCKET_ERROR) { printf("shutdown 函数调用错误,错误号:%d\n", WSAGetLastError()); closesocket(ListenSocket); WSACleanup(); return 1; }
7)等待其他客户端的连接请求,回到步骤5。
while(1);
8)关闭套接字,释放资源,程序终止。
closesocket(ListenSocket); WSACleanup(); return 0;
2、客户端编程步骤
1)引用头文件。
#include <winsock.h> #include <stdio.h> #pragma comment(lib,"ws2_32.lib"); #define MAXLINE 4096 //接收缓冲区长度 #define SERVER_PORT 13 //时间同步服务器端口号
2)创建流式套接字。
if ((ConnectSocket = socket(AF_INET, SOCK_STREAM, 0)) < 0) { printf("socket函数调用失败,错误号:%d\n", WSAGetLastError()); WSACleanup(); return 1; }
3)绑定服务器IP地址和端口
memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); servaddr.sin_port = htons(SERVER_PORT);
4)与服务器建立连接。
iResult = connect(ConnectSocket, (sockaddr *)&servaddr, sizeof(servaddr)); if (iResult == SOCKET_ERROR) { printf("connect 函数调用错误,错误号:%d\n", WSAGetLastError()); closesocket(ConnectSocket); WSACleanup(); return 1; }
5)循环接收服务器返回的应答,并显示获得时间。
memset(&recvline, 0, sizeof(recvline)); printf("当前时间是:"); do { iResult = recv(ConnectSocket, recvline, MAXLINE, 0); if (iResult > 0) printf("%s",recvline); else { if (iResult == 0) printf("对方连接关闭,退出\n"); else printf("recv 函数调用错误,错误号:%d\n", WSAGetLastError()); } } while (iResult > 0);
6)如果接收到服务器关闭或者网络出错的信号,则关闭套接字,释放资源,终止程序。
closesocket(ConnectSocket); WSACleanup(); return 0;
3、server.c
#include <time.h> #include <winsock.h> #include <stdio.h> #pragma comment(lib,"ws2_32.lib"); #define MAXLINE 4096 //接收缓冲区长度 #define LISTENQ 1024 //监听队列长度 #define SERVER_PORT 13 //时间同步服务器端口号 int main(int argc, char *argv[]) { SOCKET ListenSocket = INVALID_SOCKET, ClientSocket = INVALID_SOCKET; int iResult; struct sockaddr_in servaddr; char buff[MAXLINE]; time_t ticks; int iSendResult; //初始化Windows Sockets DLL;协商版本号 WORD wVersionRequested; WSADATA wsaData; //使用MAKEWORD(lowbyte, highbyte) 宏,在windef.h中声明 wVersionRequested = MAKEWORD(2, 2); iResult = WSAStartup(wVersionRequested, &wsaData); if (iResult != 0) { printf("WSAStartup函数调用错误,错误号:%d\n",WSAGetLastError()); return 1; } else { printf("初始化成功!\n"); } //创建流式套接字 if ((ListenSocket = socket(AF_INET, SOCK_STREAM, 0)) < 0) { printf("socket函数调用失败,错误号:%d\n",WSAGetLastError()); WSACleanup(); return 1; } memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1 "); servaddr.sin_port = htons(SERVER_PORT); //绑定服务器地址 iResult = bind(ListenSocket, (struct sockaddr *) & servaddr, sizeof(servaddr)); if (iResult == SOCKET_ERROR) { printf("bind 函数调用错误,错误号:%d\n", WSAGetLastError()); closesocket(ListenSocket); WSACleanup(); return 1; } //设置服务器位监听状态,监听队列长度为LISTENQ iResult = listen(ListenSocket, LISTENQ); if (iResult == SOCKET_ERROR) { printf("listen 函数调用错误,错误号:%d\n", WSAGetLastError()); closesocket(ListenSocket); WSACleanup(); return 1; } while (1) { //接收客户端连接请求,返回连接套接字ClientSocket ClientSocket = accept(ListenSocket, NULL, NULL); if (ClientSocket == INVALID_SOCKET) { printf("accept 函数调用错误,错误号:%d\n", WSAGetLastError()); closesocket(ListenSocket); WSACleanup(); return 1; } //获取当前时间 ticks = time(NULL); memset(buff, 0, sizeof(buff)); sprintf(buff, "%.24s\r\n", ctime(&ticks)); printf("获取当前时间:%s\n",buff); //发送时间 iSendResult = send(ClientSocket, buff, strlen(buff), 0); if (iSendResult == SOCKET_ERROR) { printf("send 函数调用错误,错误号:%d\n", WSAGetLastError()); closesocket(ListenSocket); WSACleanup(); return 1; } printf("向客户端发送时间成功\n"); //停止连接,不再发送数据 iResult = shutdown(ClientSocket, SD_SEND); if (iResult == SOCKET_ERROR) { printf("shutdown 函数调用错误,错误号:%d\n", WSAGetLastError()); closesocket(ListenSocket); WSACleanup(); return 1; } //关闭套接字 closesocket(ClientSocket); printf("主动关闭连接\n"); } closesocket(ListenSocket); WSACleanup(); return 0;}
4、client.c
#include <winsock.h> #include <stdio.h> #pragma comment(lib,"ws2_32.lib"); #define MAXLINE 4096 //接收缓冲区长度 #define SERVER_PORT 13 //时间同步服务器端口号 int main(int argc, char *argv[]) { SOCKET ConnectSocket = INVALID_SOCKET; int iResult; struct sockaddr_in servaddr; char recvline[MAXLINE + 1]; //初始化Windows Sockets DLL;协商版本号 WORD wVersionRequested; WSADATA wsaData; //使用MAKEWORD(lowbyte, highbyte) 宏,在windef.h中声明 wVersionRequested = MAKEWORD(2, 2); iResult = WSAStartup(wVersionRequested, &wsaData); if (iResult != 0) { printf("WSAStartup函数调用错误,错误号:%d\n", WSAGetLastError()); return 1; } else { printf("初始化成功!\n"); } //创建流式套接字 if ((ConnectSocket = socket(AF_INET, SOCK_STREAM, 0)) < 0) { printf("socket函数调用失败,错误号:%d\n", WSAGetLastError()); WSACleanup(); return 1; } //服务器地址赋值 memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");; servaddr.sin_port = htons(SERVER_PORT); //向服务器请求连接 iResult = connect(ConnectSocket, (sockaddr *)&servaddr, sizeof(servaddr)); if (iResult == SOCKET_ERROR) { printf("connect 函数调用错误,错误号:%d\n", WSAGetLastError()); closesocket(ConnectSocket); WSACleanup(); return 1; } //持续接收数据,直到服务器关闭连接 memset(&recvline, 0, sizeof(recvline)); printf("当前时间是:"); do { iResult = recv(ConnectSocket, recvline, MAXLINE, 0); if (iResult > 0) printf("%s",recvline); else { if (iResult == 0) printf("对方连接关闭,退出\n"); else printf("recv 函数调用错误,错误号:%d\n", WSAGetLastError()); } } while (iResult > 0); closesocket(ConnectSocket); WSACleanup(); return 0;}
5、结果图
- Socket基础四:基于流式套接字的网络程序(并发服务器设计)
- Socket基础三:基于流式套接字的网络程序(服务器回射程序设计)
- linux基础编程 套接字socket 完整的服务器端多线程socket程序
- 基于Socket(套接字)的低层次Java网络编程
- 基于SOCKET套接字的网络编程
- 基于Udp的Socket网络编程聊天程序
- 基于网络环境的程序设计综合实验报告
- Python 网络编程之UDP发送接收数据功能示例【基于socket套接字】
- linux基础编程 套接字socket 完整的服务器端多线程socket程序【转】
- [精通WindowsSocket网络开发-基于VC++实现]第三章——WindowsSockets基础—TCP,UDP程序 .
- 第19章 网络通信----TCP程序设计基础
- [精通WindowsSocket网络开发-基于VC++实现]第三章——WindowsSockets基础—TCP,UDP程序
- [精通WindowsSocket网络开发-基于VC++实现]第三章——WindowsSockets基础—TCP,UDP程序
- [网络编程]Socket_流式套接字编程
- 基于TCP协议的网络摄像头程序的设计与实现
- 基于TCP协议的网络程序(基础学习)
- 基于TCP协议的网络程序(基础学习)
- 基于TCP协议的网络程序(基础学习)
- 我的IM - 基础篇[3] - 基于UDP通讯的IM设计[基于XML格式的网络通讯协议 以及 包解析器和包处理器的基础概念]
- 超基础的网络编程02:基于TCP的Socket通信