网络编程02 c/s模型

c/s模型,可以实现最简单的客户端与服务器之间的通讯,虽然这个模型无法实际使用,但是可以学习函数的调用及使用

下面代码可以实现客户端与服务器的一问一答的通讯

服务器

/*windows socket 第二版本的网络库*/
#include <WinSock2.h>
/*添加库文件  Windows socket 库就是编译好的二进制代码,使用时不需要再次编译*/
#pragma comment(lib,"ws2_32.lib") 

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

int main(void)
{
	WORD wdVersion = MAKEWORD(2, 2);
	WSADATA wdSocMsg;
	/*
	* WSAStartup()
	* 启动网络库,Windows socket Asynchronous异步 
	* 参数1 网络库版本 参数2 系统返回的库信息
	* 系统返回的库信息:1、我们要使用的版本 2、系统能提供给我们的最高版本 3、4、5、2.0版本后不再使用
	*/
	int nRes = WSAStartup(wdVersion,&wdSocMsg);
	if (0 != nRes)
	{
		switch (nRes)
		{
		case WSASYSNOTREADY:
			printf("重启电脑试试,或者检查网络库");
			break;
		case WSAVERNOTSUPPORTED:
			printf("请更新网络库");
			break;
		case WSAEINPROGRESS:
			printf("请重新打开软件");
			break;
		case WSAEPROCLIM:
			printf("请关闭不必要的资源,为当前程序腾出更多控件");
			break;
		case WSAEFAULT:
			printf("第二个参数输入错误");
			break;
		}
		return 0;
	}
		
		/*检验版本*/
		if (2 != HIBYTE(wdSocMsg.wVersion) || 2 != LOBYTE(wdSocMsg.wVersion))
		{
			//版本不对
			/*关闭网络库*/
			WSACleanup();
			return 0;
		}
			

		/*
		* socket()
		* 本质就是一个uint,这个数是唯一的
		* 参数1 地址的类型 参数2 套接字的类型 参数3 协议的类型
		* 参数1:AF_INET_2 ipv4 192.168.xxx.xxx/AF_BTH_23 ipv6 /AF_BTH_32 蓝牙 6B:2D:BC:A9:8C:12
		* 参数2:SOCK_STREAM 1一种套接字类型,提供带有OOB数据传输机制的顺序,可靠,双向,顺序,使用的传输协议为TCP(邮件)
		* 参数2:SOCK_DGRAM 2 UDP 不可靠的,无顺序,效率高, (电影)
		* 参数3:IPPROTO_TCP  IPPROTO_UDP IPPROTO_ICMP  0的话系统默认选
		*/
		SOCKET socketServer = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);

		
		if (INVALID_SOCKET == socketServer)
		{
			/*获取socket错误码*/
			int errCode = WSAGetLastError();

			/*失败时,清理网路库*/
			WSACleanup();
			return 0;
		}

		/*
		* bind()
		* 给socket绑定端口号与具体地址  地址如192.168.x.x 端口号找到对应的软件,
		* 如QQ,浏览器等,都对应自己的端口号,同一个软件可能对应多个端口号,用于不同功能,每种通讯的端口号是唯一的
		* 参数1 socket
		* 参数2 结构体 sockaddr
		* 参数3 结构体大小
		*/
		struct sockaddr_in si;
		si.sin_family = AF_INET; //地址类型
		/*端口号,本质就是一个整数,0—65535,0-1023是系统保留占用端口号,通过cmd netstat -ano查看系统已使用的端口号 ,通过netstat -ao|findstr "12345"来查看12345端口有没有被占用*/
		si.sin_port = htons(12345); 
		/*IP地址 如127.0.0.1本地回传地址,用于网路测试,*/
		si.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");

		int bres = bind(socketServer,(const struct sockaddr *)&si,sizeof(si));

		if (SOCKET_ERROR == bres)
		{
			//获取错误码
			int a = WSAGetLastError();
			/*关闭socket*/
			closesocket(socketServer);
			/*清理网路库*/
			WSACleanup();

		}
		/*
		* listen()
		* 将套接字置于正在监听传入连接的状态,就是调用这个函数后,客户端就可以通过服务器的socket连接服务器,
		* 就是将socket启动,服务器进入可侦听状态
		* 参数1 服务器端的socket
		* 参数2 挂起连接队列的最大长度,如果服务器同时能处理10个连接,对于超出10个的连接放入队列,此参数指示队列的长度
		* WSAAPI前缀,调用约定,决定了函数的编译方式,决定参数的入栈顺序,决定函数的调用时间,给操作系统来看的 
		*/
		int lisState = listen(socketServer,SOMAXCONN);//SOMAXCONN 让系统决定等待队列的长度

		if (lisState == SOCKET_ERROR)
		{
			/*关闭socket*/
			closesocket(socketServer);
			/*清理网路库*/
			WSACleanup();
		}

		/*
		* accept() //有多少客户端连接,就要循环多少次,不能多,否则就会等待
		* 将每个客户端信息创建成socket
		* 参数1 服务器的socket
		* 参数2 结构体
		* 参数3 参数2的大小
		*/
		struct sockaddr_in clientMsg;
		int len = sizeof(clientMsg);
		SOCKET socketClient = accept(socketServer, (struct sockaddr *)&clientMsg, &len);
		//getpeername(socketServer,(struct sockaddr *)&clientMsg, &len)可以随时获取客户端socket信息

		if (INVALID_SOCKET == socketClient)
		{
			/*关闭socket*/
			closesocket(socketServer);
			/*清理网路库*/
			WSACleanup();
			printf("客户端链接失败");
			return 0;
		}
		printf("客户端链接成功");

		while (1)
		{
			/*
			* recv()
			* 如果客户端在线,则会等待其发送数据...
			* 得到指定客户端发来的消息,本质是复制,从系统中的缓冲区(协议自动实现),存入数组中
			* 参数1 客户端的socket
			* 参数2 *buf 客户端消息的存储空间,数组不要超出1500字节,网络一次最大的传输单元就为1500字节
			* 参数3 len 想要读取的字节数 <= buf长度
			* 参数4 flag 数据的读取方式 0是正常逻辑,数据读出后,就会删除缓存区,MSG_PEEK,读出来不删除
			* 参数4 MSG_OOB 带外数据 传输一段数据,再外带一个额外的特殊数据,会降低存储效率
			* 参数4 MSG_WAITALL 直到缓冲区的字节数满足参数3所请求的字节数,才开始读取
			* 返回值 读出的字节的大小
			*
			*/
			char buf[1500] = { 0 };
			int lens = 1024;
			int res = recv(socketClient, buf, lens, 0);
			if (0 == res)
			{
				printf("连接中断");

			}
			else if (SOCKET_ERROR == res)
			{
				//出错了
			}
			else
			{
				printf("%d  %s\n", res, buf);
			}
			/*
			* send()
			* 向目标发送数据
			* 将数据复制到系统的协议发送缓冲区,计算机伺机发送出去
			* 参数1 目标的socket,每个客户端对应唯一的socket
			* 参数2 要发送的数据
			* 参数3 要字节数
			* 参数4 0 正常发送  MSG_OOB 带外  MSG_DONTROUTE 指定数据不受路由限制
			* 返回值 成功写入的字节数
			*/
			
			scanf("%s",buf);
			send(socketClient, buf, strlen(buf), 0);
			//if (SOCKET_ERROR)

		}

		

		//清理网络库
		closesocket(socketServer);
		closesocket(socketClient);
		WSACleanup();
		system("pause");
	
	return 0;
}

