有没有人能够为我提供一些使用C++中套接字进行客户端和服务器连接的示例样例。
我已经阅读了一些教程,现在想实现它。
如何开始?
有没有人能够为我提供一些使用C++中套接字进行客户端和服务器连接的示例样例。
我已经阅读了一些教程,现在想实现它。
如何开始?
你可以在这里找到一个可工作的客户端-服务器程序:Beej的网络编程指南
C++标准中没有套接字API。POSIX C API具有较高的可移植性(GNU libc文档提供了UDP和TCP客户端和服务器的示例,当我需要快速搭建服务器时通常会参考这些示例),或者您可以使用Boost.ASIO库以获得更加C ++化的体验...
我使用C/C++编写了一个简单的客户端/服务器示例,没有任何额外的功能。您可以参考这个示例构建自己的用例。原始代码附在此处,并且也在github上开源!
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char **argv) {
struct sockaddr_in server_addr; // set server addr and port
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr(argv[1]);
server_addr.sin_port = htons(8000); // server default port
int sock_client;
char send_buf[65536];
memset(send_buf, '\0', sizeof(send_buf));
char *send_content = "I am client";
strcpy(send_buf, send_content);
if ((sock_client = socket(AF_INET,SOCK_STREAM, 0)) < 0) {
return 0;
}
//connect server, return 0 with success, return -1 with error
if (connect(sock_client, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
{
perror("connect");
return 0;
}
char server_ip[INET_ADDRSTRLEN]="";
inet_ntop(AF_INET, &server_addr.sin_addr, server_ip, INET_ADDRSTRLEN);
printf("connected server(%s:%d). \n", server_ip, ntohs(server_addr.sin_port));
//send a message to server
send(sock_client, send_buf, strlen(send_buf), 0);
close(sock_client);
return 0;
}
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h> // for close
#include <string.h>
int main(int argc, char *argv[]) {
int server_sockfd; // server socket fd
struct sockaddr_in server_addr; // server info struct
server_addr.sin_family=AF_INET; // TCP/IP
server_addr.sin_addr.s_addr=INADDR_ANY; // server addr--permit all connection
server_addr.sin_port=htons(8000); // server port
/*创建服务器端套接字--IPv4协议,面向连接通信,TCP协议*/
/* create socket fd with IPv4 and TCP protocal*/
if((server_sockfd=socket(PF_INET,SOCK_STREAM,0))<0) {
perror("socket error");
return 1;
}
/*将套接字绑定到服务器的网络地址上*/
/* bind socket with server addr */
if(bind(server_sockfd,(struct sockaddr *)&server_addr,sizeof(struct sockaddr))<0) {
perror("bind error");
return 1;
}
/*监听连接请求--监听队列长度为20*/
/* listen connection request with a queue length of 20 */
if(listen(server_sockfd,20)<0) {
perror("listen error");
return 1;
}
printf("listen success.\n");
char recv_buf[65536];
memset(recv_buf, '\0', sizeof(recv_buf));
while (1) {
struct sockaddr_in client_addr;
socklen_t length = sizeof(client_addr);
//进程阻塞在accept上,成功返回非负描述字,出错返回-1
// block on accept until positive fd or error
int conn = accept(server_sockfd, (struct sockaddr*)&client_addr,&length);
if(conn<0) {
perror("connect");
return -1;
}
printf("new client accepted.\n");
char client_ip[INET_ADDRSTRLEN] = "";
inet_ntop(AF_INET, &client_addr.sin_addr, client_ip, INET_ADDRSTRLEN);
while(recv(conn, recv_buf, sizeof(recv_buf), 0) > 0 ){
printf("recv %s from client(%s:%d). \n", recv_buf, client_ip, ntohs(client_addr.sin_port));
memset(recv_buf, '\0', strlen(recv_buf));
break;
}
}
printf("closed. \n");
close(server_sockfd);
return 0;
}
虽然标准的C++没有包括标准套接字对象,但是目前在g++中有一个实验性技术规范可用,使用编译器标志-std=gnu++2a。
https://en.cppreference.com/w/cpp/header/experimental/net
这个提议的扩展尚未在C++20中标准化,也不能保证它会被采纳,但它基于Boost ASIO库,你也可以在大多数平台上免费获取该库。
https://www.boost.org/doc/libs/1_76_0/doc/html/boost_asio/tutorial.html
或者你可以自己从头开始创建。
“C++ Web Server from Scratch | Part 1: Creating a Socket Object” 是一个不错的视频教程,它介绍了如何在C++中创建面向对象的UNIX风格套接字。
https://www.youtube.com/watch?v=YwHErWJIh6Y
你可以在Windows上使用winsock进行类似操作,但需要使用winsock.h而不是所有的UNIX风格头文件,并且需要添加一些Windows风格的初始化和清理。Boost为Windows和UNIX风格都提供了支持,所以我建议使用它。
如果你只是想快速进行UNIX风格的网络编程而不使用实验性库,那么这个简单的Socket.h头文件可以帮助你入门:
// Adapted from C code example
// at https://www.geeksforgeeks.org/socket-programming-cc/
#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <string>
#include <stdexcept>
class Socket {
int sock;
public:
Socket(int socket) : sock(socket) {
if (sock<0) throw std::runtime_error("Socket creation error");
}
Socket() : Socket(socket(AF_INET, SOCK_STREAM, 0)) {}
std::string rx() {
char buffer[1024] = {0};
int n = read( sock , buffer, sizeof(buffer));
return std::string(buffer,n);
}
void tx(std::string s) {
send(sock , s.c_str() , s.length() , 0);
}
int getSocket() {
return sock;
}
};
class Connection: public Socket {
public:
Connection(int socket) : Socket(socket) {}
Connection(std::string address,unsigned short port): Socket()
{
struct sockaddr_in serv_addr;
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(port);
// Convert IPv4 and IPv6 addresses from text to binary form
if(inet_pton(
AF_INET,
address.c_str(),
&serv_addr.sin_addr
) <= 0) throw std::runtime_error("Invalid address: Address not supported");
if (connect(
getSocket(),
(struct sockaddr *)&serv_addr,
sizeof(serv_addr)
) < 0) throw std::runtime_error("\nConnection Failed \n");
}
};
class PortListener {
Socket server; // fd is created in default Socket constructor
struct sockaddr_in address;
int opt = 1;
public:
PortListener(unsigned short port) {
// Forcefully attaching socket to the port 8080
if (setsockopt(
server.getSocket(),
SOL_SOCKET,
SO_REUSEADDR | SO_REUSEPORT,
&opt,
sizeof(opt)
)) throw std::runtime_error("setsockopt");
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons( port );
// Forcefully attaching socket to the port 8080
if (bind(
server.getSocket(),
(struct sockaddr *)&address,
sizeof(address)
) < 0) throw std::runtime_error("bind failed");
if (listen(server.getSocket(), 3) < 0) {
throw std::runtime_error("listen");
}
}
Connection waitForConnection() {
int new_socket;
int addrlen = sizeof(struct sockaddr_in);
new_socket = accept(
server.getSocket(),
(struct sockaddr *)&address,
(socklen_t*)&addrlen
);
if (new_socket<0) throw std::runtime_error("accept");
return Connection(new_socket);
}
};
以下是示例服务器代码 server.cpp:
#include "Socket.h"
#include <iostream>
int main(int argc, char const *argv[]) {
using namespace std;
try {
// Normally you'd spawn threads for multiple connections.
Connection conn = PortListener(8080).waitForConnection();
cout << conn.rx() << endl;
conn.tx("Hello from server");
cout << "Hello message sent" << endl;
} catch (runtime_error &e) {
cerr << e.what() << endl;
return EXIT_FAILURE;
}
return 0;
}
#include "Socket.h"
#include <iostream>
int main(int argc, char const *argv[]) {
using namespace std;
try {
Connection conn("127.0.0.1",8080);
conn.tx("Hello from client");
cout << "Hello message sent" << endl;
string s = conn.rx();
cout << s << endl;
} catch (exception &e) {
cerr << e.what() << endl;
return EXIT_FAILURE;
}
return 0;
}
使用g++进行编译和运行:
$ g++ server.cpp -o server
$ g++ client.cpp -o client
$ server &
[1] 2468
$ client
Hello message sent
Hello from client
Hello message sent
Hello from server
[1]+ Done ./server