目录
1. 创建套接字
2. 为套接字绑定地址信息
3. 开始监听
将套接字状态置为LISTEN:
1)告诉服务器,当前socket可以开始处理连接请求。
2)若有客户端发送连接请求过来,服务器会为客户端创建一个新的socket,这个socket负责专门与该客户端进行通信。
4. 获取新建连接
从已完成连接队列中取出一个新建套接字的描述符。这个描述符对应了指定的socket与指定客户端进行通信。
5. 收发数据
6. 关闭套接字
1. 创建套接字
2. 为套接字绑定地址信息(不推荐)
3. 向服务器发起连接请求
客户端的tcp套接字中也会保存完整的五元组。
4. 收发数据
5. 关闭套接字
int socket(int domain, int type, int protocol);
domain:地址域类型:
ipv4: AF_INET;
ipv6:AF_INET6;
type:套接字类型:
SOCK_STREAM:提供字节流传输服务;
protocol:协议类型:
TCP:IPPROTO_TCP;
返回值:
成功,返回一个套接字描述符;失败,返回-1。
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
sockfd:创建套接字返回的描述符;
addr:要绑定的地址信息结构:
ipv4:struct sockaddr_in;
ipv6:struct sockaddr_in6;
addrlen:地址信息长度;
返回值:
成功,返回0;失败,返回-1。
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
sockfd:套接字描述符;
*addr:服务器地址信息;
addrlen:地址信息长度。
返回值:
成功,返回0;失败,返回-1。
int listen(int sockfd, int backlog);
sockfd:监听套接字描述符;
backlog:服务器端同一时间最大并发连接数。
注意:第二个参数限制的是同一时间所能处理的最大连接请求数量,而不是服务器所能建立的总连接数量。
在内核中,存在一个socket连接队列,其最大容量就是backlog+1。如果连接队列已满,则新到的连接请求会被丢弃。
int accept(int listen_sockfd, struct sockaddr *addr, socklen_t *addrlen);
listen_sockfd:监听套接字,决定获取的是哪个监听套接字的新建连接;
addr:地址结构的空间首地址,用于接收新连接的客户端地址信息;
*addrlen:用于指定想要获取的地址长度,以及返回实际的长度。
返回值:
成功,返回新建连接的描述符;失败,返回-1。
因为tcp通信的套接字中保存了完整的五元组,所以收发数据时,不需要再指定和获取对端的地址信息。
接收数据:
ssize_t recv(int sockfd, char *buf, int len, int flag);
sockfd:新建的套接字的描述符;
buf:用于存放接收的数据的空间首地址;
len:想要获取的数据长度;
flag:0-阻塞接收;
返回值:
成功,返回实际获取到的数据长度;出错,返回-1;连接断开,返回0。
发送数据:
ssize_t send(int sockfd, char *data, int len, int flag);
sockfd:新建的套接字的描述符;
data:要发送的数据的空间首地址;
len:要发送的数据长度;
flag:0-默认阻塞发送;
返回值:
成功,返回实际发送成功的数据长度;出错,返回-1。
int close(fd);
fd:套接字描述符。
注:关闭套接字时,监听套接字一般不关闭,不需要与哪个客户端通信,则关闭那个对应的通信套接字即可。
- #include
- #include
- #include
- #include
- #include
- #include
- #include
-
- //封装TCPsocket类
- #define MAX_LISTEN 5
- #define CHECK_RETURN(X) if((X) == false) {return -1;}
-
- class TCPsocket {
- private:
- int _sockfd;
- public:
- TCPsocket () : _sockfd(-1) {}
-
- //1.创建套接字
- bool Socket() {
- _sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
- if (_sockfd < 0) {
- perror("create socket error!");
- return false;
- }
- return true;
- }
-
- //2.为套接字绑定地址信息
- bool Bind(const std::string &ip, uint16_t port) {
- struct sockaddr_in addr;
- addr.sin_family = AF_INET;
- addr.sin_port = htons(port);
- addr.sin_addr.s_addr = inet_addr(ip.c_str());
- socklen_t len = sizeof(struct sockaddr_in);
-
- int ret = bind(_sockfd, (struct sockaddr*)&addr, len);
- if (ret < 0) {
- perror("bind error!");
- return false;
- }
- return true;
- }
-
- //客户端:3.向服务器发起连接请求
- bool Connect(const std::string &ip, uint16_t port) {
- struct sockaddr_in addr;
- addr.sin_family = AF_INET;
- addr.sin_port = htons(port);
- addr.sin_addr.s_addr = inet_addr(ip.c_str());
- socklen_t len = sizeof(struct sockaddr_in);
-
- int ret = connect(_sockfd, (struct sockaddr*)&addr, len);
- if (ret < 0) {
- perror("connect error!");
- return false;
- }
- return true;
- }
-
- //服务端:3.开始监听
- bool Listen(int backlog = MAX_LISTEN) {
- int ret = listen(_sockfd, backlog);
- if (ret < 0) {
- perror("connect error!");
- return false;
- }
- return true;
- }
-
- //服务端:4. 获取新建连接
- bool Accept(TCPsocket *sock, std::string *ip = NULL, uint16_t *port = NULL) {
- struct sockaddr_in addr;
- socklen_t len = sizeof(struct sockaddr_in);
-
- int newfd = accept(_sockfd, (struct sockaddr*)&addr, &len);
- if (newfd < 0) {
- perror("accept error!");
- return false;
- }
- sock -> _sockfd = newfd;
-
- if (ip != NULL) *ip = inet_ntoa(addr.sin_addr);
- if (port != NULL) *port = ntohs(addr.sin_port);
-
- return true;
- }
-
- //4. 接收数据
- bool Recve(std::string *body) {
- char temp[4096] = {0};
-
- int ret = recv(_sockfd, temp, 4095, 0);
- if (ret < 0) {
- perror("recve error!");
- return false;
- }
- else if (ret == 0) {
- std::cout<<"peer shutdown!"<< std::endl;
- return false;
- }
-
- body -> assign(temp, ret);
- return true;
- }
-
- //5.发送数据
- bool Send(const std::string &body) {
- int ret = send(_sockfd, body.c_str(), body.size(), 0);
- if (ret < 0) {
- perror("send error!");
- return false;
- }
- return true;
- }
-
- //6.关闭套接字
- bool Close() {
- if (_sockfd != -1) close(_sockfd);
- return true;
- }
- };
- #include "socket_tcp.hpp"
- #include
-
- int new_worker(TCPsocket& conn_sock) {
- pid_t pid = fork();
- if (pid < 0) {
- perror("fork error!");
- return -1;
- }
- else if(pid == 0){
- while (1) {
- std::string buf;
- bool ret = conn_sock.Recve(&buf);
- if (ret == false) {
- conn_sock.Close();
- break;
- }
- std::cout<<"clinet send: "<
- std::cout<<"server reply: ";
- fflush(stdout);
- std::cin>>buf;
- ret = conn_sock.Send(buf);
- if (ret == false) {
- conn_sock.Close();
- break;
- }
- }
- //出错,则关闭套接字,直接退出当前进程
- conn_sock.Close();
- exit(-1);
- }
- return 0;
- }
-
- int main() {
- signal(SIGCHLD, SIG_IGN);//或略进行退出信息;子进程退出则会直接释放资源
- TCPsocket sock;
- //1.创建套接字
- CHECK_RETURN(sock.Socket());
- //2。绑定地址信息
- CHECK_RETURN(sock.Bind("192.168.247.128", 8888));
- //3.开始监听
- CHECK_RETURN(sock.Listen());
-
- while (1) {
- //4.获取新建连接
- TCPsocket conn_sock;
- std::string client_ip;
- uint16_t client_port;
-
- bool ret = sock.Accept(&conn_sock, &client_ip, &client_port);
- if (ret == false) continue;
- std::cout<<"new connect:"<
" "< -
- //5.使用新建连接与客户端通信
- //创建子进程负责通信
- new_worker(conn_sock);
- //父进程本质并不与客户端进行通信,所以父进程需要关闭该套接字,防止资源耗尽
- //因为父子进程数据独有,所以父进程的关闭不会影响子进程进行通信
- conn_sock.Close();
- }
-
- //6.关闭套接字
- sock.Close();
-
- return 0;
- }
2.2 多线程实现
- #include "socket_tcp.hpp"
- #include
-
- void *entry(void *arg) {
- TCPsocket *conn_sock = (TCPsocket*)arg;
- while (1) {
- std::string buf;
- bool ret = conn_sock->Recve(&buf);
- if (ret == false) {
- conn_sock->Close();
- break;
- }
- std::cout<<"clinet send: "<
- std::cout<<"server reply: ";
- fflush(stdout);
- std::cin>>buf;
- ret = conn_sock->Send(buf);
- if (ret == false) {
- conn_sock->Close();
- break;
- }
-
- }
- conn_sock->Close();
- delete conn_sock;
- }
-
- bool new_worker(TCPsocket*conn_sock) {
- pthread_t tid;
- int ret = pthread_create(&tid, NULL, entry, (void*)conn_sock);
- if (ret != 0) {
- std::cout<<"create thread error!"<
- return false;
- }
- pthread_detach(tid);//设置分离属性,不关心返回值,退出直接释放资源
- return true;
- }
-
- int main() {
- TCPsocket sock;
- //1.创建套接字
- CHECK_RETURN(sock.Socket());
- //2。绑定地址信息
- CHECK_RETURN(sock.Bind("192.168.247.128", 8888));
- //3.开始监听
- CHECK_RETURN(sock.Listen());
-
- while (1) {
- //4.获取新建连接
- TCPsocket *conn_sock = new TCPsocket();
- std::string client_ip;
- uint16_t client_port;
-
- bool ret = sock.Accept(conn_sock, &client_ip, &client_port);
- if (ret == false) continue;
- std::cout<<"new connect:"<
" "< -
- //5.使用新建连接与客户端通信
- new_worker(conn_sock);
- //注意:线程间共享文件描述符表,所以这里不能关闭套接字
- }
-
- //6.关闭套接字
- sock.Close();
-
- return 0;
- }
3. 客户端实现
- #include "socket_tcp.hpp"
-
- int main(int argc, char *argv[]) {
- if (argc != 3) {
- std::cout<<"please add server address!"<
- std::cout<<"usage: ./clinet_tcp ip port"<
- return -1;
- }
-
- std::string server_ip = argv[1];
- uint16_t server_port = std::stoi(argv[2]);
- TCPsocket sock;
-
- //1.创建套接字
- CHECK_RETURN(sock.Socket());
- //2.绑定地址信息(客户端不推荐)
- //3.向服务器发起连接请求
- CHECK_RETURN(sock.Connect(server_ip, server_port));
- //4.与服务器通信
- while (1) {
- std::string buf;
- std::cout<<"client send:";
- fflush(stdout);
- std::cin>>buf;
-
- //发送数据
- bool ret = sock.Send(buf);
- if (ret == false) {
- perror("send error!");
- sock.Close();
- return -1;
- }
-
- //接收数据
- buf.clear();
- ret = sock.Recve(&buf);
- if (ret == false) {
- perror("recve error!");
- sock.Close();
- return -1;
- }
- std::cout<<"server reply:"<< buf << std::endl;
- }
- //5.关闭套接字
- sock.Close();
- return 0;
- }
-
相关阅读:
Go 语言变量
<C++>【入门篇】
Wordpress - Xydown独立下载页面插件
嵌入式Linux裸机开发(三)SDK移植及BSP管理
DJYOS开源往事三:DJYOS源码发布网络实证
Linux 中的 chage 命令及示例
(附源码)ssm基于web技术的医务志愿者管理系统 毕业设计 100910
深度解析:为何在 SwiftUI 视图的 init 初始化器里无法更改 @State 的值?
Java 并发编程解析 | 如何正确理解Java领域中的并发锁,我们应该具体掌握到什么程度?
支持向量机(SVM)----sklearn库的应用
-
原文地址:https://blog.csdn.net/m0_63020222/article/details/127574155