gpt4 book ai didi

c - 在理解套接字 getaddrinfo 方面需要帮助

转载 作者:太空宇宙 更新时间:2023-11-04 02:29:33 25 4
gpt4 key购买 nike

我一直在努力学习套接字编程,并偶然发现了这个网站:http://beej.us/guide/bgnet/output/html/multipage/index.html这真的很好。我能够理解,但是在我自己编写了一个测试程序之后,我遇到了一个问题,我在 3 小时后无法理解。时间有限,所以想在这里请教高手。

这是我的服务器程序:

/**
* Program showing how sockets can be used
*/

#include <stdio.h> // For printf
#include <strings.h> // For bzero, memset
#include <stdlib.h> // For exit, atoi
#include <unistd.h> // For close

#include <sys/socket.h> // For socket
#include <sys/types.h> // For types

#include <arpa/inet.h> // For inet_addr
#include <netdb.h> // Import module network database

#include <errno.h> // To access the global errno that holds last system call error
#include <assert.h> // For asserting

#define ADDRESSINFO_GET_SUCCESS 0
const int kSuccess = 0;
const char *kMyPort = "3490"; // Can be either port num or name of the service
const int kMaxListenConn = 10; // How many connections queue will hold

// Utility function to get socket address, IPv4 or IPv6:
void* getSocketAddress(const struct sockaddr *sa)
{
// Cast socketaddr to sockaddr_in to get address and port values from data

if (sa->sa_family == PF_INET) {
return &(((struct sockaddr_in *)sa)->sin_addr);
}

return &(((struct sockaddr_in6 *)sa)->sin6_addr);
}

// Utility function to get socket address string
void getSocketAddressString(const struct sockaddr *sd, char *ipAddr)
{
inet_ntop(sd->sa_family, getSocketAddress(sd), ipAddr, INET6_ADDRSTRLEN);
}

