C++Socket网络通信多路复用

C++Socket网络通信多路复用

1.select

可以在windows上面使用!

服务端:

/*
 * @Description:SOCKET
 * @Author: szq
 * @Github: https://github.com/MrQqqq
 * @Date: 2020-06-26 00:04:39
 * @LastEditors: szq
 * @LastEditTime: 2020-06-26 00:04:39
 * @FilePath: \cpp\src\BinaryTree\client_TCp.cpp
 */
#include
#include
#include
#pragma comment(lib,"ws2_32.lib")
using namespace std;
void initialization() {
     
	//初始化套接字库
	WORD w_req = MAKEWORD(2, 2);//版本号
	WSADATA wsadata;
	int err;
	err = WSAStartup(w_req, &wsadata);
	if (err != 0) {
     
		cout << "初始化套接字库失败!" << endl;
	}
	else {
     
		cout << "初始化套接字库成功!" << endl;
	}
	//检测版本号
	if (LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wHighVersion) != 2) {
     
		cout << "套接字库版本号不符!" << endl;
		WSACleanup();
	}
	else {
     
		cout << "套接字库版本正确!" << endl;
	}
}

enum MyCMD {
     
	LOGIN,
	LOGOUT,
	LOGERROR,
	RESULT
};
class DataHeader {
     
public:
	int dataLength;
	MyCMD cmd;
};

class Login : public DataHeader{
     
public:
	Login() {
     
		dataLength = sizeof(Login);
		cmd = LOGIN;
	}
	char username[30];
	char password[30];
};

class Result : public DataHeader {
     
public:
	int result;
	Result(int a) {
     
		dataLength = sizeof(Result);
		cmd = RESULT;
		result = a;
	}
};
vector<SOCKET> g_clients;
int main() {
     
	//定义长度变量
	int send_len = 0;
	int recv_len = 0;
	int len = 0;

	//定义发送缓冲区和接收缓冲区
	char send_buf[1024];
	char recv_buf[1024];
	//定义服务器套接字,接受请求套接字
	SOCKET s_server;
	

	//服务器地址和客户端地址
	SOCKADDR_IN server_addr;
	
	initialization();

	//填充服务端信息
	server_addr.sin_family = AF_INET;
	server_addr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
	server_addr.sin_port = htons(5010);

	//创建套接字
	s_server = socket(AF_INET, SOCK_STREAM, 0);
	if (bind(s_server, (SOCKADDR*)&server_addr, sizeof(SOCKADDR)) == SOCKET_ERROR) {
     
		cout << "套接字绑定失败!" << endl;
	}
	else {
     
		cout << "套接字绑定成功!" << endl;
	}
	if (listen(s_server, SOMAXCONN) < 0) {
     
		cout << "设置监听状态失败!" << endl;
	}
	else {
     
		cout << "设置监听状态成功!" << endl;
	}
	cout << "服务器正在监听连接,请稍候..." << endl;

	while (1) {
     
		fd_set fdRead;
		fd_set fdWrite;
		fd_set fdExecpt;
		//清空
		FD_ZERO(&fdRead);
		FD_ZERO(&fdWrite);
		FD_ZERO(&fdExecpt);

		FD_SET(s_server, &fdRead);
		FD_SET(s_server, &fdWrite);
		FD_SET(s_server, &fdExecpt);

		for (int i = 0; i < g_clients.size(); i++) {
     
			FD_SET(g_clients[i], &fdRead);
		}
		//nfds是一个整数值,是指fd_set集合中所有描述符的范围,而不是数量,即最大值
		timeval t = {
      0,0 };//设置成非阻塞
		int ret = select(s_server + 1, &fdRead, &fdWrite, &fdExecpt,&t);
		if (ret < 0) {
     
			cout << "select任务结束!" << endl;
			break;
		}
		if (FD_ISSET(s_server, &fdRead)) {
     
			FD_CLR(s_server, &fdRead);
			SOCKET s_accept = INVALID_SOCKET;
			SOCKADDR_IN accept_addr;
			len = sizeof(SOCKADDR);
			s_accept = accept(s_server, (SOCKADDR*)&accept_addr, &len);
			if (s_accept == SOCKET_ERROR) {
     
				cout << "连接失败!" << endl;
				return 0;
			}
			cout << "连接建立,准备接受数据" << endl;

			g_clients.push_back(s_accept);
		}

		for (int i = 0; i < fdRead.fd_count; i++) {
     
			//处理
			recv_len = recv(fdRead.fd_array[i], recv_buf, 1024, 0);
			if (recv_len < 0) {
     
				cout << "接受失败!" << endl;
				auto iter = find(g_clients.begin(), g_clients.end(), fdRead.fd_array[i]);
				g_clients.erase(iter);
				break;
			}
			else {
     
				Login *login = (Login*)recv_buf;
				if (login->cmd == LOGIN) {
     
					if (strcmp(login->username, "szq") == 0) {
     
						Result result(1);
						send_len = send(g_clients[i], (char*)&result, sizeof(Result), 0);
					}

					cout << "登录信息:  用户名=" << login->username << " 密码=" << login->password << endl;

					if (send_len < 0) {
     
						cout << "发送失败!" << endl;
						break;
					}
				}

			}
		}
	}

	//关闭套接字
	closesocket(s_server);
	for (auto skt : g_clients) {
     
		closesocket(skt);
	}
	return 0;
}

