【问题标题】:C socket - recv: connection reset by peerC套接字-recv:对等方重置连接
【发布时间】:2017-03-30 01:08:13
【问题描述】:

这里一切都很好,除了客户端收不到消息,或者服务器不能发送,我不知道。 我的时间真的很少,所以我不能再浪费时间来处理这个问题了,所以我求助于你们。我认为(也许)你必须知道的一件事:服务器在我的网络下,客户端在我的学校网络下。 附言服务器的不同 IP 是因为我在 NAT 后面,这没问题。

客户端代码

const char* IPSERVER = "87.21.70.136";

int main(int argc, char *argv[]){

struct addrinfo hints, *serverInfo;
int s;

memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC; // AF_INET or AF_INET6 to force version
hints.ai_socktype = SOCK_STREAM;

if(getaddrinfo(IPSERVER, "50", &hints, &serverInfo) != 0){
    printf("Errore getaddrinfo(). Chiusura...\n");
    exit(-1);
}

s = socket(serverInfo->ai_family, serverInfo->ai_socktype, serverInfo->ai_protocol);
printf("Porta: %d\n", ((struct sockaddr_in * ) serverInfo->ai_addr)->sin_port);

if(connect(s, serverInfo->ai_addr, serverInfo->ai_addrlen) < 0)
    perror("Errore connect()");

char buf[2000];
int bytes_rec;
if((bytes_rec = recv(s, buf, sizeof buf, 0)) < 0)
    perror("Errore recv");
printf("%s\n",buf);
close(s);

return 0;

}

服务器代码

struct sockaddr_storage clientAddr;
socklen_t addrSize;
struct addrinfo hints, *myInfo;
int s, newS;

memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;  // use IPv4 or IPv6, whichever
hints.ai_socktype = SOCK_STREAM;

if(getaddrinfo("192.168.1.2", "50", &hints, &myInfo) < 0)
    perror("Errore getaddrinfo()");
printf("Porta: %d\n", ((struct sockaddr_in *) myInfo->ai_addr) -> sin_port);
s = socket(myInfo->ai_family, myInfo->ai_socktype, myInfo->ai_protocol);
if(s < 0)
    perror("Errore socket()");
printf("Socket stabilita.\n");
if(bind(s, myInfo->ai_addr, myInfo->ai_addrlen) < 0)
 perror("Errore bind()");
printf("Porta creata.\n");
if(listen(s, 5) < 0)
    perror("Errore listen()");
printf("Server in ascolto...\n");
addrSize = sizeof clientAddr;
if((newS = accept(s, (struct sockaddr * )&clientAddr, &addrSize) < 0))
   perror("Errore accept()");
printf("Invio messaggio in corso...\n");
char *msg = "ciao, mi vedi?";
int len, bytes_sent;
len = strlen(msg);
if((bytes_sent = send(newS, msg, len, 0)) < 0)
    perror("Errore send()");
printf("Messaggio inviato.\n");
closesocket(newS);
closesocket(s);
WSACleanup();

return 0;

}

客户端输出

Porta: 12800
Errore recv: Connection reset by peer

服务器输出

Porta: 12800
Socket stabilita.
Porta creata.
Server in ascolto...
Invio messaggio in corso...
Errore send(): No error
Messaggio inviato.

Process returned 0 (0x0)   execution time : 4.789 s
Press any key to continue.

【问题讨论】:

  • 打印sin_port值时,需要使用ntohs()。 12800是以网络字节顺序表示的50,所以在打印之前将其翻转到主机字节顺序。此外,recv() 不会以空值终止缓冲区,因此您必须手动执行此操作,或者更好地使用%.*s,以便您可以将bytes_rec 传递给printf()
  • 这里真正的问题是perror()send() 错误之后打印no error。我怀疑更多的防火墙问题,如您的previous question with the same code。注意:如果您在系统调用中遇到错误,那么继续进行几乎是完全无效的,就好像它没有发生一样。 socket(), listen(), bind(), connect(), accept() 中的错误对这段代码来说是致命的,应该会导致退出或返回。
  • 谢谢大家。我明天试试。
  • 附带说明,您不应该使用AF_UNSPEC,因为您的其余代码假定使用 IPv4(通过使用 sockaddr_in)。请改用AF_INET
  • 当然。我没想到。

