Windows网络编程-简单c/s模型

223 阅读5分钟

简单c/s模型

windows-简单cs模型.PNG

函数说明

  • 打开网络库
    一切网络函数操作都需要先打开windows的网络库
    网络库需要使用#include <WinSock2.h>库
    打开网络库的函数WSAStartup(WORD version, LPWSADATA lpWSAData);
    version: 定义使用那个版本的网络函数 MAKEAORD(主版本号,副版本号)
#include <WinSock2.h>
#pragma comment(lib, "Ws2_32.lib")
int main(void)
{
    WORD version = MAKEWORD(2,2);  // 使用2.2版本的函数
    WSADATA wsaData;
    WSAStartup(version, &wsaData);
}
  • socket
    socket封装了底层的复杂协议 在代码里面通过socket就可以完成网络通信 不需要考虑传输层 网络层 链路层和网络驱动这些操作
    创建socket函数socket(地址类型,socket类型,协议类型)
    地址类型:
宏定义含义
AF_INETIP地址是ipv4
AF_INET6IP地址是ipv6
AF_BTH蓝牙
AF_IRDA红外

socket类型

宏定义含义
SOCK_STREAM使用TCP传输
SOCK_DGRAM使用UDP传输
SOCK_RDM局域网内多播
SOCK_RAW原始套接字

协议类型

宏定义含义
IPPROTO_TCPTCP协议
IPPROTO_UDPUDP协议
IPPROTO_ICMPICMP协议
int main(void)
{
    SOCKET socket = socket(AF_INET, SOCK_STREAM,IPPROTO_TCP);
    if(INVALID_SOCKET == socket)
    {
        int errno = WSAGetLastError();
        printf("创建socket失败 错误码:%d", errno);
    }
    else
    {
        printf("创建socket成功");
    }
}
  • bind
    给socket绑定IP和端口号
SOCKADDR_IN bindMsg;
bindMsg.sin_family = AF_INET;
bindMsg.sin_port = 22333;
inet_pton(AF_INET, "127.0.0.1", (void*)(&bindMsg.sin_addr.s_addr));
if(SOCKET_ERROR == bind(socket, (SOCKADDR*)(&bindMsg), sizeof(SOCKADDR)))
{
    int errno = WSAGetLastError();
    printf("bind函数执行失败 错误码:%d", errno);
}
  • listen
    只有调用这个函数 客户端才能通过网络访问服务器 服务器开始监听外部对服务器的请求
    服务器的请求有上限 当超过请求上限后 在对服务器的请求会进入到队列中 这个队列就是挂起队列
    当挂起队列也满了 就会拒绝客户端的访问
    listen(服务器监听客户端连接的socket, 挂起队列长度)
SOMAXCONN让系统决定挂起队列长度
if(SOCKET_ERROR == listen(socket, SOMAXCONN))
{
    int errno = WSAGetLastError();
    printf("listen函数执行失败 错误码:%d", errno);
}
  • accept 根据listen监听到的客户端信息 创建一个与客户端通信的socket
SOCKADDR_IN clientMsg; // 存客户端信息
int clientMsgLen = sizeof(SOCKADDR_IN);
SOCKET clientSocket = accept(serverSocket, (SOCKADDR*)(&clientMsg), &clientMsgLen);
if(INVALID_SOCKET == clientSocket)
{
    int errno = WSAGetLastError();
    printf("accept执行失败 错误码:%d", errno);
}
  • connect 客户端向服务器发起连接请求
SOCKADDR_IN serverMsg;
serverMsg.sin_family = AF_INET;
serverMsg.sin_port = 22333;
inet_pton(AF_INET, "127.0.0.1", (void*)(&serverMsg.sin_addr.s_addr));
connect(serverSocket, (SOCKADDR*)(&serverMsg), sizeof());
  • recv 客户端和服务端通信客户端数据是放到协议缓冲区的 recv的本质是将协议缓冲区的数据放到应用缓冲区里
    clientsocket: accept函数创建的与客户端通信的socket
    buf: 代码中缓冲区
    len: 重协议缓冲区复制到代码缓冲区的长度
    参数0的作用: 每一次从协议缓冲区复制到代码中的缓冲区就将协议缓冲区里面len长度的字符删掉 否则会重复读取
