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<iostream>
#include<winsock.h>
#include<vector>
#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<iostream>
#include<winsock.h>
#include<cstring>
#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<iostream>
#include<vector>
#ifdef _WIN32
    #include<winsock.h>
    #pragma comment(lib,"ws2_32.lib")
#else
    #include<unistd.h>
    #include<sys/types.h>
    #include<sys/socket.h>
    #include <netinet/in.h>
#include <cstring>

#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<iostream>
#include<vector>
#ifdef _WIN32
    #include<winsock.h>
    #pragma comment(lib,"ws2_32.lib")
#else
    #include<unistd.h>
    #include<sys/types.h>
    #include<sys/socket.h>
    #include <netinet/in.h>
    #include<arpa/inet.h>
#include <cstring>

#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;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Socket网络编程是一种在计算机网络中进行数据交互的编程方式。通过使用Socket,我们可以在不同的计算机之间建立网络连接并进行数据传输。 在C语言中,我们可以使用标准库中的socket函数来创建一个Socket。首先,我们需要使用socket函数来创建一个Socket套接字,指定协议和类型。然后,使用bind函数将Socket与一个本地地址绑定,通常是一个IP地址和一个端口号。接着,使用listen函数启动Socket的监听模式,等待其他计算机发起连接请求。 一旦有其他计算机发起连接请求,我们可以使用accept函数来接受连接,并返回一个新的Socket文件描述符,这个新的Socket可以用于与对方进行数据交互。在这之后,我们可以使用send和recv函数来发送和接收数据。 当数据发送完毕或接收完毕后,我们可以使用close函数关闭Socket,释放资源。在进行Socket网络编程时,我们还可以使用select函数来实现多路复用,同时监听多个Socket,提高程序的效率。 在Socket网络编程中,还可以使用一些其他的函数来设置Socket的各种属性,例如设置Socket为非阻塞模式,设置等待时间等。 总之,Socket网络编程提供了一个方便的方式来进行网络通信。通过使用Socket函数及其相关函数,我们可以在C语言中轻松实现网络编程,实现不同计算机之间的数据交互。 ### 回答2: c socket网络编程是一种用C语言编写的网络编程技术,它允许计算机之间通过网络进行通信和数据交换。在C语言中,socket函数库提供了一种方便的方式来创建网络应用程序。 使用C socket网络编程,我们可以通过创建一个套接字(socket)来建立与其他计算机的连接。套接字可以视为两个应用程序之间的通信端口。一个应用程序可以充当服务器(server),等待其他应用程序的连接请求,并接受它们的连接。另一个应用程序可以充当客户端(client),向服务器发送连接请求,并与服务器建立连接。一旦连接建立,我们可以在客户端和服务器之间传递数据。 在进行C socket网络编程时,我们需要使用一些重要的函数,如socket、bind、listen、accept、connect和send/recv等。其中,socket函数用于创建一个套接字,bind函数用于将套接字与本地IP地址和端口绑定,listen函数用于在服务器端开始监听连接请求,accept函数用于接受客户端连接请求,connect函数用于与服务器建立连接,send/recv函数用于在已经建立的连接上发送和接收数据。 C socket网络编程在实际应用中具有广泛的用途。它可以用于开发各种类型的网络应用程序,如网络聊天室、网络游戏、文件传输、远程控制等。通过使用C socket网络编程技术,我们可以轻松地实现不同计算机之间的通信和数据交换,为用户提供更好的网络体验。 ### 回答3: C语言的socket网络编程是指利用socket库函数,通过TCP/IP协议或UDP协议在网络上进行数据通信的一种编程方式。 在C语言中,可以使用socket()函数创建一个套接字,该函数返回一个整数值作为套接字的标识符。创建套接字后,可以使用bind()函数将套接字与本地的IP地址和端口号绑定起来。 对于服务器端程序,可以使用listen()函数设置套接字为监听状态,然后通过accept()函数接受客户端连接请求。 对于客户端程序,可以使用connect()函数连接到服务器端的套接字。连接成功后,客户端可以使用send()函数发送数据给服务器端,也可以使用recv()函数接收服务器端发送过来的数据。 在网络编程中,需要注意错误处理。例如,当创建套接字或者连接失败时,需要使用perror()函数输出错误信息,并且使用close()函数关闭套接字。此外,还需要使用htons()和htonl()函数将主机字节序转换为网络字节序,以及使用ntohs()和ntohl()函数将网络字节序转换为主机字节序。 socket网络编程还可以使用多线程或多进程实现并发处理,可以同时处理多个连接请求,提高服务器的并发性能。 总之,C语言的socket网络编程是一种强大的工具,用于在网络上进行数据通信。它可以使程序与程序之间实现数据的传输和通信,方便地实现分布式系统、网络游戏、即时通信等应用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值