标签: c sockets send recv


【解决方案1】:

服务器代码显然使用了 WinSock API,这意味着服务器运行在 Windows 上。

Windows 上的套接字句柄是无符号值,因此 int 是错误的数据类型用于它们。您需要改用 SOCKET 类型。并再次比较socket()accept() 的返回值INVALID_SOCKET 常量。

忽略套接字创建错误并将无效套接字传递给send()recv() 可能会导致它们失败。

perror()errno 上运行,而 WinSock(以及一般的 Win32 API)不使用该 errno。这可以解释send() 失败后的"no error" 消息,因为errno 为0。当WinSock 函数失败时,您必须使用WSAGetLastError() 来获取WinSock 错误代码。

话虽如此,试试这个服务器代码:

void psocketerror(const char *msg, int err)
{
    fprintf(stderr, "%s: %d\n", msg, err);
}

void psocketerror(const char *msg)
{
    psocketerror(msg, WSAgetLastError());
}

int main(int argc, char *argv[])
{
    struct sockaddr_storage clientAddr;
    socklen_t addrSize;
    struct addrinfo hints, *myInfo;
    SOCKET s, newS;
    int err;

    WSADATA wsa;
    err = WSAStartup(MAKEWORD(2, 0), &wsa);
    if (err != 0) {
        psocketerror("Errore WSAStartup()", err);
        return 1;
    }

    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;

    if ((err = getaddrinfo("192.168.1.2", "50", &hints, &myInfo)) < 0) {
        psocketerror("Errore getaddrinfo()", err);
        WSACleanup();
        return 1;
    }

    printf("Porta: %d\n", ntohs(((struct sockaddr_in *)(myInfo->ai_addr))->sin_port));

    s = socket(myInfo->ai_family, myInfo->ai_socktype, myInfo->ai_protocol);
    if (s == INVALID_SOCKET) {
        psocketerror("Errore socket()");
        WSACleanup();
        return 1;
    }

    printf("Socket stabilita.\n");

    if (bind(s, myInfo->ai_addr, myInfo->ai_addrlen) < 0) {
        psocketerror("Errore bind()");
        closesocket(s);
        WSACleanup();
        return 1;
    }

    printf("Porta creata.\n");

    if (listen(s, 5) < 0) {
        psocketerror("Errore listen()");
        closesocket(s);
        WSACleanup();
        return 1;
    }

    printf("Server in ascolto...\n");

    addrSize = sizeof clientAddr;
    newS = accept(s, (struct sockaddr * )&clientAddr, &addrSize)
    if (newS === INVALID_SOCKET) {
        psocketerror("Errore accept()");
        closesocket(s);
        WSACleanup();
        return 1;
    }

    printf("Invio messaggio in corso...\n");

    char *msg = "ciao, mi vedi?";
    int len, bytes_sent;
    len = strlen(msg);

    if ((bytes_sent = send(newS, msg, len, 0)) < 0) {
        psocketerror("Errore send()");
        closesocket(newS);
        closesocket(s);
        WSACleanup();
        return 1;
    }

    printf("Messaggio inviato.\n");

    closesocket(newS);
    closesocket(s);

    WSACleanup();

    return 0;
}

【讨论】:

  • 谢谢。我明天试试。
  • 我试过你的代码,它可以工作,谢谢。 Obv 只需要用 fprintf 更改 printfs。关于客户端,我注意到使用 %.*s 它打印随机字符,而不是使用 %s 并且没有使用 \0 手动终止数组,它通常会打印接收到的字符串。只是说。
  • %.*s 如果你正确使用它可以正常工作,例如:printf("%.*s\n", bytes_rec, buf);。为%s 指定精度会告诉printf() 要打印的最大字符数,因此缓冲区不需要以空值结尾。 * 只是告诉printf() 从参数列表而不是格式字符串中获取精度值。如果您没有为%s 指定精度,则缓冲区必须以空值结尾,否则如果缓冲区不包含任何嵌入的空字符,它将超出缓冲区的边界并从周围的内存中打印数据。跨度>
  • 哦,我明白了。不知道那个。感谢这个有用的提示。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-12
  • 1970-01-01
  • 2017-02-04
  • 1970-01-01
  • 1970-01-01
  • 2016-04-12
相关资源
最近更新 更多