Windows網絡編程 I/O複用的多人聊天室

代碼:

https://github.com/ChristmasError/TCP-IP-Network-programming/tree/master/WINDOWS%20IO%E5%A4%8D%E7%94%A8%E5%A4%9A%E4%BA%BA%E8%81%8A%E5%A4%A9%E5%AE%A4%E5%AE%9E%E7%8E%B0

請多開幾個客戶端進行測試。

//server
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#include <winsock2.h> 
#include <stdio.h>
#include <vector>
#include<ws2tcpip.h>//定義socklen_t

using namespace std;


#pragma comment(lib, "WS2_32")  // 鏈接到WS2_32.lib


#define SERVER_IP "127.0.0.1"// 默認服務器端IP地址
#define SERVER_PORT 8888// 服務器端口號


class server
{
public:
	server();
	void init();
	void process();

private:
	int listener;//監聽套接字
	sockaddr_in  serverAddr;//IPV4的地址方式
	vector <int> socksArr;//存放創建的套接字
};
int main()
{
	server ser;
	ser.process();

	return 0;
}


server::server()
{
	listener = 0;
	serverAddr.sin_family = PF_INET;
	serverAddr.sin_port = SERVER_PORT;
	serverAddr.sin_addr.s_addr = inet_addr(SERVER_IP);//將字符串類型轉換uint32_t
}
//初始化函數,創建監聽套接字,綁定端口,並進行監聽
void server::init()
{
	WSADATA   wsaData;                   
	WSAStartup(MAKEWORD(2, 2), &wsaData);

	listener = socket(AF_INET, SOCK_STREAM, 0);//採用ipv4,TCP傳輸
	if (listener == -1) { printf("Error at socket(): %ld\n", WSAGetLastError()); perror("listener failed"); exit(1); }
	else printf("Server is running......\n");

	if (bind(listener, (sockaddr *)&serverAddr, sizeof(serverAddr)) < 0)
	{
		perror("bind error");
		exit(1);
	}
	listen(listener, 5);//listener這個套接字監聽申請的鏈接,最大等待連接隊列爲5,等待accept()
	socksArr.push_back(listener);//將監聽套接字加入套接字數組,數組內首個套接字就是服務器的套接字
}

void server::process()
{

	int mount = 0;
	fd_set fds,fds_copy;
	FD_ZERO(&fds);//將fds清零

	init();
	//下面就是不斷的檢查申請的連接隊列
	printf("Server is waiting......\n");
	while (1)
	{
		mount = socksArr.size();
		//每次循環更新一次fds數組
		for (int i = 0; i<mount; ++i)
		{
			FD_SET(socksArr[i], &fds);
		}

		struct timeval timeout = { 1,0 };//每個Select等待三秒
		switch (select(0, &fds, NULL, NULL, &timeout))
		{
		case -1:     //select error
		{
			perror("select\n");
			printf("Error at socket(): %ld\n", WSAGetLastError());
			printf("%d\n", mount);
			Sleep(1000);
			break;
		}
		case 0:		//等待時間內無客戶端申請連接
		{
			break;
		}
		default:
		{
			//將數組中的每一個套接字都和剩餘的套接字進行比較,從而得到當前的任務
			for (int i = 0; i < mount; ++i)
			{
				//首個套接字就是服務器的套接字,當期存在並收到客戶端連接請求,進行連接並更新套接字數組(將連接的客戶端套接字加進數組中)
				if (i == 0 && FD_ISSET(socksArr[i], &fds))
				{
					struct sockaddr_in client_addr;
					int clntSz = sizeof(struct sockaddr_in);
					//返回一個用戶的套接字
					int clientfd = accept(listener, (struct sockaddr*)&client_addr, &clntSz);
					//添加用戶,服務器上顯示消息,並通知用戶連接成功
					socksArr.push_back(clientfd);
					printf("connect sucessfully\n");
					char ID[1024];
					sprintf(ID, "You ID is: %d and ", clientfd);
					char buf[30] = "welcome joint the chatroom! \n";
					strcat(ID, buf);
					send(clientfd, ID, sizeof(ID) - 1, 0);//減去最後一個'/0'
				}
				if (i != 0 && FD_ISSET(socksArr[i], &fds))
				{
					char buf[1024];
					memset(buf, '\0', sizeof(buf));
					int size = recv(socksArr[i], buf, sizeof(buf) - 1, 0);
					//檢測是否斷線
					if (size == 0 || size == -1)
					{
						printf("client %d leave\n", socksArr[i]);

						closesocket(socksArr[i]);//關閉這個套接字
						FD_CLR(socksArr[i], &fds);//在列表中刪除
						socksArr.erase(socksArr.begin() + i);//在套接字數組中刪除
					}
					//若是沒有掉線
					else
					{
						printf("clint %d says: %s \n", socksArr[i], buf);
						//將此客戶端說的話發送給每一個客戶端
						char client[1024];
						sprintf(client, "client %d:", socksArr[i]);
						strcat(client, buf);
						for (int j = 1; j < mount; j++)
						{
							send(socksArr[j], client, sizeof(client) - 1, 0);
						}
					}
				}
			}
			break;
		}
		}
	}
}
//Client
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include<conio.h>
#include <iostream>
#include <thread>
#include <winsock2.h> 
#include <stdio.h>
#include<ws2tcpip.h>//定義socklen_t

