网络通信—Select模型

了解基础在我的另一篇博客网络通信入门  简单的服务器客户端的链接

这是我画的大概的流程图:

这是服务器的代码,(客户端就是发消息和上边链接中的客户端代码原理一样)剩下的在代码后边解释:

#include <WinSock2.h>                //Socketk编程所需要的头文件
#include <windows.h>
#include <iostream>
#include <tchar.h>
#pragma comment(lib,"Ws2_32.lib")    //需要加载的链接库
using namespace std;

//初始化Socket类库
BOOL InitializeSocket();           

int _tmain(int argc, TCHAR* argv[],TCHAR *envp[])
{
     setlocale(LC_ALL,"Chinese-simplified");        //Unicode下 或者 vs 下中文的显示需要
	
	
	 if (InitializeSocket()==FALSE)      //初始化成功的话继续进行
	 {
		 return 0;
	 } 
	 //创建监听套节字对象
	 SOCKET ListenSocket;
	 ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);   //AF_INET表示 Internet的 IP地址家族
	 															//后两个参数就是使用TCP协议的固定形式
	                                                             //TCP协议安全但是慢   UDP协议快但是不太安全
	
	 if (ListenSocket == INVALID_SOCKET)    //如果是无效的Socket
	 {
		 return 0;
	 }
	  //初始本地网卡
	 sockaddr_in ServerAddress;           //internet下服务器的套接字地址形式
	/*
	 typedef struct sockaddr_in 
	 {
		short   sin_family;           //地址协议家族
		USHORT  sin_port;             //端口号   
		IN_ADDR sin_addr;            //如果 Internet 地址等于 INADDR_ANY,系统会自动使用当前主机配置的所有 IP 地 址,简化了程序设计
		CHAR sin_zero[8];            //预留的参数
	} SOCKADDR_IN, *PSOCKADDR_IN;
	 typedef struct in_addr {
		union {
				struct { UCHAR s_b1,s_b2,s_b3,s_b4; } S_un_b;
				struct { USHORT s_w1,s_w2; } S_un_w;
				ULONG S_addr;
		} S_un;
	} IN_ADDR, *PIN_ADDR, FAR *LPIN_ADDR;*/

	 ServerAddress.sin_family = AF_INET;                 //地址协议家族 这个是internet
	 ServerAddress.sin_port = htons(8888);               //htons函数是将CPU的字节顺序转化为网络字节顺序  
	 ServerAddress.sin_addr.S_un.S_addr = INADDR_ANY;    //解释在上边的结构体成员后边
	 //绑定套节字到本地机器
	 if (bind(ListenSocket, (sockaddr*)&ServerAddress, sizeof(ServerAddress)) == SOCKET_ERROR)
	 {
		 return 0;
	 }
	 //进入监听模式
	 listen(ListenSocket, 5);        //5,代表的是允许消息同时到达的最大数量,可以更改

	 //声明一个套接字集合,就是数组
	 fd_set v1;   
	
	 FD_ZERO(&v1);
	 //将监听套接字放入到集合中,刚开始只加入一个监听套接字,后来会加入下边的通讯套接字
	 FD_SET(ListenSocket, &v1);


    //死循环,不停等待新的消息的到来
	 while (1)
	 {	        
		 fd_set v2 = v1;       //从旧的套接字集合拷贝一份,以进行对于select函数执行之后的两个新旧的套接字之间进行比较
		 int IsOk = select(0, &v2, NULL, NULL, NULL);   //返回值大于零则有信号到来(无论是监听套接字的accept close信号 还是通讯套接字的read write close信号),并被设置标记 
		 if (IsOk > 0)
		 {
			 for (int i = 0; i < (int)v1.fd_count; i++)   //套接字集合中一个一个的遍历  进行比较
			 {
				 if (FD_ISSET(v1.fd_array[i], &v2))      //IS SET就是当前套接字是否被设置了标记,如果没有的话进入下一个for循环
				 {
					 if (v1.fd_array[i] == ListenSocket)   //如果是的话,看是不是监听套接字,不是监听套接字就是通讯套接字,进入else
					 {        
					                              //不懂先往下看就懂了
					 	//是监听套接字
						 //接收connect信号
						 if (v1.fd_count < FD_SETSIZE)   //在最大的连接范围内  FD_SETSIZE为64个
						 {

							 //记录客户端信息
							 sockaddr_in ClientAddress;
							 int ClientAddressLength = sizeof(ClientAddress);

							 //创建新的通信套接字  接受连接,这个套接字是通讯套接字
							 SOCKET ClientSocket = accept(ListenSocket,(SOCKADDR*)&ClientAddress, &ClientAddressLength);

							 //将新的通讯套接字放入到老集合中,下一次进行FOR循环,就会被识别为通讯套接字,进入下边接受通讯内容的else中
							 FD_SET(ClientSocket, &v1);					
							 printf("接收到连接%s\n", ::inet_ntoa(ClientAddress.sin_addr));
							 /*
                             char Ine_Addr(INET_ADDRSTRLEN);
                             PCSTR str=inet_ntop(AF_INET,&ClientAddress.sin_addr,(PSTR)Ine_Addr,sizeof(Ine_Addr));\
                             printf("接到连接%s\n",Ine_Addr);
							 */
						 }
						 else
						 {
							 _tprintf(_T("Too Much Connections!\n"));
							 continue;
						 }
					 }
					 else
					 {
						 //这个else就是当for循环判断为通讯套接字的情况
						 char BufferData[0x1000];  
						 //刚才已经accept了,现在需要接受消息的内容 
						 int ReturnLength = recv(v1.fd_array[i], BufferData, 0x1000, 0);
						 if (ReturnLength > 0)
						 {
							 BufferData[ReturnLength] = '\0';       //OX1000设置的缓冲区会比较大,进行末尾置零以便于显示
							 printf("接收到数据:%s\n", BufferData);
						 }
						 else
						 {
							 //对方关闭套接字
							 closesocket(v1.fd_array[i]);
							 //从集合中移除
							 FD_CLR(v1.fd_array[i], &v1);
						 }
					 }
				 }
			 }   //记住这是两个循环 外边是一个while循环  里边是一个for循环  while循环相当于时间上的读秒,
			     //for循环相当于检索那一秒集合中的套接字受信状态并进行处理
		 }
	 }
    WSACleanup();          //关闭Socket       
	system("pause");
    return 0;
}

BOOL InitializeSocket()
{
	WSADATA  v1 = { 0 };
	//#define MAKEWORD(a, b)      ((WORD)(((BYTE)(((DWORD_PTR)(a)) & 0xff)) | ((WORD)((BYTE)(((DWORD_PTR)(b)) & 0xff))) << 8))
	int IsOk = WSAStartup(MAKEWORD(2, 2), &v1);
	if (IsOk == 0)
	{
		return TRUE;
	}
	return FALSE;
}
发布了37 篇原创文章 · 获赞 12 · 访问量 9262

猜你喜欢

转载自blog.csdn.net/weixin_43265881/article/details/101177826