x3fang_c 2023-05-04 14:03 采纳率: 8.7%
浏览 12

关于windows sock的问题,如何解决?

最近在研究windows sock
dev c++跑起来后
打印了:
Connected to server

Error receiving data: 10057
客户端代码(隐蔽了服务器地址)

#include <Winsock2.h>
#include <iostream>
#include <string>
#include <conio.h>
#pragma comment(lib, "ws2_32.lib") //链接到winsock库
using namespace std;

int main()
{
    // 初始化 Winsock 库
    WSADATA wsaData;
    if (WSAStartup(MAKEWORD(2,2), &wsaData) != NO_ERROR) {
        cerr << "Error initializing socket library" << endl;
        return 1;
    }

    // 创建用于通信的套接字
    SOCKET sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (sockfd == INVALID_SOCKET) {
        cerr << "\nSocket creation failed: "<< WSAGetLastError() << endl;
        WSACleanup();
        return 1;
    }

    // 将套接字设置为非阻塞模式
    unsigned long blocking_mode = 1; // 0=non-blocking, 1=blocking mode
    if (ioctlsocket(sockfd, FIONBIO, &blocking_mode) != NO_ERROR) {
        cerr << "\nFailed to set non-blocking mode" << endl;
        closesocket(sockfd);
        WSACleanup();
        return 1;
    }

    // 获取要连接的主机和端口号,这里以localhost为例
    string host = "43.138.236.72"; //localhost
    int port = 1160;

    // 连接服务器
    sockaddr_in server_addr;
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(port);
    server_addr.sin_addr.s_addr = inet_addr(host.c_str());
    int connect_result = connect(sockfd, (sockaddr *)&server_addr, sizeof(server_addr));

    if (connect_result == SOCKET_ERROR) {
        int err = WSAGetLastError();
        if (err == WSAEWOULDBLOCK || err == WSAEINPROGRESS) {
            // 连接正在进行中,这是正常行为
        } else {
            cerr << "\nFailed to connect: " << err << endl;
            closesocket(sockfd);
            WSACleanup();
            return 1;
        }
    }

    cout << "\nConnected to server" << endl;
    string input;
    while(true) {
        // 检查是否有来自服务器的新消息
        char buffer[1024];
        int recv_len = recv(sockfd, buffer, sizeof(buffer), 0);
        if (recv_len > 0) {
            buffer[recv_len] = '\0'; // 在结尾添加 null 字符以便于打印字符串
            cout <<"\nServer says: " << buffer << endl;
            cout<<input;
        } else {
            int err = WSAGetLastError();
            if (err != WSAEWOULDBLOCK && err != WSAEINPROGRESS) {
                cerr << "\nError receiving data: " << err << endl;
                break;
            }
        }

        // 从标准输入读取信息并向服务器发送
        if(_kbhit())
        {
            char ch;
            ch=_getch(); 
            if(cin.fail()) { // 如果无法读取输入,则表示用户已经通过Ctrl+Z或Ctrl+C退出
                break;
            }
            if(ch) 
            {
                if(ch=='\n')
                {
                    int send_len = send(sockfd, input.c_str(), input.length(), 0);
                    if (send_len == SOCKET_ERROR) 
                    {
                        int err = WSAGetLastError();
                        if (err != WSAEWOULDBLOCK && err != WSAEINPROGRESS) 
                        {
                            cerr << "\nFailed to send data: ";
                        }
                    }
                    input.clear();
                }
                else
                {
                    input=+ch;
                }
            }
        }
        
    }
}

服务端:

#include <iostream>
#include <fstream>
#include <cstdio>
#include <cstring>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <fcntl.h>

using namespace std;

