c++下基于windows socket的单线程服务器客户端程序

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了c++下基于windows socket的单线程服务器客户端程序相关的知识,希望对你有一定的参考价值。

今天自己用编写了一个简单的c++服务器客户端程序,注释较详细,在此做个笔记。

windows下socket编程的主要流程可概括如下:初始化ws2_32.dll动态库-->创建套接字-->绑定地址信息-->服务器进行监听/客户端连接服务器-->数据交换-->关闭套接字对象。

服务器端:

  1 #include <Winsock2.h>
  2 #include <Ws2tcpip.h>
  3 #include <iostream>
  4 
  5 #pragma comment(lib, "ws2_32.lib") //socket编程需要引用该库
  6 
  7 using std::cerr;
  8 using std::cout;
  9 using std::endl;
 10 
 11 const char DEFAULT_PORT[] = "4000";
 12 const int RECV_BUF_SIZE = 256;
 13 
 14 //服务器
 15 int main() {
 16     WSADATA wsa_data; //WSADATA变量,包含windows socket执行的信息
 17     int i_result = 0; //接收返回值
 18     SOCKET sock_server = INVALID_SOCKET; //创建服务器套接字
 19     SOCKET sock_client = INVALID_SOCKET; //创建客户端套接字
 20     //addrinfo是getaddrinfo()函数用来保存主机地址信息的结构体
 21     addrinfo *result = nullptr; //result是存储地址信息的链表
 22     addrinfo hints;
 23     //初始化winsock动态库(ws2_32.dll),MAKEWORD(2, 2)用于请求使用winsock2.2版本
 24     i_result = WSAStartup(MAKEWORD(2, 2), &wsa_data);
 25     if (i_result != 0) {
 26         cerr << "WSAStartup() function failed: " << i_result << "\n";
 27         system("pause");
 28         return 1;
 29     }
 30     //用0填充内存区域,是ZeroMemory的更安全版本
 31     SecureZeroMemory(&hints, sizeof(addrinfo));
 32     hints.ai_family = AF_INET;
 33     hints.ai_socktype = SOCK_STREAM; //流式套接字用于TCP协议
 34     hints.ai_protocol = IPPROTO_TCP;
 35     hints.ai_flags = AI_PASSIVE; //socket的地址会被用于bind()函数的调用
 36     //确定服务器的地址与端口,将相关信息写入result中
 37     i_result = getaddrinfo(nullptr, DEFAULT_PORT, &hints, &result);
 38     if (i_result != 0) {
 39         cerr << "getaddrinfo() function failed with error: " << WSAGetLastError() << "\n";
 40         WSACleanup();
 41         system("pause");
 42         return 1;
 43     }
 44     //创建服务器套接字
 45     sock_server = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
 46     //套接字创建失败
 47     if (sock_server == INVALID_SOCKET) {
 48         cerr << "socket() function failed with error: " << WSAGetLastError() << "\n";
 49         //将getaddrinfo()函数动态分配的addrinfo中的地址信息释放掉
 50         freeaddrinfo(result);
 51         //释放套接字资源
 52         WSACleanup();
 53         system("pause");
 54         return 1;
 55     }
 56     //将服务器套接字与地址对象绑定,result->ai_addr是结构体的指针
 57     i_result = bind(sock_server, result->ai_addr, static_cast<int>(result->ai_addrlen));
 58     //绑定失败
 59     if (i_result == SOCKET_ERROR) {
 60         cerr << "bind() function failed with error: " << WSAGetLastError() << "\n";
 61         freeaddrinfo(result);
 62         closesocket(sock_server);
 63         WSACleanup();
 64         system("pause");
 65         return 1;
 66     }
 67     freeaddrinfo(result);
 68     cout << "server started successfully..." << endl;
 69     //开始监听
 70     cout << "start listening..." << endl;
 71     i_result = listen(sock_server, SOMAXCONN);
 72     if (i_result == SOCKET_ERROR) {
 73         cerr << "listen() function failed with error: " << WSAGetLastError() << "\n";
 74         closesocket(sock_server);
 75         system("pause");
 76         return 1;
 77     }
 78     //接收客户端请求
 79     sock_client = accept(sock_server, nullptr, nullptr);
 80     if (sock_client == INVALID_SOCKET) {
 81         cerr << "accept() function failed with error: " << WSAGetLastError() << "\n";
 82         closesocket(sock_server);
 83         WSACleanup();
 84         system("pause");
 85         return 1;
 86     }
 87     //接收和发送数据
 88     char recv_buf[RECV_BUF_SIZE];
 89     int send_result = 0;
 90     do {
 91         //不可缺少,若不将内存空间清零会输出乱码,这是因为输送过来的信息未必有256个字节
 92         SecureZeroMemory(recv_buf, RECV_BUF_SIZE);
 93         //标志位一般设置为0
 94         i_result = recv(sock_client, recv_buf, RECV_BUF_SIZE, 0);
 95         if (i_result > 0) {
 96             //exit表示客户端请求断开连接
 97             if (strcmp(recv_buf, "exit") == 0) {
 98                 cout << "client request to close the connection..." << endl;
 99                 break;
100             }
101             //输出接收的字节数
102             cout << "Bytes received: " << i_result << endl;
103             cout << "message received: " << recv_buf << endl;
104             //向客户端发送接收到的数据
105             send_result = send(sock_client, recv_buf, i_result, 0);
106             if (send_result == SOCKET_ERROR) {
107                 cerr << "send() function failed with error: " << WSAGetLastError() << "\n";
108                 closesocket(sock_client);
109                 WSACleanup();
110                 system("pause");
111                 return 1;
112             }
113             cout << "Bytes sent: " << send_result << endl;
114         }
115         //i_result的值为0表示连接已经关闭
116         else if (i_result == 0) {
117             cout << "connection closed..." << endl;
118         }
119         else {
120             cerr << "recv() function failed with error: " << WSAGetLastError() << "\n";
121             closesocket(sock_client);
122             WSACleanup();
123             system("pause");
124             return 1;
125         }
126     } while (i_result > 0); //do...while语句后注意要有分号
127     //shutdown()禁用套接字的接收或发送功能
128     i_result = shutdown(sock_client, SD_SEND);
129     if (i_result == SOCKET_ERROR) {
130         cerr << "shutdown() function failed with error: " << WSAGetLastError() << "\n";
131         closesocket(sock_client);
132         WSACleanup();
133         system("pause");
134         return 1;
135     }
136     //关闭套接字
137     i_result = closesocket(sock_server);
138     WSACleanup();
139     cout << "socket closed..." << endl;
140     system("pause");
141     return 0;
142 }