char buf[1024] = {0};
recv(clientSocket, buf, sizeof(buf), 0)
  • send
    将代码中缓冲区的数据复制到协议缓冲区中 什么时候发送由系统决定
char sendbuf[1024] = {0};
strcpy(sendbuf, 1024, "欢迎访问服务器");
send(clientSocket, sendbuf, sizeof(sendbuf), 0);
  • 释放资源函数
closesocket(socket);  
WSACleanup();

代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <WinSock2.h>

#pragma comment(lib, "Ws2_32.lib")

int main(void)
{
	WORD version = MAKEWORD(2, 2);
	WSADATA netMsg;
	int isStart = WSAStartup(version, &netMsg);

	int errno;
	int sendCount;
	int recvCount;

	if (isStart != 0)
	{
		errno = WSAGetLastError();
		printf("|# 服务端网络库打开失败. 错误码: %d\n", errno);
		return 0;
	}

	printf("|# 服务端网络库打开成功.\n");

	SOCKET serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (INVALID_SOCKET == serverSocket)
	{
		errno = WSAGetLastError();
		printf("|# 服务端socket创建失败. 错误码: %d\n", errno);
		WSACleanup();
		return 0;
	}

	SOCKADDR_IN bindMsg;
	bindMsg.sin_family = AF_INET;
	bindMsg.sin_port = 22333;
	inet_pton(AF_INET, "127.0.0.1", (void*)(&bindMsg.sin_addr.s_addr));
	if (SOCKET_ERROR == bind(serverSocket, (SOCKADDR*)(&bindMsg), sizeof(bindMsg)))
	{
		printf("|# bind执行失败\n");
		closesocket(serverSocket);
		WSACleanup();
		return 0;
	}
	printf("|# bind执行成功. |# 协议类型:%s |# 端口号:%d |# IP地址: %s\n", "AF_INET", 22333, "127.0.0.1");

	if (SOCKET_ERROR == listen(serverSocket, SOMAXCONN))
	{
		printf("|# listen执行失败. 无法监听端口 端口号:%d\n", 22333);
		closesocket(serverSocket);
		WSACleanup();
		return 0;
	}
	printf("|# listen执行成功 |# 监听端口号:%d\n", 22333);

	SOCKADDR_IN clientMsg;
	int clientMsgLen = sizeof(SOCKADDR_IN);
	SOCKET clientSocket = accept(serverSocket, (SOCKADDR*)(&clientMsg), &clientMsgLen);
	if (INVALID_SOCKET == clientSocket)
	{
		errno = WSAGetLastError();
		printf("|# 服务端创建与客户端通信的socket失败 错误码: %d\n", errno);
		closesocket(serverSocket);
		closesocket(clientSocket);
		WSACleanup();
		return 0;
	}
	printf("|# 服务端创建与客户端通信的socket成功. |# 客户端协议类型:%d |# 客户端端口:%d |# 客户端IP地址:%ld\n",
		clientMsg.sin_family, clientMsg.sin_port, clientMsg.sin_addr.s_addr);

	char sendbuf[1024] = {0};
	char recvbuf[1024] = {0};
	
	char* tmp = "~~欢迎访问服务器~~\n";
	strcpy_s(sendbuf, 1024, tmp);

	sendCount = send(clientSocket, sendbuf, sizeof(sendbuf), 0);
	if (sendCount == SOCKET_ERROR)
	{
		errno = WSAGetLastError();
		printf("|# 向客户端发送信息失败 错误码: %d |# 要发送的数据:%s |# 客户端协议类型:%d |# 客户端端口号:%d |# 客户端IP地址: %ld\n",
			errno, sendbuf, clientMsg.sin_family, clientMsg.sin_port, clientMsg.sin_addr.s_addr);
		closesocket(serverSocket);
		closesocket(clientSocket);
		WSACleanup();
		return 0;
	}
	printf("|# 发送数据成功 数据:%s\n", sendbuf);

	while (1)
	{
		recvCount = recv(clientSocket, recvbuf, sizeof(recvbuf), 0);
		if (0 == recvCount)
		{
			printf("|# 客户端下线\n");
			closesocket(serverSocket);
			closesocket(clientSocket);
			WSACleanup();
			return 0;
		}
		else if (SOCKET_ERROR == recvCount)
		{
			errno = WSAGetLastError();
			printf("|# 接收客户端数据失败. 错误码: %d\n", errno);
			closesocket(serverSocket);
			closesocket(clientSocket);
			WSACleanup();
			return 0;
		}
		else
		{
			printf("|# 客户端数据: %s\n", recvbuf);
		}

		if (strcmp("EXIT", recvbuf) == 0)
		{
			break;
		}

		strcpy_s(sendbuf, 1024, recvbuf);
		send(clientSocket, sendbuf, sizeof(sendbuf), 0);
	}

	printf("|# 客户端退出.\n");
	// 释放资源 关闭socket
	closesocket(serverSocket);
	closesocket(clientSocket);
	// 释放资源 关闭网络库
	WSACleanup();
	return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <WinSock2.h>

#pragma comment(lib, "Ws2_32.lib")

int main(void)
{

	WORD version = MAKEWORD(2, 2);
	WSADATA netMsg;
	int isStart = WSAStartup(version, &netMsg);

	int errno;
	int sendCount;
	int recvCount;

	if (isStart != 0)
	{
		errno = WSAGetLastError();
		printf("|# 客户端网络库打开失败. 错误码: %d\n", errno);
		return 0;
	}

	printf("|# 客户端网络库打开成功.\n");

	SOCKET serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (INVALID_SOCKET == serverSocket)
	{
		errno = WSAGetLastError();
		printf("|# 客户端向服务端请求的socket创建失败. 错误码: %d\n", errno);
		WSACleanup();
		return 0;
	}

	SOCKADDR_IN serverMsg;
	serverMsg.sin_family = AF_INET;
	serverMsg.sin_port = 22333;
	inet_pton(AF_INET, "127.0.0.1", (void*)(&serverMsg.sin_addr.s_addr));
	if (SOCKET_ERROR == connect(serverSocket, (SOCKADDR*)(&serverMsg), sizeof(serverMsg)))
	{
		printf("|# 与服务器建立连接失败.\n");
		closesocket(serverSocket);
		WSACleanup();
		return 0;
	}

	char sendbuf[1024] = {0};
	char recvbuf[1024] = {0};
	
	recvCount = recv(serverSocket, recvbuf, sizeof(recvbuf), 0);
	if (SOCKET_ERROR == recvCount)
	{
		printf("|# 接收服务器数据失败.\n");
		closesocket(serverSocket);
		WSACleanup();
		return 0;
	}
	else
	{
		printf("|# 接收数据: %s\n", recvbuf);
	}

	while (1)
	{
		// scanf_s("%s", sendbuf, 1024);
		// scanf_s会对空格截断 改为gets_s
		gets_s(sendbuf, 1024);
		sendCount = send(serverSocket, sendbuf, sizeof(sendbuf), 0);
		if (strcmp("EXIT", sendbuf) == 0)
		{
			break;
		}
		if (sendCount == SOCKET_ERROR)
		{
			errno = WSAGetLastError();
			printf("|# send执行失败 发送数据失败. 错误码:%d\n", errno);
			closesocket(serverSocket);
			WSACleanup();
			return 0;
		}

		recvCount = recv(serverSocket, recvbuf, sizeof(recvbuf), 0);
		if (recvCount == SOCKET_ERROR)
		{
			errno = WSAGetLastError();
			printf("|# 接收服务端数据失败 错误码: %d\n", errno);
			closesocket(serverSocket);
			WSACleanup();
			return 0;
		}
		printf("|# 服务端回送数据: %s\n", recvbuf);
	}

	printf("|# 结束通信.\n");

	closesocket(serverSocket);
	WSACleanup();
	return 0;
}

结果

捕获.PNG