如何使用套接字在c中指定tcp连接双方(客户端和服务器端)的端口号

问题描述 投票:0回答:1

我正在尝试在 C: 中复制此图

我已经很好地创建了请求线连接,但我找不到任何有关如何创建连接的信息,其中双方都指定了端口号,如图中的回复线所示。到目前为止,我只能指定连接的一侧。

服务器代码:

#include "acc.h"

int main() {
    int listenSocket, connSocket, clientDataPort, dataSocket, conStat;
    struct sockaddr_in servAddr, clientAddr, dataAddr;
    char buffer[MAXLINE];
    socklen_t clientLen;
    ssize_t bytesRead;

    listenSocket = socket(AF_INET, SOCK_STREAM, 0);

    memset(&servAddr, 0, sizeof(servAddr));
    servAddr.sin_family = AF_INET;
    servAddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servAddr.sin_port = htons(SERV_TCP_PORT);

    bind(listenSocket, (SA*)&servAddr, sizeof(servAddr));

    listen(listenSocket, MAX_CLIENTS);

    printf("Server listening on port %d (Port L)...\n", SERV_TCP_PORT);

    clientLen = sizeof(clientAddr);
    connSocket = accept(listenSocket, (SA*)&clientAddr, &clientLen);

    printf("Client connected for control: %s : %d (Ephemeral Port)\n", inet_ntoa(clientAddr.sin_addr), ntohs(clientAddr.sin_port));

    bytesRead = recv(connSocket, buffer, sizeof(buffer), 0);
    buffer[bytesRead] = '\0';

    clientDataPort = atoi(buffer);
    printf("Received client's data port: %d (Port U)\n", clientDataPort);

    close(connSocket);
    printf("request line closed\n");


    dataSocket = socket(AF_INET, SOCK_STREAM, 0);

    memset(&dataAddr, 0, sizeof(dataAddr));
    dataAddr.sin_family = AF_INET;
    dataAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    dataAddr.sin_port = htons(clientDataPort);

    if ((conStat = connect(dataSocket, (SA*)&dataAddr, sizeof(dataAddr))) < 0) {
        printf("connect failed\n");  // error connecting to remote socket
    } else {
        printf("Connected to client's data port: %d\n", clientDataPort);
    }

    close(dataSocket);

    return 0;
}

客户端代码:

#include "acc.h"

int main() {
    int cliSocket, dataSocket, dataConnSocket;
    struct sockaddr_in servAddr, dataAddr;
    char buffer[MAXLINE];
    socklen_t dataLen;
    
    cliSocket = socket(AF_INET, SOCK_STREAM, 0);

    bzero(&servAddr, sizeof(servAddr));
    servAddr.sin_family = AF_INET;
    servAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    servAddr.sin_port = htons(SERV_TCP_PORT);

    connect(cliSocket, (SA*)&servAddr, sizeof(servAddr));
    
    printf("Connected to server for control on port: %d\n", SERV_TCP_PORT);

    sprintf(buffer, "%d", CLI_TCP_PORT);
    send(cliSocket, buffer, strlen(buffer), 0);

    close(cliSocket);
    printf("request line closed\n");

    dataSocket = socket(AF_INET, SOCK_STREAM, 0);

    bzero(&dataAddr, sizeof(dataAddr));
    dataAddr.sin_family = AF_INET;
    dataAddr.sin_addr.s_addr = htonl(INADDR_ANY);
    dataAddr.sin_port = htons(CLI_TCP_PORT);

    bind(dataSocket, (SA*)&dataAddr, sizeof(dataAddr));

    listen(dataSocket, MAX_CLIENTS);

    printf("Client listening for data on port: %d...\n", CLI_TCP_PORT);

    dataLen = sizeof(dataAddr);
    if((dataConnSocket = accept(dataSocket, (SA*)&dataAddr, &dataLen)) < 0 ) {
    fprintf(stderr, "accept failed\n");
    exit (1);
    }
    
    printf("Server connected on data port: %d\n", CLI_TCP_PORT);
    printf("%s : %d\n", inet_ntoa(dataAddr.sin_addr), ntohs(dataAddr.sin_port));

    close(dataConnSocket);
    close(dataSocket);

    return 0;
}

acc.h文件:

#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <string.h>
#include <errno.h>
#include <netdb.h>
#include <sys/uio.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>

#define MAXLINE 4096
#define LISTENQ 1024
#define SERV_TCP_PORT   50090 // port L
#define CLI_TCP_PORT    50094 // port U
#define SA struct sockaddr
#define MAX_CLIENTS 10

我需要如何设置程序:

对于请求线路,服务器在端口 L 上侦听(被动模式)。客户端在端口 L 上向服务器发起请求线路(主动模式)。另一方面,对于回复线路,服务器使用端口 L +1 表示主动模式,客户端在端口 U 上处于被动模式。客户端通过请求线向服务器发送其端口号 U,以便服务器可以向该端口发起回复线。当请求行关闭时,服务器也会关闭回复行。

如果我在这里描述的内容与我之前所说的我想要做的不同,请让我知道我误解了什么。我看过的所有地方都说它要么不可能,要么我需要在连接的两侧使用

bind()
两次,但这不起作用。

如果解决了,任何证明所有端口均已指定并正确连接的代码(即

printf()
)将不胜感激。

c sockets tcp port client-server
1个回答
0
投票

该图的解决方案相当简单:

  1. server创建一个套接字,

    bind()
    将其连接到端口
    L
    并在其上调用
    listen()
    ,然后调用
    accept()
    来接收入站连接,然后调用
    recv()
    来接收来自的数据那个套接字连接。

  2. client 创建一个套接字,可选 1

    bind()
    到端口
    0
    指示操作系统选择一个随机临时端口,然后
    connect()
    到服务器上的端口
    L
    .

    1 如果省略此

    bind()
    connect()
    将选择临时端口。

  3. client创建第二个套接字,

    bind()
    将其连接到端口
    U
    (可以是特定端口或另一个临时端口,由您决定),在其上调用
    listen()
    ,然后
    send() 
    的端口
    U
    通过第一个套接字连接连接到服务器,
    accept()
    是第二个套接字上的连接。

  4. 服务器

    recv()
    的端口
    U
    从第一个套接字连接,然后创建第二个套接字,
    bind()
    是端口
    L+1
    connect()
    是端口
    U
    在客户端上,然后
    send()
    通过第二个套接字连接进行回复。

就该图而言,这就是所需要的。

就您的代码而言:

  • 双方都完全缺乏足够的错误处理。但让我们暂时忽略这一点。

  • 您确实应该将客户端端口作为二进制

    uint16_t
    值而不是以空结尾的字符串发送。但我们暂时也忽略这一点。

  • 您的客户端正在正确处理第一个连接(除非在

    close()
    'ing 请求之后不应该
    send()
    'ing 此连接,因为这违反了给定的规则)。然而,它在创建第二个监听套接字之前就向服务器发送了请求。因此,服务器可能会在第二个套接字存在之前尝试
    connect()
    到它。客户端实际上需要在端口
    listen()
    上运行,然后才告诉服务器
    U
    到端口
    connect()
    
    

  • 您的服务器正在正确处理第一个连接(除非一旦它是客户端的数据,它就不应该
  • U

    'ing此连接,因为这违反了给定的规则)。然而,在将其连接到客户端之前,它根本没有连接第二个套接字,更不用说端口了。

    
    
        
    
    
© www.soinside.com 2019 - 2024. All rights reserved.