客户端:

  1 #include <iostream>
  2 #include <WinSock2.h>
  3 #include <Ws2tcpip.h>
  4 
  5 #pragma comment(lib, "ws2_32.lib")
  6 
  7 using std::cin;
  8 using std::cerr;
  9 using std::cout;
 10 using std::endl;
 11 using std::flush;
 12 
 13 const char DEFAULT_PORT[] = "4000";
 14 const int SEND_BUF_SIZE = 256;
 15 
 16 //客户端
 17 int main() {
 18     WSADATA wsa_data; //WSADATA变量,包含windows socket执行的信息
 19     int i_result = 0; //接收返回值
 20     SOCKET sock_client = INVALID_SOCKET;
 21     addrinfo *result = nullptr, hints;
 22     //初始化winsock动态库(ws2_32.dll),MAKEWORD(2, 2)用于请求使用winsock2.2版本
 23     i_result = WSAStartup(MAKEWORD(2, 2), &wsa_data);
 24     if (i_result != 0) {
 25         cerr << "WSAStartup() function failed: " << i_result << "\n";
 26         system("pause");
 27         return 1;
 28     }
 29     SecureZeroMemory(&hints, sizeof(hints));
 30     hints.ai_family = AF_UNSPEC;
 31     hints.ai_socktype = SOCK_STREAM;
 32     hints.ai_protocol = IPPROTO_TCP;
 33     //
 34     i_result = getaddrinfo("127.0.0.1", DEFAULT_PORT, &hints, &result);
 35     if (i_result != 0) {
 36         cerr << "getaddrinfo() function failed with error: " << WSAGetLastError() << "\n";
 37         WSACleanup();
 38         system("pause");
 39         return 1;
 40     }
 41     //创建套接字
 42     sock_client = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
 43     if (sock_client == INVALID_SOCKET) {
 44         cerr << "socket() function failed with error: " << WSAGetLastError() << "\n";
 45         WSACleanup();
 46         system("pause");
 47         return 1;
 48     }
 49     //连接服务器
 50     i_result = connect(sock_client, result->ai_addr, result->ai_addrlen);
 51     if (i_result == SOCKET_ERROR) {
 52         cerr << "connect() function failed with error: " << WSAGetLastError() << "\n";
 53         WSACleanup();
 54         system("pause");
 55         return 1;
 56     }
 57     cout << "connect server successfully..." << endl;
 58     //
 59     freeaddrinfo(result);
 60     //
 61     char send_buf[SEND_BUF_SIZE];
 62     int recv_result = 0;
 63     //SecureZeroMemory(send_buf, SEND_BUF_SIZE);
 64     do {
 65         cout << "enter the message that you want to send: " << flush;
 66         cin >> send_buf;
 67         i_result = send(sock_client, send_buf, static_cast<int>(strlen(send_buf)), 0);
 68         if (i_result == SOCKET_ERROR) {
 69             cerr << "send() function failed with error: " << WSAGetLastError() << "\n";
 70             closesocket(sock_client);
 71             WSACleanup();
 72             system("pause");
 73             return 1;
 74         }
 75         cout << "Bytes sent: " << i_result << endl;
 76         //接收服务器返回的数据
 77         recv_result = recv(sock_client, send_buf, SEND_BUF_SIZE, 0);
 78         if (recv_result > 0) {
 79             cout << "feedback from server: " << send_buf << endl;
 80         }
 81         else if (recv_result == 0) {
 82             cout << "connection closed..." << endl;
 83         }
 84         else {
 85             cerr << "recv() function failed with error: " << WSAGetLastError() << "\n";
 86             closesocket(sock_client);
 87             WSACleanup();
 88             system("pause");
 89             return 1;
 90         }
 91     } while (recv_result > 0);
 92     //
 93     i_result = shutdown(sock_client, SD_SEND);
 94     if (i_result == SOCKET_ERROR) {
 95         cerr << "shutdown() function failed with error: " << WSAGetLastError() << "\n";
 96         closesocket(sock_client);
 97         WSACleanup();
 98         system("pause");
 99         return 1;
100     }
101     closesocket(sock_client);
102     WSACleanup();
103     cout << "socket closed..." << endl;
104     system("pause");
105     return 0;
106 }

 

以上是关于c++下基于windows socket的单线程服务器客户端程序的主要内容,如果未能解决你的问题,请参考以下文章

Redis中的单线程模型

采用 SOCKET 参数的 C++ 线程

C++ 单例模式(懒汉饿汉模式)

C++基于TCP和UDP的socket通信

缓存数据库Redis之四:单线程下的一些事

缓存数据库Redis之四:单线程下的一些事