客户端:

#include
#include
#include
#pragma comment(lib,"ws2_32.lib")
#pragma warning( disable : 4996)
using namespace std;
void initialization();

enum MyCMD {
     
	LOGIN,
	LOGOUT,
	LOGERROR,
	RESULT
};
class DataHeader {
     
public:
	int dataLength;
	MyCMD cmd;
};

class Login : public DataHeader {
     
public:
	Login() {
     
		dataLength = sizeof(Login);
		cmd = LOGIN;
	}
	char username[30];
	char password[30];
};

class Result : public DataHeader {
     
public:
	int result;
	Result(int a) {
     
		dataLength = sizeof(Result);
		cmd = RESULT;
		result = a;
	}
};
int main() {
     
	//定义长度变量
	int send_len = 0;
	int recv_len = 0;
	//定义发送缓冲区和接受缓冲区
	char send_buf[100];
	char recv_buf[100];
	//定义服务端套接字,接受请求套接字
	SOCKET s_server;
	//服务端地址客户端地址
	SOCKADDR_IN server_addr;
	initialization();
	//填充服务端信息
	server_addr.sin_family = AF_INET;
	server_addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
	server_addr.sin_port = htons(5010);
	//创建套接字
	s_server = socket(AF_INET, SOCK_STREAM, 0);
	if (connect(s_server, (SOCKADDR *)&server_addr, sizeof(SOCKADDR)) == SOCKET_ERROR) {
     
		cout << "服务器连接失败!" << endl;
		WSACleanup();
	}
	else {
     
		cout << "服务器连接成功!" << endl;
	}

	//发送,接收数据
	while (1) {
     
		char s[30];
		cin >> s;
		if (strcmp(s,"login") != 0) {
     
			continue;
		}
		Login login;
		strcpy(login.username, "szq");
		strcpy(login.password, "123");
		send_len = send(s_server, (char*)&login, sizeof(login), 0);
		if (send_len < 0) {
     
			cout << "发送失败!" << endl;
			break;
		}
		recv_len = recv(s_server, recv_buf, 100, 0);
		if (recv_len < 0) {
     
			cout << "接受失败!" << endl;
			break;
		}
		else {
     
			Result *result = (Result*)recv_buf;
			cout << "登录结果为:" << result->result << endl;
		}

	}
	//关闭套接字
	closesocket(s_server);
	//释放DLL资源
	WSACleanup();
	return 0;
}
void initialization() {
     
	//初始化套接字库
	WORD w_req = MAKEWORD(2, 2);//版本号
	WSADATA wsadata;
	int err;
	err = WSAStartup(w_req, &wsadata);
	if (err != 0) {
     
		cout << "初始化套接字库失败!" << endl;
	}
	else {
     
		cout << "初始化套接字库成功!" << endl;
	}
	//检测版本号
	if (LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wHighVersion) != 2) {
     
		cout << "套接字库版本号不符!" << endl;
		WSACleanup();
	}
	else {
     
		cout << "套接字库版本正确!" << endl;
	}
	//填充服务端地址信息

}

版本2:可以支持跨平台的版本。
服务端

/*
 * @Description:SOCKET
 * @Author: szq
 * @Github: https://github.com/MrQqqq
 * @Date: 2020-06-26 00:04:39
 * @LastEditors: szq
 * @LastEditTime: 2020-06-26 00:04:39
 * @FilePath: \cpp\src\BinaryTree\client_TCp.cpp
 */
#include
#include
#ifdef _WIN32
    #include
    #pragma comment(lib,"ws2_32.lib")
#else
    #include
    #include
    #include
    #include 
#include 

#define SOCKET int
#define SOCKADDR_IN sockaddr_in
#define SOCKADDR sockaddr
#define SOCKET_ERROR socket_error
#endif