int createAndBindSocket(const struct addrinfo *addrList)
{
int status = -1; // Invalid status
int socketFileDesc = -1; // Invalid descriptor

const struct addrinfo *addrIt;

/*
* STEP 2.1: Loop through all the addrinfo nodes and bind to the one we can
*/

for (addrIt = addrList; addrIt != NULL; addrIt = addrIt->ai_next)
{
char ipAddr[INET6_ADDRSTRLEN];
inet_ntop(addrIt->ai_family, getSocketAddress(addrIt->ai_addr), ipAddr, sizeof ipAddr);
printf("IP: %s\n", ipAddr);

/*
* STEP 2.2: Crete the socket file descriptor for our IP address
*/

socketFileDesc = socket(addrIt->ai_family, addrIt->ai_socktype, addrIt->ai_protocol);

if (socketFileDesc == -1) {
fprintf(stderr, "Failed to create socket with error: %d\n", errno);
perror("socket"); // Get error desc
continue; // Try next address
}

/*
* STEP 2.3: Set socket behaviour by making ip address to be re-used if used already
*/

int yes=1;

if (setsockopt(socketFileDesc, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1)
{
perror("setsockopt");
exit(1);
}

printf("Port %d\n",((struct sockaddr_in *)addrIt->ai_addr)->sin_port);

/*
* STEP 2.4: Bind our socket with ip address and port number to listen
*/

status = bind(socketFileDesc, addrIt->ai_addr, addrIt->ai_addrlen);
if (status != kSuccess) {
fprintf(stderr, "Failed to bind socket with error:%d\n", errno);
perror("bind"); // Get error desc

// Clear socket
close(socketFileDesc);
socketFileDesc = -1;

continue; // Try next address
}
}

return socketFileDesc;
}


int main()
{
int status = -1; // Status is invalid

struct addrinfo hints; // Holds our hints to get address info
struct addrinfo *addrList; // Contains our address info

/*
* STEP 1: Setup service details
*/

// Make sure struct is empty
bzero(&hints, sizeof hints); // memset(&hints, 0, sizeof(hints));

hints.ai_family = AF_UNSPEC; // Don't care IPv4 or v6
hints.ai_socktype = SOCK_STREAM; // Use TCP stream sockets
hints.ai_flags = AI_PASSIVE; // Use my local IPs or you igore this and provide IP manually in first arg


status = getaddrinfo(NULL, kMyPort, &hints, &addrList);
if (status != kSuccess) {
fprintf(stderr, "Failed to get address info with error: %s\n", gai_strerror(status));
exit(1);
}

/*
* STEP 2: Create a socket and bind it
*/

int socketFileDesc;
socketFileDesc = createAndBindSocket(addrList);

freeaddrinfo(addrList); // Done with list

if (socketFileDesc == -1) {
exit(1);
}

/*
* STEP 3: Listen to the port for incoming connections
*/

status = listen(socketFileDesc, kMaxListenConn); // Second arg is number of incoming connections in queue
if (status != kSuccess) {
fprintf(stderr, "Failed to listen to the port\n");
perror("listen");
goto exit;
}

printf("Server is listening at the port %s\n", kMyPort);

struct sockaddr_storage inConnAddr; // Big enough to hold both IPv4 and v6
socklen_t inConnAddrLen = sizeof inConnAddr;

const size_t kMaxBufferSize = 50;
char buff[kMaxBufferSize] = {0};

long bytesReceived = -1;
long bytesSent = 0;
int clientSockfd;

while (1) {

/*
* STEP 4: Accept incoming connections
*/

inConnAddrLen = sizeof inConnAddr;

clientSockfd = accept(socketFileDesc, (struct sockaddr *)&inConnAddr, &inConnAddrLen);

// Got new connection ?
if (clientSockfd == -1) {
perror("accept"); // Print error description
continue; // Continue to look for new connections
}

//
// Got connection, create child process to handle request
//

if (!fork()) {
close(socketFileDesc); // No need with child

char ipAddr[INET6_ADDRSTRLEN];
getSocketAddressString((struct sockaddr *)&inConnAddr, ipAddr);
printf("Child process created for hanlding request from %s\n", ipAddr);

/*
* STEP 5: Receive and Send data to requests
*/

bytesReceived = recv(clientSockfd, &buff, kMaxBufferSize - 1, 0);
if (bytesReceived > 0)
{
printf("Data from client %s\n", buff);
while (bytesSent < bytesReceived) {
bytesSent = send(clientSockfd, buff, bytesReceived, 0);
printf("Bytes sent %ld\n", bytesSent);
}
}

if (bytesReceived < 0) {
perror("recv");
}
else if (bytesReceived == 0) {
printf("Connection closed by server\n");
}

close(clientSockfd); // Close socket
exit(0);
}
}

exit:
/*
* STEP 5: Close the socket
*/
close(socketFileDesc);


return 0;
}

这是我的客户端程序:

#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <netdb.h>
#include <strings.h>
#include <arpa/inet.h>

const char kSuccess = 0;

// Utility function to get socket address
void* getSocketAddress(const struct sockaddr *sa)
{
if (sa->sa_family == AF_INET) {
return &( ((struct sockaddr_in *)sa)->sin_addr );
}

return &( ((struct sockaddr_in6 *)sa)->sin6_addr );
}

// Utility function to get socket address string
void getSocketAddressString(const struct sockaddr *sd, char *ipAddr)
{
inet_ntop(sd->sa_family, getSocketAddress(sd), ipAddr, INET6_ADDRSTRLEN);
}

int createAndConnectSocket(const struct addrinfo *addrList)
{
int socketFileDesc = -1; // Invalid descriptor

const struct addrinfo *addrIt;

/*
* STEP 2.1: Loop through all the addrinfo nodes and bind to the one we can
*/

for (addrIt = addrList; addrIt != NULL; addrIt = addrIt->ai_next)
{
/*
* STEP 2.2: Crete the socket file descriptor for our IP address
*/

socketFileDesc = socket(addrIt->ai_family, addrIt->ai_socktype, addrIt->ai_protocol);

if (socketFileDesc == -1) {
fprintf(stderr, "Failed to create socket with error: %d\n", errno);
perror("socket"); // Get error desc
continue; // Try next address
}

/*
* STEP 2.3: Set socket behaviour by making ip address to be re-used if used already
*/

int yes=1;

if (setsockopt(socketFileDesc, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1)
{
perror("setsockopt");
exit(1);
}

char ipAdd[INET6_ADDRSTRLEN];
getSocketAddressString(addrIt->ai_addr, ipAdd);
((struct sockaddr_in *)addrIt->ai_addr)->sin_port = atoi("3490");

printf("IP is %s::%d\n", ipAdd,((struct sockaddr_in *)addrIt->ai_addr)->sin_port);

// Connect to the socket
int status;
status = connect(socketFileDesc, addrIt->ai_addr, addrIt->ai_addrlen);
if (status != kSuccess) {
perror("connect");
close(socketFileDesc);
socketFileDesc = -1;
continue;
}
}

return socketFileDesc;
}

int main(int argc, char* argv[])
{
// Check we have data from arguments
if (argc < 3 || argc > 3) {
perror("Invalid command");
printf("Usage: %s hostname portnumber\n", argv[0]);
printf(" %s 192.168.1.2 3490\n", argv[0]);
}

// Setup server address info
struct addrinfo *serverInfo;
struct addrinfo hints;

int status = -1;

memset(&hints, 0, sizeof hints); // Make sure it is empty

hints.ai_family = AF_INET; // IPv4
hints.ai_socktype = SOCK_STREAM; // Use socket stream

((struct sockaddr_in *)&hints.ai_addr)->sin_port = atoi(argv[2]);

status = getaddrinfo(argv[1], "3490", &hints, &serverInfo);
if (status != kSuccess) {
fprintf(stderr, "Failed to get address info %s\n", gai_strerror(status));
exit(1);
}

printf("Connecting to %s::%s...\n", argv[1], argv[2]);

// Create and bind socket
int sockfd = createAndConnectSocket(serverInfo);

freeaddrinfo(serverInfo); // We are done with serverinfo

if (sockfd == -1) {
exit(1);
}

// Send and receive data from server

long bytesReceived = -1;
long bytesSent = 0;

const size_t kMaxBufferSize = 50;
char buff[kMaxBufferSize];

const char *msg = "Hi! I am client!";
size_t msgLen = strlen(msg);

printf("Connected to server\n");

// Loop to send and receive data
while (1) {

while (bytesSent < msgLen) {
bytesSent = send(sockfd, msg, msgLen, 0);
printf("Bytes sent %ld\n", bytesSent);
}

bytesReceived = recv(sockfd, &buff, kMaxBufferSize - 1, 0);
if (bytesReceived > 0)
{
printf("Data received from server: %s\n", buff);
}
else if (bytesReceived < 0) {
perror("Read error");
break;
}
else if (bytesReceived == 0) {
printf("Connection closed by server\n");
break;
}
}

// Close socket
close(sockfd);

return 0;
}

我的问题是:即使我在 getaddrinfo() 调用中设置了端口和 IP,但是当它绑定(bind)或连接时,在 sockaddr 结构中发现的端口号是错误的,因为它是不同的值。我确实知道这里发生了什么,因此当我这样做时我收到连接被拒绝的消息。谁能看看我的程序并告诉我为什么连接被拒绝?如果有人可以对我的代码提出任何改进建议,我将不胜感激。

谢谢

最佳答案

您没有连接到您认为正在连接的端口。

((struct sockaddr_in *)addrIt->ai_addr)->sin_port = atoi("3490");

sin_port 的值必须是网络字节顺序,即大端。您改为直接分配值 3490(通过 atoi),因此该值按主机字节顺序排列。您的平台很可能使用小端字节序。

因此,您连接的不是端口 3490(十六进制 0DA2),而是端口 41485(十六进制 A2 0D)。

您需要使用 htons 函数,它将 16 位值(因为 sin_port 是一个 16 位字段)从主机字节顺序转换为网络字节顺序。另外,这里不需要使用 atoi。只需使用数字常量即可。

((struct sockaddr_in *)addrIt->ai_addr)->sin_port = htons(3490);

关于c - 在理解套接字 getaddrinfo 方面需要帮助,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45488276/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com