matwachich Posted May 15, 2013 Posted May 15, 2013 Hi everybody! I wanted to learn winsockets, and for this, I try to reproduce AutoIt's TCP functions. My code is working pretty good! I juste want somebody, an AutoIt dev, to look my code and tell me if I'm doing something wrong, or something need to be done differently. Another problem, is that when I close a socket on one side of a connection, the recv functions in the other side doesn't detect the close action! (like TCPRecv would return an @error) Thanks in advance! (I compile it with GCC 4x) Here is the code expandcollapse popup#ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #define WINVER 0x0501 #define _WIN32_WINNT 0x0501 #include <windows.h> #include <winsock2.h> #include <ws2tcpip.h> #include <iphlpapi.h> #include <stdio.h> #include <stdlib.h> /* -------------------------------------------------------------------------- */ #define SIMPLE_TCP_BUILD_DLL #include "simpletcp.h" /* -------------------------------------------------------------------------- */ #define CHECK_INIT if (_global_ == NULL) return; #define CHECK_INIT_RET(ret) if (_global_ == NULL) return ret; #define SET_ERR(func) sprintf(_global_->err_func, "%s", func); _global_->err = WSAGetLastError(); #define UNSET_ERR sprintf(_global_->err_func, "%s", ""); _global_->err = 0; /* -------------------------------------------------------------------------- */ typedef struct { WSADATA wsaData; char err_func[20]; int err; } simple_tcp_t; simple_tcp_t* _global_ = NULL; /* -------------------------------------------------------------------------- */ /* Standard functions */ /** \brief * * \return */ int SIMPLE_TCP TCPStartup () { if (_global_ != NULL) { return 1; } _global_ = malloc(sizeof(simple_tcp_t)); if (_global_ == NULL) { return 0; } if (WSAStartup(MAKEWORD(2, 2), &_global_->wsaData) != 0) { free(_global_); _global_ = NULL; return 0; } UNSET_ERR return 1; } /** \brief * * \return */ void SIMPLE_TCP TCPShutdown () { if (_global_ == NULL) { return; } WSACleanup(); free(_global_); _global_ = NULL; } /** \brief * * \param * \param * * \return */ int SIMPLE_TCP TCPGetError (char* err_func, const size_t err_func_len) { CHECK_INIT_RET(0) int err = _global_->err; if (err_func != NULL && err_func_len > 0) { snprintf(err_func, err_func_len, "%s", _global_->err_func); } UNSET_ERR return err; } /** \brief * * \param * \param * * \return */ int SIMPLE_TCP TCPConnect (const char* host, const char* port) { CHECK_INIT_RET(0) UNSET_ERR struct addrinfo hints, *result = NULL; ZeroMemory(&hints, sizeof(hints)); hints.ai_family = AF_INET; // IP v4 hints.ai_socktype = SOCK_STREAM; // Stream socket hints.ai_protocol = IPPROTO_TCP; // TCP if (getaddrinfo(host, port, &hints, &result) != 0) { SET_ERR("getaddrinfo") return 0; } SOCKET ConnectSocket = INVALID_SOCKET; ConnectSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol); if (ConnectSocket == INVALID_SOCKET) { freeaddrinfo(result); SET_ERR("socket") return 0; } if (connect(ConnectSocket, result->ai_addr, (int)result->ai_addrlen) != 0) { freeaddrinfo(result); SET_ERR("connect") return 0; } freeaddrinfo(result); unsigned long mode = 1; if (ioctlsocket(ConnectSocket, FIONBIO, &mode) != 0) { SET_ERR("ioctlsocket") return 0; } return ConnectSocket; } /** \brief * * \param * * \return */ int SIMPLE_TCP TCPListen (const char* port) { CHECK_INIT_RET(0) UNSET_ERR struct addrinfo hints, *result = NULL; ZeroMemory(&hints, sizeof(hints)); hints.ai_family = AF_INET; // IP v4 hints.ai_socktype = SOCK_STREAM; // Stream socket hints.ai_protocol = IPPROTO_TCP; // TCP hints.ai_flags = AI_PASSIVE; // this + node = NULL => local host IP if (getaddrinfo(NULL, port, &hints, &result) != 0) { SET_ERR("getaddrinfo") return 0; } SOCKET ListenSocket = INVALID_SOCKET; ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol); if (ListenSocket == INVALID_SOCKET) { freeaddrinfo(result); SET_ERR("socket") return 0; } unsigned long mode = 1; if (ioctlsocket(ListenSocket, FIONBIO, &mode) != 0) { freeaddrinfo(result); SET_ERR("ioctlsocket") return 0; } if (bind(ListenSocket, result->ai_addr, (int)result->ai_addrlen) != 0) { freeaddrinfo(result); SET_ERR("bind") return 0; } freeaddrinfo(result); if (listen(ListenSocket, SOMAXCONN) != 0) { SET_ERR("listen") return 0; } return ListenSocket; } /** \brief * * \param * * \return */ int SIMPLE_TCP TCPAccept (int socket) { CHECK_INIT_RET(0) UNSET_ERR int err; SOCKET ClientSocket = INVALID_SOCKET; ClientSocket = accept(socket, NULL, NULL); err = WSAGetLastError(); if (ClientSocket == INVALID_SOCKET && err != WSAEWOULDBLOCK) { SET_ERR("accept") return INVALID_SOCKET; } if (ClientSocket != INVALID_SOCKET) { unsigned long mode = 1; if (ioctlsocket(ClientSocket, FIONBIO, &mode) != 0) { SET_ERR("ioctlsocket") return INVALID_SOCKET; } } return ClientSocket; } /** \brief * * \param * * \return */ int SIMPLE_TCP TCPCloseSocket (int socket) { CHECK_INIT_RET(0) UNSET_ERR if (shutdown(socket, SD_BOTH) != 0) { SET_ERR("shutdown") return 0; } if (closesocket(socket) != 0) { SET_ERR("closesocket") return 0; } return 1; } /** \brief * * \param * \param * \param * * \return */ int SIMPLE_TCP TCPSend (int socket, const char* data, const size_t data_len) { CHECK_INIT_RET(0) UNSET_ERR int ret = send(socket, data, data_len, 0); if ((ret == SOCKET_ERROR || ret == 0) && WSAGetLastError() != WSAEWOULDBLOCK) { SET_ERR("send") return 0; } return ret; } /** \brief * * \param * \param * \param * * \return */ int SIMPLE_TCP TCPRecv (int socket, char* data, const size_t data_len) { CHECK_INIT_RET(0) UNSET_ERR int ret = recv(socket, data, data_len, 0); if ((ret == 0 || ret == SOCKET_ERROR) && WSAGetLastError() != WSAEWOULDBLOCK) { SET_ERR("recv") return 0; } return ret; } /** \brief * * \param * \param * \param * * \return */ int SIMPLE_TCP TCPNameToIP (const char* name, char* ip, const size_t ip_len) { CHECK_INIT_RET(0) UNSET_ERR struct addrinfo hints, *result = NULL; ZeroMemory(&hints, sizeof(hints)); hints.ai_family = AF_INET; // IP v4 hints.ai_socktype = SOCK_STREAM; // Stream socket hints.ai_protocol = IPPROTO_TCP; // TCP if (getaddrinfo(name, NULL, &hints, &result) != 0) { SET_ERR("getaddrinfo") return 0; } if (result->ai_family != AF_INET || result->ai_addrlen != sizeof(struct sockaddr_in)) { SET_ERR("bad address family") freeaddrinfo(result); return 0; } struct sockaddr_in* addr = (struct sockaddr_in*)result->ai_addr; char* ret = inet_ntoa(addr->sin_addr); snprintf(ip, ip_len, "%s", ret); freeaddrinfo(result); return 1; } /** \brief * * \param * \param * \param * * \return */ int SIMPLE_TCP TCPSocketToIP (int socket, char* ip, const size_t ip_len) { CHECK_INIT_RET(0) UNSET_ERR struct sockaddr_in addr_in; int len = sizeof(addr_in); if (getpeername(socket, (struct sockaddr*)&addr_in, &len) != 0) { SET_ERR("getpeername") return 0; } char* ret = inet_ntoa(addr_in.sin_addr); if (ret == NULL) { SET_ERR("inet_ntoa") return 0; } snprintf(ip, ip_len, "%s", ret); return 1; } and the header #ifndef __SIMPLE_TCP_H__ #define __SIMPLE_TCP_H__ #ifdef SIMPLE_TCP_BUILD_DLL #define SIMPLE_TCP __stdcall __declspec(dllexport) #else #define SIMPLE_TCP __stdcall __declspec(dllimport) #endif /* -------------------------------------------------------------------------- */ /* Standard functions */ int SIMPLE_TCP TCPStartup (); void SIMPLE_TCP TCPShutdown (); int SIMPLE_TCP TCPGetError (char* err_func, const size_t err_func_len); int SIMPLE_TCP TCPConnect (const char* ip, const char* port); int SIMPLE_TCP TCPListen (const char* port); int SIMPLE_TCP TCPAccept (int socket); int SIMPLE_TCP TCPCloseSocket (int socket); int SIMPLE_TCP TCPSend (int socket, const char* data, const size_t data_len); int SIMPLE_TCP TCPRecv (int socket, char* data, const size_t data_len); int SIMPLE_TCP TCPNameToIP (const char* name, char* ip, const size_t ip_len); int SIMPLE_TCP TCPSocketToIP (int socket, char* ip, const size_t ip_len); #endif // __SIMPLE_TCP_H__
funkey Posted May 16, 2013 Posted May 16, 2013 As far as I see you are just missing some timeouts.I wrapped some C functions in AutoIt to extend the TCP functions of AutoIt.expandcollapse popup#include "socket_UDF.au3" _WSAStartup() Global $iSock = _socket($AF_INET, $SOCK_STREAM, $IPPROTO_TCP) ConsoleWrite("Socket: " & $iSock & @CRLF) Global $tTimeVal = DllStructCreate("DWORD timeout") DllStructSetData($tTimeVal, "timeout", 200) ; Timeout 200ms $iSockError = _setsockopt($iSock, $SOL_SOCKET, $SO_RCVTIMEO, $tTimeVal) ; set Timeout for recv If $iSockError Then ConsoleWrite("SetSockOpt error setting recv timeout!. Windows Sockets Error Codes: " & _WSAGetLastError() & @CRLF) _closesocket($iSock) TCPShutdown() ;WSACleanup EndIf Global $iBind = _bind($iSock, @IPAddress1, 20000) ;local IP-Address and port to use ConsoleWrite("Bind error: " & $iBind & @CRLF) ; 0 is OK Global $iConnect = _connect($iSock, "10.1.6.51", 80) ConsoleWrite("Connect error: " & $iConnect & @CRLF) ; 0 is OK If $iConnect Then ConsoleWrite("Windows Sockets Error Codes: " & _WSAGetLastError() & @CRLF) _closesocket($iSock) TCPShutdown() ;WSACleanup EndIf Global $sToSend = "GET /input1?PW=?" Global $tSendBuffer = DllStructCreate("char[" & StringLen($sToSend) & "]") DllStructSetData($tSendBuffer, 1, $sToSend) Global $iSend = _send($iSock, $tSendBuffer) ConsoleWrite("Bytes sent: " & $iSend & @CRLF) Global $tRecvBuffer = DllStructCreate("char[64]") Global $iRecv = _recv($iSock, $tRecvBuffer) ConsoleWrite("Bytes received: " & $iRecv & @CRLF) ConsoleWrite("Data received: " & DllStructGetData($tRecvBuffer, 1) & @CRLF) ;input1;OFF _closesocket($iSock) _WSACleanup() socket_UDF.au3 Programming today is a race between software engineers striving tobuild bigger and better idiot-proof programs, and the Universetrying to produce bigger and better idiots.So far, the Universe is winning.
matwachich Posted May 18, 2013 Author Posted May 18, 2013 (edited) Thanks for the answer! Adding a timeout will not solve the problem, since my sockets are asynchronous. I think it hase something to do with to SO_KEEPALIVE options, but I don't very well understand how it works or how to use it. Edited May 18, 2013 by matwachich
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now