c++tcp服务端客户端
服务端代码
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<iostream>
#include<string>
#include<vector>
using namespace std;
int main() {
// https://zhuanlan.zhihu.com/p/360042334
// 1. 创建一个监听socket
// PF_LOCAL 本地地址; PF_INET: ipv4; PF_INET6: ipv6 -- 同样也有AF_LOCAL ...
// SOCK_STREAM 字节流,对应tcp; SOCK_DGRAM 数据包,对应udp; SOCK_RAW 原始套接字
// protocol 协议, 指定通信协议的,但现在基本废弃。因为协议已经通过前面两个参数指定完成
int socket_fd = socket(PF_INET, SOCK_STREAM, 0);
if (socket_fd == -1) {
cout << "create socket failed!" << endl;
return -1;
}
cout << "create socket success!" << endl;
// 2. 初始化服务器地址
struct sockaddr_in local_addr{};
// ipv4
local_addr.sin_family = AF_INET;
// 指定端口, 注意必须是 htonl(9999), 不能仅指定为 9999
local_addr.sin_port = htonl(9999);
// 通配地址 INADDR_ANY 表示通配地址,
// 如果你只想该服务器程序仅本机上可以访问,那么你 bind 函数中的地址就可以使用127.0.0.1;
// 如果你的服务只想被局域网内部机器访问,bind 函数的地址可以使用192.168.1.104;
// 如果 希望这个服务可以被公网访问,你就可以使用地址0.0.0.0或 INADDR_ANY
local_addr.sin_addr.s_addr = htonl (INADDR_ANY);
// 如果 using namespace std; 并且使用 bind(而不是 ::bind) 会报错 Invalid operands to binary expression ('int' and '__bind<int &, sockaddr *, unsigned long>')
// 因为使用的是 std::bind, 可以改为 ::bind, 避免这个问题 https://blog.csdn.net/YoungHong1992/article/details/122567619
if (-1 == ::bind(socket_fd, (struct sockaddr *) &local_addr, sizeof(local_addr))) {
cout << "bind port failed!" << endl;
return -1;
}
cout << "bind port success!" << endl;
// 3. 启动监听
// 第一个参数 socketdf 为套接字描述符,
// 第二个参数 backlog,在 Linux 中表示已完成 (ESTABLISHED) 且未 accept 的队列大小,(全连接队列的大小)这个参数的大小决定了可以接收的并发数目。
// 这个参数越大,并发数目理论上也会越大。但是参数过大也会占用过多的系统资源
// SOMAXCONN 表示监听队列数默认为128
if (listen(socket_fd, SOMAXCONN) == -1) {
cout << "listen failed!" << endl;
return -1;
}
cout << "listen success!" << endl;
//记录所有客户端连接的容器
std::vector<int> client_fds;
while (true) {
struct sockaddr_in client_addr{};
socklen_t client_addr_len = sizeof(client_addr);
//接受客户端的连接
int client_fd = accept(socket_fd, (struct sockaddr *) &client_addr, &client_addr_len);
if (client_fd != -1) {
// 接收客户端数据缓存
char receive_buf[1024] = {0};
// 从客户端接收数据
long ret = recv(client_fd, receive_buf, 1024, 0);
if (ret != -1) {
cout << "receive client data: " << receive_buf << endl;
// 原样返回, 加一个
char echo_data[] = "echo:";
strcat(echo_data, receive_buf);
ret = send(client_fd, echo_data, strlen(echo_data), 0);
if (ret != strlen(echo_data)) {
cout << "send data error!" << endl;
} else {
cout << "send data success!" << endl;
}
} else {
cout << "receive data failed!" << endl;
}
// 关闭客户端连接
close(client_fd);
} else {
cout << "accept client error: " << client_fd << endl;
break;
}
}
// 关闭服务端监听fd
close(socket_fd);
return 0;
}
客户端代码
#include <gtest/gtest.h>
#include <iostream>
#include <string>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<unistd.h>
#define SERVER_ADDRESS "127.0.0.1"
#define SERVER_PORT 9999
#define SEND_DATA "hello world!"
#define RECEIVE_BUF_SIZE 32
TEST(TcpTest, Client) {
//1. 创建一个socket
int client_fd = socket(AF_INET, SOCK_STREAM, 0);
if (client_fd == -1) {
std::cout << "create client fd error. " << std::endl;
return;
}
std::cout << "create client fd success!" << std::endl;
//2. 连接服务器
struct sockaddr_in server_addr{};
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr(SERVER_ADDRESS);
server_addr.sin_port = htons(SERVER_PORT);
// 第一个参数 sock_fd 是连接套接字
// 第二个、第三个参数 server_addr 和 addr_len 分别代表指向套接字地址结构的指针和该结构的大小。
// 套接字地址结构必须含有服务器的 IP 地址和端口号
if (connect(client_fd, (struct sockaddr *) &server_addr, sizeof(server_addr)) == -1) {
std::cout << "connect socket error" << std::endl;
return;
}
std::cout << "connect socket success!" << std::endl;
//3. 向服务器端发送数据
long ret = send(client_fd, SEND_DATA, strlen(SEND_DATA), 0);
if (ret != strlen(SEND_DATA)) {
std::cout << "send data error." << std::endl;
return;
}
std::cout << "send data successfully, data :" << SEND_DATA << std::endl;
//4. 客户端收数据
char receive_buf[RECEIVE_BUF_SIZE] = {0};
ret = recv(client_fd, receive_buf, RECEIVE_BUF_SIZE, 0);
if (ret > 0) {
std::cout << "receive data successfully, data: " << receive_buf << std::endl;
} else {
std::cout << "receive data error, data: " << receive_buf << std::endl;
}
//5. 关闭socket
close(client_fd);
}
问题
服务端启动后,端口联不上
服务端启动后, telnet 0 9999 连不上: telnet: Unable to connect to remote host 。
检查发现, 服务端代码33行写成了 local_addr.sin_port = 9999;
,改为 local_addr.sin_port = htons(9999);
就可以了。
实际上使用 = 9999 启动的是别的端口, 可以使用 netstat -nlpt | grep 检查 (mac上可以用 lsof -n -P | grep xxx) 检查。
发现是 TCP *:3879 (LISTEN)
。