#pragma comment(lib, "WS2_32")  // 鏈接到WS2_32.lib

using namespace std;



#define SERVER_IP "127.0.0.1"// 默認服務器端IP地址
#define SERVER_PORT 8888// 服務器端口號



class client
{
public:
	client();
	void init();
	void process();
private:
	int user; //客戶端套接字(句柄)
	int writing;
	sockaddr_in  serverAddr;
	void sendata();
};
int main()
{
	client user;
	user.process();

	return 0;
}

client::client()
{
	user = 0;
	writing = 0;
	serverAddr.sin_family = PF_INET;
	serverAddr.sin_port = SERVER_PORT;
	serverAddr.sin_addr.s_addr = inet_addr(SERVER_IP);//將字符串類型轉換uint32_t
}

void client::init()
{
	WSADATA   wsaData;                											
	WSAStartup(MAKEWORD(2, 2), &wsaData);

	user = socket(AF_INET, SOCK_STREAM, 0);//採用ipv4,TCP傳輸,成功時返回非負數socket描述符

	if (user <= 0)
	{
		perror("establish client faild");
		printf("Error at socket(): %ld\n", WSAGetLastError());
		exit(1);
	};
	printf("establish succesfully\n");//創建成功,阻塞式的等待服務器連接
	if (connect(user, (const sockaddr *)&serverAddr, sizeof(serverAddr)) < 0)
	{
		perror("connect to server faild");
		printf("Error at socket(): %ld\n", WSAGetLastError());
		exit(1);
	}
	printf("connect IP:%s  Port:%d succesfully\n", SERVER_IP, SERVER_PORT);//創建成功
}

void client::process()
{
	char recvbuf[1024];
	fd_set fdread, fedwrite;
	FD_ZERO(&fdread);//將fdread清零
	FD_ZERO(&fedwrite);//將fedwrite清零

	init();

	while (1)
	{
		FD_SET(user, &fdread);
		if (writing == 0)
			FD_SET(user, &fedwrite);
		struct timeval timeout = { 1,0 };//每個Select等待三秒
		switch (select(0, &fdread, &fedwrite, NULL, &timeout))
		{
		case -1:
		{
			printf("Error at socket(): %ld\n", WSAGetLastError());
			break;
		}
		case 0:		
			break;
		default:
		{
			if (FD_ISSET(user, &fdread))//有待讀事件
			{
				int size = recv(user, recvbuf, sizeof(recvbuf) - 1, 0);
				if (size > 0)
				{
					printf("server : %s\n", recvbuf);
					memset(recvbuf, '\0', sizeof(recvbuf));
				}
				else if (size == 0)
				{
					printf("server is closed\n");
					exit(1);
				}
			}
			if (FD_ISSET(user, &fedwrite))
			{
				FD_ZERO(&fedwrite);//將fedwrite清零
				writing = 1;//表示正在寫入
				thread sendtask(bind(&client::sendata, this));
				sendtask.detach();//將子線程和主進程分離且互相不影響
			}
			break;
		}
	}
	}
}

void client::sendata()
{
	char sendbuf[1024];
	char middle[1024];

	cin.getline(sendbuf, 1024);//讀取一行
	send(user, sendbuf, sizeof(sendbuf) - 1, 0);
	writing = 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章