服务端代码

//
//  Tcp_server.cpp
//  Cpp
//
//  Created by JH on 2020/4/5.
//  Copyright © 2020 JH. All rights reserved.
//

#include <stdio.h>
#include <iostream>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>


#define PORT 8111
#define MESSAGE_LEN 1024

int main(int argc,char * argv[]){
    
    int ret = -1;
    int socket_fd ,accept_fd;
    int on =1;
    int backlog = 10;
    
    char in_buff[MESSAGE_LEN] ={0};
    
    struct sockaddr_in localaddr,remoteaddr;
    
    socket_fd = socket(AF_INET, SOCK_STREAM, 0);
    
    if (socket_fd == -1) {
        exit(-1);
    }
    
    ret = setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
    
    if (ret == -1) {
        std:: cout << "set option failed" << std::endl;
        exit(-1);
    }
    localaddr.sin_family = AF_INET;
    localaddr.sin_port = PORT;
    localaddr.sin_addr.s_addr = INADDR_ANY;
    bzero(&(localaddr.sin_zero), 8);
    
    ret = bind(socket_fd, (struct sockaddr *)&localaddr, sizeof(struct sockaddr));
    
    if (ret == -1) {
        std:: cout << "set bind failed" << std::endl;
        exit(-1);
    }
    
    ret = listen(socket_fd, backlog);
    if (ret == -1) {
        std:: cout << "set listen failed" << std::endl;
        exit(-1);
    }
    
    ssize_t size;
    for (; ; ) {
        socklen_t addr_len = sizeof(struct sockaddr);
        accept_fd = accept(socket_fd, (struct sockaddr *)& remoteaddr, &addr_len);
        
        for (; ; ) {
            size = recv(accept_fd, (void *) in_buff, MESSAGE_LEN, 0);
            if (size < 0) {
                break;
            }
            std:: cout <<"recv : " <<in_buff << std::endl;
            send(accept_fd, (void *)in_buff, MESSAGE_LEN, 0);
        }
        close(accept_fd);
    }
//    close(socket_fd);
    
    return 0;
}

客服端代码

//
//  Tcp_Client.cpp
//  Cpp
//
//  Created by JH on 2020/4/7.
//  Copyright © 2020 JH. All rights reserved.
//

#include <stdio.h>
#include <sys/socket.h>
#include  <netinet/in.h>
#include <iostream>
#include <unistd.h>
#include <arpa/inet.h>

#define PORT 8111

#define MESSAEG_LEN 1024

int main(int argc,char *argv[]){
    
    int socket_fd;
    
    int ret;
    
    char sendbuf[MESSAEG_LEN] ={0};
    char recvbuf[MESSAEG_LEN] ={0};
    struct sockaddr_in serveraddr;
    
    ssize_t status;
    
    socket_fd = socket(AF_INET, SOCK_STREAM, 0);
    
    if (socket_fd == -1) {
        std::cout<< "failed to create socekt" << std::endl;
        exit(-1);
    }
    
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_port = PORT;
    serveraddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    
    socklen_t size =sizeof(struct sockaddr);
    
    ret = connect(socket_fd, (struct sockaddr*)&serveraddr, size);
    
    if (ret < 0) {
        std:: cout << "failed to connect server !" <<std::endl;
        exit(-1);
    }
    
    while (1) {
        //清空sendbuf
        memset(sendbuf, 0, MESSAEG_LEN);
        
        gets(sendbuf);
        status = send(socket_fd, sendbuf, strlen(sendbuf), 0);
        if (status < 0) {
            std:: cout << "failed to send message !" <<std::endl;
            exit(-1);
            
        }
        
        if (strcmp(sendbuf, "quit")==0) {
            break;
        }
        
        
        status = recv(socket_fd, recvbuf, MESSAEG_LEN, 0);
        
        recvbuf[status] = '\0';
        
        std:: cout<< "recv:"<<recvbuf <<std::endl;
    }
    
    close(socket_fd);
    
    return 0;
}

上面只是一个简单socket通信,实现了客户端发送一段消息给服务端,服务端再返回给客户端。

但是该代码会存在黏包的现象,当客户端连续发送几次消息,服务端第二个收到消息会和第一次收到的消息发生粘连,主要原因是:socket创建时会分配两个缓冲区,输入缓冲区和输出缓冲区。缓冲区的作用主要是为了避免网络不好的时候,消息接收和发送不完整。有了缓冲区,write()/send() 并不立即向网络中传输数据,而是先将数据写入缓冲区中,再由TCP协议将数据从缓冲区发送到目标机器。

当服务端接收到的第一个消息还在缓冲区时,服务端收到第二个消息,此时就会发生黏包现象。

通常做法是在消息发送前添加一个固定字节大小的DataHeader:包含消息id、消息字节大小size等等,客户端和服务端判首先截取消息头,并根据消息头中的size获取消息大小,接收时只有当缓冲区消息头之后的字节大小等于size时,再从缓冲区读取消息。

Logo

GitCode 天启AI是一款由 GitCode 团队打造的智能助手,基于先进的LLM(大语言模型)与多智能体 Agent 技术构建,致力于为用户提供高效、智能、多模态的创作与开发支持。它不仅支持自然语言对话,还具备处理文件、生成 PPT、撰写分析报告、开发 Web 应用等多项能力,真正做到“一句话,让 Al帮你完成复杂任务”。

更多推荐