using namespace std;
void initialization() {
     
#ifdef _WIN32
    //初始化套接字库
    WORD w_req = MAKEWORD(2, 2);//版本号
    WSADATA wsadata;
    int err;
    err = WSAStartup(w_req, &wsadata);
    if (err != 0) {
     
        cout << "初始化套接字库失败!" << endl;
    }
    else {
     
        cout << "初始化套接字库成功!" << endl;
    }
    //检测版本号
    if (LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wHighVersion) != 2) {
     
        cout << "套接字库版本号不符!" << endl;
        WSACleanup();
    }
    else {
     
        cout << "套接字库版本正确!" << endl;
    }
#endif
}


enum MyCMD {
     
    LOGIN,
    LOGOUT,
    LOGERROR,
    RESULT
};
class DataHeader {
     
public:
    int dataLength;
    MyCMD cmd;
};
class Login : public DataHeader{
     
public:
    Login() {
     
        dataLength = sizeof(Login);
        cmd = LOGIN;
    }
    char username[30];
    char password[30];
};

class Result : public DataHeader {
     
public:
    int result;
    Result(int a) {
     
        dataLength = sizeof(Result);
        cmd = RESULT;
        result = a;
    }
};
vector<SOCKET> g_clients;
int main() {
     
    //定义长度变量
    int send_len = 0;
    int recv_len = 0;
    unsigned int len = 0;

    //定义发送缓冲区和接收缓冲区
    char send_buf[1024];
    char recv_buf[1024];
    //定义服务器套接字,接受请求套接字
    SOCKET s_server;


    //服务器地址和客户端地址
    SOCKADDR_IN server_addr;

    initialization();

    //填充服务端信息
    server_addr.sin_family = AF_INET;
#ifdef _WIN32
    server_addr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
#else
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
#endif
    server_addr.sin_port = htons(5010);

    //创建套接字
    s_server = socket(AF_INET, SOCK_STREAM, 0);
    if (bind(s_server, (SOCKADDR*)&server_addr, sizeof(SOCKADDR)) < 0) {
     
        cout << "套接字绑定失败!" << endl;
    }
    else {
     
        cout << "套接字绑定成功!" << endl;
    }
    if (listen(s_server, SOMAXCONN) < 0) {
     
        cout << "设置监听状态失败!" << endl;
    }
    else {
     
        cout << "设置监听状态成功!" << endl;
    }
    cout << "服务器正在监听连接,请稍候..." << endl;

    while (1) {
     
        fd_set fdRead;
        fd_set fdWrite;
        fd_set fdExecpt;
        //清空
        FD_ZERO(&fdRead);
        FD_ZERO(&fdWrite);
        FD_ZERO(&fdExecpt);

        FD_SET(s_server, &fdRead);
        FD_SET(s_server, &fdWrite);
        FD_SET(s_server, &fdExecpt);

        for (int i = 0; i < g_clients.size(); i++) {
     
            FD_SET(g_clients[i], &fdRead);
        }
        //nfds是一个整数值,是指fd_set集合中所有描述符的范围,而不是数量,即最大值
        timeval t = {
      0,0 };//设置成非阻塞
        int ret = select(s_server + 1, &fdRead, &fdWrite, &fdExecpt,&t);
        if (ret < 0) {
     
            cout << "select任务结束!" << endl;
            break;
        }
        if (FD_ISSET(s_server, &fdRead)) {
     
            FD_CLR(s_server, &fdRead);
            SOCKET s_accept = 0;
            SOCKADDR_IN accept_addr;
            len = sizeof(SOCKADDR);
            s_accept = accept(s_server, (SOCKADDR*)&accept_addr, &len);
            if (s_accept < 1) {
     
                cout << "连接失败!" << endl;
                return 0;
            }
            cout << "连接建立,准备接受数据" << endl;

            g_clients.push_back(s_accept);
        }

#ifdef _WIN32
        for (int i = 0; i < fdRead.fd_count; i++) {
     
            //处理
            recv_len = recv(fdRead.fd_array[i], recv_buf, 1024, 0);
            if (recv_len < 0) {
     
                cout << "接受失败!" << endl;
                auto iter = find(g_clients.begin(), g_clients.end(), fdRead.fd_array[i]);
                g_clients.erase(iter);
                break;
            }
            else {
     
                Login *login = (Login*)recv_buf;
                if (login->cmd == LOGIN) {
     
                    if (strcmp(login->username, "szq") == 0) {
     
                        Result result(1);
                        send_len = send(g_clients[i], (char*)&result, sizeof(Result), 0);
                    }

                    cout << "登录信息:  用户名=" << login->username << " 密码=" << login->password << endl;

                    if (send_len < 0) {
     
                        cout << "发送失败!" << endl;
                        break;
                    }
                }

            }
        }
#else
        /*
         * 由于在Linux系统中FD_SET的定义不同,没有相应的fd_count和fd_array,因此这里遍历FD_SET的方式有所不同
         *
         * */
        while(g_clients.size() > 0){
     
            //处理
            recv_len = recv(g_clients[0], recv_buf, 1024, 0);
            if (recv_len < 0) {
     
                cout << "接受失败!" << endl;
                g_clients.erase(g_clients.begin());
            }
            else {
     
                Login *login = (Login*)recv_buf;
                if (login->cmd == LOGIN) {
     
                    if (strcmp(login->username, "szq") == 0) {
     
                        Result result(1);
                        send_len = send(g_clients[0], (char*)&result, sizeof(Result), 0);
                    }

                    cout << "登录信息:  用户名=" << login->username << " 密码=" << login->password << endl;

                    if (send_len < 0) {
     
                        cout << "发送失败!" << endl;
                        break;
                    }
                }

            }
        }
#endif
    }

    //关闭套接字
#ifdef _WIN32
    closesocket(s_server);
#else
    close(s_server);
#endif
    for (auto skt : g_clients) {
     
#ifdef _WIN32
        closesocket(skt);
#else
        close(skt);
#endif
    }
    return 0;
}

