我正在尝试学习如何使用 Winsock 在 C 上使用 SMTP。我遵循了 Microsoft 提供的guide,最终代码按原样完美运行。现在,我决定更进一步,通过获取代码的不同部分并将它们放入具有适当名称的函数中,使其更加“有条理”。 我很高兴看到我的代码编译成功,完全没有错误,但是在第一次运行它之后,VSC 调试器通知我出现了分段错误。有问题的行(到目前为止)是这一行:
89| *iResult = connect( *ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
现在我有点迷路了,因为你访问指针结构的方式就像指针一样“加起来”,在那里你可以有一个
**var1
。也许错误实际上并不在有问题的行本身,而是之前......我不知道,我迷路了。
到目前为止,这是我的代码:
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#define _WIN32_WINNT 0x0501
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iphlpapi.h>
#include <stdio.h>
#include <stdlib.h>
#define DEFAULT_PORT "25"
char data[500];
int initWinsock(int *, WSADATA *);
int makeSocket(int *, struct addrinfo *, struct addrinfo *, struct addrinfo *, SOCKET *);
int connSocket(int *, SOCKET *, struct addrinfo *, struct addrinfo *);
int clientReceive(int *, SOCKET *, char *);
int closeSocketListener(int *, SOCKET *);
int closeSocket(SOCKET *);
int main() {
SOCKET ConnectSocket = INVALID_SOCKET;
WSADATA wsaData;
int iResult;
struct addrinfo *result = NULL,
*ptr = NULL,
hints;
ZeroMemory( &hints, sizeof(hints) );
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
initWinsock(&iResult, &wsaData);
makeSocket(&iResult, &hints, result, ptr, &ConnectSocket);
connSocket(&iResult, &ConnectSocket, ptr, result);
clientReceive(&iResult, &ConnectSocket, data);
closeSocketListener(&iResult, &ConnectSocket);
closeSocket(&ConnectSocket);
system("pause");
return 0;
}
int initWinsock(int *iResult, WSADATA *wsaData){
// Initialize Winsock
*iResult = WSAStartup(MAKEWORD(2,2), wsaData);
if (*iResult != 0) {
printf("WSAStartup failed: %d\n", *iResult);
return 1;
}
return 0;
}
int makeSocket(int *iResult, struct addrinfo *hints, struct addrinfo *result, struct addrinfo *ptr, SOCKET *ConnectSocket){
// Resolve the server address and port
*iResult = getaddrinfo("52.97.26.134", DEFAULT_PORT, hints, &result);
if (*iResult != 0) {
printf("getaddrinfo failed: %d\n", *iResult);
WSACleanup();
return 1;
}
// Attempt to connect to the first address returned by
// the call to getaddrinfo
ptr=result;
// Create a SOCKET for connecting to server
*ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);
if (*ConnectSocket == INVALID_SOCKET) {
printf("Error at socket(): %d\n", WSAGetLastError());
freeaddrinfo(result);
WSACleanup();
return 1;
}
return 0;
}
int connSocket(int *iResult, SOCKET *ConnectSocket, struct addrinfo *ptr, struct addrinfo *result){
// Connect to server.
*iResult = connect( *ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
if (*iResult == SOCKET_ERROR) {
closesocket(*ConnectSocket);
*ConnectSocket = INVALID_SOCKET;
}
// Should really try the next address returned by getaddrinfo
// if the connect call failed
// But for this simple example we just free the resources
// returned by getaddrinfo and print an error message
freeaddrinfo(result);
if (*ConnectSocket == INVALID_SOCKET) {
printf("Unable to connect to server!\n");
WSACleanup();
return 1;
}
return 0;
}
int clientReceive(int *iResult, SOCKET *ConnectSocket, char *out){
*iResult = recv(*ConnectSocket, out, 200, 0);
if (iResult > 0) {
printf("Bytes received: %d\n", *iResult);
return 0;
} else if (iResult == 0) {
printf("Connection closed\n");
return 1;
} else {
printf("recv failed: %d\n", WSAGetLastError());
return 2;
}
return -1;
}
int closeSocketListener(int *iResult, SOCKET *ConnectSocket){
// shutdown the send half of the connection since no more data will be sent
*iResult = shutdown(*ConnectSocket, SD_SEND);
if (*iResult == SOCKET_ERROR) {
printf("shutdown failed: %d\n", WSAGetLastError());
closesocket(*ConnectSocket);
WSACleanup();
return 1;
}
return 0;
}
int closeSocket(SOCKET *ConnectSocket){
// cleanup
closesocket(*ConnectSocket);
WSACleanup();
return 0;
}
在函数体范围内,所有参数都是局部变量。您可以将参数视为局部变量,即使它们不是,显然因为您可以从“外部”为它们赋值,这对于实际局部变量而言并非如此。
例1):
void foo(int bar)
{
bar = 123;
}
没有什么奇怪的,你不会期望这个函数会影响这个函数之外的任何东西。
int var = 987;
foo(var);
printf("%i", var); // prints 987 as expected
但是有指针,例子2):
void foo(int *bar)
{
*bar = 123;
}
这会影响外部价值。
int var = 987;
foo(&var);
printf("%i", var); // prints 123, again as expected
这个例子 3):
void foo(int *bar)
{
int baz = 123;
bar = &baz;
}
现在我们称它为:
int var = 987;
foo(&var);
printf("%i", var); // prints 987
怎么会这样?这条线
bar = &baz;
不应该覆盖 bar 的值吗?答案是应该而且确实如此,但 bar 只是“局部变量”,这就是为什么我们看不到外部效果的原因。原因与第一个示例完全相同。
当我们看第二个例子时,它确实会影响外部价值,唯一的区别是取消引用。
只有当你在函数体外部取消引用你正在读取/写入值的指针时,请注意箭头运算符是指向结构的指针的特殊类型取消引用。
您的相关代码:
int makeSocket(int *iResult, struct addrinfo *hints, struct addrinfo *result, struct addrinfo *ptr, SOCKET *ConnectSocket){
*iResult = getaddrinfo("52.97.26.134", DEFAULT_PORT, hints, &result);
// ...
ptr=result;
// ...
}
这一行:
ptr=result;
同例3,对“外”值没有影响
&result
表示局部变量的地址,让我们想象一下如何使用函数设置值getaddrinfo
:
void getaddrinfo(/* ... */ int **bar)
{
static int baz = 123;
*bar = &baz;
}
现在如果我们调用那个函数:
int *var;
getaddrinfo(&var);
printf("%i", *var); // prints 123
但是:
void foo(int *bar)
{
getaddrinfo(&bar);
printf("%i", *bar); // prints 123
}
int _default = 987;
int *var = &_default;
foo(var);
printf("%i", *var); // prints 987, as if the "foo(var);" wasn't called at all
解决方案可能是:
void foo(int **bar)
{
getaddrinfo(bar);
}
int *var;
foo(&var);
printf("%i", *var); // prints 123