客户端

/*windows socket 第二版本的网络库*/
#include <WinSock2.h>
/*添加库文件  Windows socket 库就是编译好的二进制代码,使用时不需要再次编译*/
#pragma comment(lib,"ws2_32.lib") 

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

int main(void)
{
	WORD wdVersion = MAKEWORD(2, 2);
	WSADATA wdSocMsg;
	/*
	* WSAStartup()
	* 启动网络库,Windows socket Asynchronous异步
	* 参数1 网络库版本 参数2 系统返回的库信息
	* 系统返回的库信息:1、我们要使用的版本 2、系统能提供给我们的最高版本 3、4、5、2.0版本后不再使用
	*/
	int nRes = WSAStartup(wdVersion, &wdSocMsg);
	if (0 != nRes)
	{
		switch (nRes)
		{
		case WSASYSNOTREADY:
			printf("重启电脑试试,或者检查网络库");
			break;
		case WSAVERNOTSUPPORTED:
			printf("请更新网络库");
			break;
		case WSAEINPROGRESS:
			printf("请重新打开软件");
			break;
		case WSAEPROCLIM:
			printf("请关闭不必要的资源,为当前程序腾出更多控件");
			break;
		case WSAEFAULT:
			printf("第二个参数输入错误");
			break;
		}
		return 0;
	}

	/*检验版本*/
	if (2 != HIBYTE(wdSocMsg.wVersion) || 2 != LOBYTE(wdSocMsg.wVersion))
	{
		//版本不对
		/*关闭网络库*/
		WSACleanup();
		return 0;
	}


	/*
	* socket() 创建服务器的socket
	* 本质就是一个uint,这个数是唯一的
	* 参数1 地址的类型 参数2 套接字的类型 参数3 协议的类型
	* 参数1:AF_INET_2 ipv4 192.168.xxx.xxx/AF_BTH_23 ipv6 /AF_BTH_32 蓝牙 6B:2D:BC:A9:8C:12
	* 参数2:SOCK_STREAM 1一种套接字类型,提供带有OOB数据传输机制的顺序,可靠,双向,顺序,使用的传输协议为TCP(邮件)
	* 参数2:SOCK_DGRAM 2 UDP 不可靠的,无顺序,效率高, (电影)
	* 参数3:IPPROTO_TCP  IPPROTO_UDP IPPROTO_ICMP  0的话系统默认选
	*/
	SOCKET socketServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);


	if (INVALID_SOCKET == socketServer)
	{
		/*获取socket错误码*/
		int errCode = WSAGetLastError();

		/*失败时,清理网路库*/
		WSACleanup();
		return 0;
	}
	/*
	* WSAAPIconnect() 连接到服务器并把服务器信息与服务器socket绑定到一起
	* 参数1:服务器socket
	* 参数2:服务器IP地址端口号结构体
	* 参数3: 参数2结构体的大小
	* 返回值:0成功
	*/
	struct sockaddr_in serverMsg;
	serverMsg.sin_family = AF_INET;
	serverMsg.sin_port = htons(12345);//服务器的端口号
	serverMsg.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");//服务器的ip

	if (SOCKET_ERROR == connect(socketServer, (struct sockaddr*)&serverMsg, sizeof(serverMsg)))
	{
		/*获取socket错误码*/
		int errCode = WSAGetLastError();

		/*失败时,清理网路库*/
		WSACleanup();
		return 0;
	}

	while (1)
	{
		/*
		* send()
		* 向服务器发送消息
		*
		*/
		char bufout[50] = { 0 };
		scanf("%s", bufout);
		send(socketServer, bufout, strlen(bufout), 0);
		//if (SOCKET_ERROR)


		/*
		* recv() 从服务器收消息
		*
		*/
		char buf[1500] = { 0 };
		int len = 1499;
		int res = recv(socketServer, buf, len, 0);
		if (0 == res)
		{
			printf("连接中断");

		}
		else if (SOCKET_ERROR == res)
		{
			//出错了
		}
		else
		{
			printf("%d  %s\n", res, buf);
		}

	}

	//清理网络库
	closesocket(socketServer);
	WSACleanup();
	system("pause");
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值