客户端:

#include
#include
#ifdef _WIN32
    #include
    #pragma comment(lib,"ws2_32.lib")
#else
    #include
    #include
    #include
    #include 
    #include
#include 

#define SOCKET int
#define SOCKADDR_IN sockaddr_in
#define SOCKADDR sockaddr
#define SOCKET_ERROR 0
#endif

using namespace std;
void initialization() {
     
#ifdef _WIN32
    //初始化套接字库
    WORD w_req = MAKEWORD(2, 2);//版本号
    WSADATA wsadata;
    int err;
    err = WSAStartup(w_req, &wsadata);
    if (err != 0) {
     
        cout << "初始化套接字库失败!" << endl;
    }
    else {
     
        cout << "初始化套接字库成功!" << endl;
    }
    //检测版本号
    if (LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wHighVersion) != 2) {
     
        cout << "套接字库版本号不符!" << endl;
        WSACleanup();
    }
    else {
     
        cout << "套接字库版本正确!" << endl;
    }
#endif
}

enum MyCMD {
     
	LOGIN,
	LOGOUT,
	LOGERROR,
	RESULT
};
class DataHeader {
     
public:
	int dataLength;
	MyCMD cmd;
};

class Login : public DataHeader {
     
public:
	Login() {
     
		dataLength = sizeof(Login);
		cmd = LOGIN;
	}
	char username[30];
	char password[30];
};

class Result : public DataHeader {
     
public:
	int result;
	Result(int a) {
     
		dataLength = sizeof(Result);
		cmd = RESULT;
		result = a;
	}
};
int main() {
     
	//定义长度变量
	int send_len = 0;
	int recv_len = 0;
	//定义发送缓冲区和接受缓冲区
	char send_buf[100];
	char recv_buf[100];
	//定义服务端套接字,接受请求套接字
	SOCKET s_server;
	//服务端地址客户端地址
	SOCKADDR_IN server_addr;
	initialization();
	//填充服务端信息
	server_addr.sin_family = AF_INET;
#ifdef _WIN32
	server_addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
#else
    server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
#endif
	server_addr.sin_port = htons(5010);
	//创建套接字
	s_server = socket(AF_INET, SOCK_STREAM, 0);
	if (connect(s_server, (SOCKADDR *)&server_addr, sizeof(SOCKADDR)) < 0) {
     
		cout << "服务器连接失败!" << endl;
#ifdef _WIN32
	    WSACleanup();
#endif
	}
	else {
     
		cout << "服务器连接成功!" << endl;
	}

	//发送,接收数据
	while (1) {
     
		char s[30];
		cin >> s;
		if (strcmp(s,"login") != 0) {
     
			continue;
		}
		Login login;
		strcpy(login.username, "szq");
		strcpy(login.password, "123");
		send_len = send(s_server, (char*)&login, sizeof(login), 0);
		if (send_len < 0) {
     
			cout << "发送失败!" << endl;
			break;
		}
		recv_len = recv(s_server, recv_buf, 100, 0);
		if (recv_len < 0) {
     
			cout << "接受失败!" << endl;
			break;
		}
		else {
     
			Result *result = (Result*)recv_buf;
			cout << "登录结果为:" << result->result << endl;
		}

	}
	//关闭套接字
#ifdef _WIN32
	closesocket(s_server);
#else
    close(s_server);
#endif
	//释放DLL资源
#ifdef _WIN32
	WSACleanup();
#endif
	return 0;
}

你可能感兴趣的:(C++,C++,socket,多路复用,网络通信,多个客户端连接)