int main() {
    // 创建Socket
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) {
        perror("Failed to create socket");
        return -1;
    }

    // 绑定地址和端口号
    sockaddr_in server_addr;
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;                        // IPv4协议族
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);         // 自动获取IP地址
    server_addr.sin_port = htons(1160);                      // 端口号为12345

    if (bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {//绑定套接字
        perror("Failed to bind socket");
        close(sockfd);
        return -1;
    }

    // 开始监听
    if (listen(sockfd, 2) < 0) {           //监听 二三参分别为创建套接字,允许建立连接队列长度。
        perror("Failed to listen on socket");
        close(sockfd);
        return -1;
    }
  
    int flags = fcntl(sockfd, F_GETFL, 0);
    fcntl(sockfd, F_SETFL, flags|O_NONBLOCK);//设置为非阻塞模式

    // 创建用于accept的变量,接受客户端连接
    int client_sockfd[2] = {0,0};
    sockaddr_in client_addr;
    socklen_t length = sizeof(client_addr);
    int connected_clients = 0;
    cout<<"server ok"<<endl;
    while (connected_clients < 2) {
        fd_set readset;
        FD_ZERO(&readset);
        FD_SET(sockfd, &readset);

        struct timeval tv;
        tv.tv_sec = 0.1; // 超时时间为0.1秒
        tv.tv_usec = 0;

        int ret = select(sockfd + 1, &readset, NULL, NULL, &tv); //在select函数阻塞期间内核会持续监控输入参数中描述符上的读事件是否发生
        if(connected_clients!=0)
        {
            if (ret == -1) {    //错误处理
                perror("Failed to select on socket");
                return -1;
            } else if (ret == 0) {   //超时处理
                printf("Timeout occurred. There are %d clients connected.\n", connected_clients);
                continue;// 回到while循环开始等待新的套接字连接
            }
    
            // 如果有客户端发起了连接请求
            if (FD_ISSET(sockfd, &readset)) {
                int new_sockfd = accept(sockfd, (struct sockaddr *)&client_addr, &length); //接受客户端连接,返回一个新的socket描述符
                if (new_sockfd < 0) {   //错误处理
                    perror("Failed to accept socket");
                } else {
                    printf("Client %d connected.\n", connected_clients+1);
                    client_sockfd[connected_clients] = new_sockfd;//保存客户端套接字描述符
                    connected_clients++;
                    if(connected_clients==1)
                    {
                        continue;
                    }
                }
            }
        }
        
    }

    // 接收和转发消息
    while (true) {
        fd_set readset;
        FD_ZERO(&readset);//清空集合

        for (int i = 0; i < 2; i++) {
            FD_SET(client_sockfd[i], &readset);//将两个客户端的描述符加入监听集合
        }

        struct timeval tv;
        tv.tv_sec = 0.1;     // 超时时间为0.1秒
        tv.tv_usec = 0;

        int ret = select(client_sockfd[1] + 1, &readset, NULL, NULL, &tv); //在select函数阻塞期间内核会持续监控输入参数中描述符上的读事件是否发生
        if (ret == -1) {    //错误处理
            perror("Failed to select on socket");
            break;
        } else if (ret == 0) {   //超时处理
            printf("Timeout occurred. Waiting for new messages...\n");
            continue;
        }

        // 如果有客户端发送了新消息
        for (int i = 0; i < 2; i++) {
            if (FD_ISSET(client_sockfd[i], &readset)) {
                char buffer[1024];
                memset(buffer, 0, sizeof(buffer));
                int len = recv(client_sockfd[i], buffer, sizeof(buffer), 0);
                if (len <= 0) {    //连接关闭
                    printf("Client %d closed.\n", i+1);
                    close(client_sockfd[i]);
                    client_sockfd[i] = 0;
                } else {
                    printf("Received from client %d: %s", i+1, buffer);
                    if (i == 0 && client_sockfd[1] != 0) { //转发给另一个客户端
                        send(client_sockfd[1], buffer, strlen(buffer), 0);
                    } else if (i == 1 && client_sockfd[0] != 0) {
                        send(client_sockfd[0], buffer, strlen(buffer), 0);
                    }
                }
            }
        }
    }

    // 关闭套接字
    for (int i = 0; i < 2; i++) {
        if (client_sockfd[i] != 0) {
            close(client_sockfd[i]);
        }
    }
    close(sockfd);

    return 0;
}

  • 写回答

2条回答 默认 最新

  • 风雨同流 2023-05-04 16:16
    关注

    根据错误码10057,该错误是一个“socket已经连接但不能发送/接收”的错误。这可能是由于套接字设置为非阻塞模式并且正在等待另一个操作完成,例如connect()或accept()。建议将客户端和服务器端套接字都改回阻塞模式,以便避免此问题。可以使用以下代码进行修改:

    // 将套接字设置为阻塞模式
    unsigned long blocking_mode = 0; // 0=blocking mode, 1=non-blocking
    if (ioctlsocket(sockfd, FIONBIO, &blocking_mode) != NO_ERROR) {
    cerr << "\nFailed to set blocking mode" << endl;
    closesocket(sockfd);
    WSACleanup();
    return 1;
    }
    
    类似的,也需要将服务器端套接字设置为阻塞模式,如下所示:
    
    int flags = fcntl(sockfd, F_GETFL, 0);
    fcntl(sockfd, F_SETFL, flags & ~O_NONBLOCK); // 取消非阻塞模式
    
    

    注意,代码中的这种阻塞方式可能会大幅降低性能,因此在实际使用时,应谨慎选择是否要使用阻塞模式或非阻塞模式。

    评论

报告相同问题?

问题事件

  • 创建了问题 5月4日

悬赏问题

  • ¥15 气象网格数据与卫星轨道数据如何匹配
  • ¥100 java ee ssm项目 悬赏,感兴趣直接联系我
  • ¥15 微软账户问题不小心注销了好像
  • ¥15 x264库中预测模式字IPM、运动向量差MVD、量化后的DCT系数的位置
  • ¥15 curl 命令调用正常,程序调用报 java.net.ConnectException: connection refused
  • ¥20 关于web前端如何播放二次加密m3u8视频的问题
  • ¥15 使用百度地图api 位置函数报错?
  • ¥15 metamask如何添加TRON自定义网络
  • ¥66 关于川崎机器人调速问题
  • ¥15 winFrom界面无法打开