【问题标题】:C Socket recv()ing weird charactersC Socket recv() 处理奇怪的字符
【发布时间】:2017-04-02 20:58:52
【问题描述】:

所以我主要坚持使用带有套接字的 MAC/Linux,并且一切正常,所以我尝试将我的 Windows 套接字作为基础,也许我这样做是错误的,这就是整个问题,但问题是我现在正在尝试修复的是控制台在服务器发送的实际字符串之后打印随机字符。 服务器.c

#include <stdio.h>
#include <winsock.h>
#include <stdlib.h>
#include "stdafx.h"
#include <conio.h>
#include <io.h>
#define bzero(b,len) (memset((b), '\0', (len)), (void) 0)  
#pragma comment(lib,"ws2_32.lib") //Winsock Library

char message[4040];
int main()
{
    WSADATA wsa;
    SOCKET sock, newsock;
    int c;
    struct sockaddr_in server, client;
    char smesg[155];
    printf("\nInitialising Winsock...");
    if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
    {
        printf("Failed. Error Code : %d", WSAGetLastError());
        return 1;
    }

    printf("Initialised.\n");

    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
        printf("Could not create socket! Error: %d", WSAGetLastError());
        return 1;
    }
    //textcolor(2);
    printf("Socket Created!\n");

    server.sin_family = AF_INET;
    server.sin_addr.s_addr = INADDR_ANY;
    server.sin_port = htons(3939);

    //bind
    if (bind(sock, (struct sockaddr *)&server, sizeof(server)) == SOCKET_ERROR) {
        printf("Bind failed! Error Code: %d", WSAGetLastError());
    }
    puts("Binded!");
    printf("\nNow Listening...\n");
    listen(sock, 1);
    //Accept!
    c = sizeof(struct sockaddr_in);
    newsock = accept(sock, (struct sockaddr *)&client, &c);
    if (newsock == INVALID_SOCKET) {
        printf("Couldn't Accept connection!");
    }
    printf("Accepted Connection!\n");
        //char *client_ip = inet_ntoa(client.sin_addr);
        //int client_port = ntohs(client.sin_port);
        while (1) {
            printf("Command: ");
            fgets(smesg, 4040, stdin);
            send(newsock, smesg, strlen(smesg), 0);
            bzero(smesg, sizeof(smesg));
            //memset(0, smesg, 4040);
        }
    return 0;
}

客户端.c

#include <stdio.h>
#include <winsock.h>
#include <stdlib.h>
#include "stdafx.h"
#include <conio.h>
#define bzero(b,len) (memset((b), '\0', (len)), (void) 0)  
#pragma comment(lib,"ws2_32.lib") //Winsock Library

int main(int argc, char *argv[])
{
    int reader;
    WSADATA wsa;
    SOCKET sock;
    struct sockaddr_in server;
    char message[2000];
    printf("\nInitialising Winsock...");
    if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
    {
        printf("Failed. Error Code : %d", WSAGetLastError());
        return 1;
    }

    printf("Initialised.\n");


    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
        printf("Could not create socket! Error: %d", WSAGetLastError());
        return 1;
    }
    //textcolor(2);
    printf("Socket Created!\n");

    server.sin_addr.s_addr = inet_addr("127.0.0.1");
    server.sin_family = AF_INET;
    server.sin_port = htons(3939);

    //Connect
    if (connect(sock, (struct sockaddr *)&server, sizeof(server)) < 0) {
        puts("Connect Error");
        return 1;
    }
    puts("Connected\n");
    while (1) {
        //memset(0, message, 4040);
        reader = recv(sock, message, 2000, 0);
        fprintf(stdout, "%s", message);
        bzero(message, 2000);
    }
    return 0;
}

我在服务器控制台和客户端控制台recv()s/Prints 中输入“Hello” 由于我不能从控制台进行 C&P,所以我只能用它的图片代替 Console Output

更新问题,正如许多人发布的空终止符我所做的修复它是开关

        printf("Command: ");
        fgets(smesg, 155, stdin);

在 server.c 中

        bzero(smesg, sizeof(smesg));
        printf("Command: ");
        fgets(smesg, 155, stdin);
        strcat(smesg, "\0");

【问题讨论】:

  • 货物崇拜 'bzero(message, 2000);'在错误的地方。
  • 那么,为什么memset(0, message, 4040); 被注释掉了,为什么你硬编码4040 的大小(这是错误的)而不是使用sizeof( message ),以及第二次键入时会发生什么“你好”?
  • 如果我再次输入 hello 它只会打印相同的内容 Hello 然后一堆这些字符
  • 'send(newsock, smesg, strlen(smesg), 0);'甚至不发送 NUL 终止符,并且如上所述,即使发送(例如使用 1+strlen),也不能保证它会在第一次 recv 调用中被接收。

标签: c windows sockets


【解决方案1】:

真的很难确切地说出您为什么会遇到这种确切的不稳定行为,因为您同时滥用了几件东西,因此可能会发生非常奇怪的事情。

首先,套接字是流,它们不是消息传递机制。这意味着套接字没有您尝试发送和接收的“消息”的概念。接收端的套接字不知道您在发送端写入了 10 个字节。所以,如果你想使用套接字来发送消息,那么你必须在流之上实现一个消息协议。

基于流协议的最简单的消息协议版本是在每条消息前面加上消息的长度。所以:在发送端,首先将消息的长度写入套接字,然后再写入消息本身。在阅读端,首先阅读长度,然后阅读消息。长度是固定的字节数(通常为 4),因此您始终知道要读取/写入多少字节。

然后,由于套接字是流,因此您必须注意每次从套接字读取多少字节。仅仅因为您要求 10 个字节,并不意味着您将收到 10 个字节。如果您请求 10 个字节并且收到的字节数少于 10 个字节,那么您必须重复调用以读取更多字节。 recv() 函数不返回任何“读者”。它返回它成功读取的字节数。并且不要将其存储在以后永远不会检查的变量中。将其考虑在内,以便您知道您是否收到了您所期望的全部消息。

另外,不要忘记 asciiz 空字符终止符。发送字符串时,可以将消息的长度视为字符串的strlen()。但是,当您收到该字符串时,它不会以空字符结尾,因此您不能在不先将 \0 附加到它的情况下继续将其提供给 printf()。如果您省略空字符终止符,则printf() 将打印您的字符串,然后打印您接收缓冲区中已经存在的任何垃圾。

【讨论】:

    【解决方案2】:

    常见问题。你忽略了recv()返回的值:

    reader = recv(sock, message, 2000, 0);
    fprintf(stdout, "%s", message)
    

    应该是

    reader = recv(sock, message, 2000, 0);
    if (reader == -1)  // error
    {
        perror("recv)"); // at least
        break; // out of the read loop and close the socket
    }
    else if (reader == 0) // EOS: peer has closed the connection
        break;
    else
        fprintf(stdout, "%.*s", reader, message);
    

    【讨论】:

      猜你喜欢
      • 2016-08-02
      • 2012-06-11
      • 1970-01-01
      • 1970-01-01
      • 2013-08-08
      • 1970-01-01
      • 1970-01-01
      • 2012-04-27
      • 1970-01-01
      相关资源
      最近更新 更多