From: Neil N. <n_n...@ds...> - 2002-03-18 22:38:57
|
Dear Jesper Eskilson, Here is some quick TCP code in both Linux and Win98 ripped out of some code I am working on. #ifdef WIN32 #include <winsock2.h> #else #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> #include <unistd.h> // close #include <fcntl.h> #include <errno.h> #include <sys/time.h> #include <termios.h> #include <dirent.h> // Directory information. #endif #include <stdio.h> #include <stdlib.h> #include <iostream> // cout #include <fstream> // file i/o #include <string> #ifdef WIN32 // ** Perform MS socket communication initialization. WORD winsock_ver; WSADATA wsadata; winsock_ver = MAKEWORD(1, 1); if (WSAStartup(winsock_ver, &wsadata)) { icsa_log << "Unable to initialise WinSock" << endl; skt_error(wrk_skt_state); return 0; // cleanup } if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1) { icsa_log << "WinSock version is incompatible with 1.1" << endl; skt_error(wrk_skt_state); return 0; // cleanup } #endif // icsa_log << "Debug icsa_open - before create server socket " << endl; // ** create server socket #ifdef WIN32 SOCKADDR_IN local_server_addr; // accept_addr, #else struct sockaddr_in local_server_addr; // accept_addr, #endif // create socket local_server_socket = socket(AF_INET, SOCK_STREAM, 0); if(local_server_socket < 0) { icsa_log << " cannot open socket " << endl; return 0; } // *** These are non-blocked sockets. You may want to have blocked sockets. #ifdef WIN32 ioctlsocket(local_server_socket, FIONBIO, &non_blocking); // make non-blocking. #elif defined UNIX fcntl(local_server_socket, F_SETFL, O_NONBLOCK); #endif /* bind server port */ local_server_addr.sin_family = AF_INET; local_server_addr.sin_addr.s_addr = htonl(INADDR_ANY); local_server_addr.sin_port = htons(icsa_config.local_server_port); if(bind(local_server_socket, (struct sockaddr*)&local_server_addr, sizeof(local_server_addr)) < 0) { icsa_log << " cannot bind port " << endl; #ifdef WIN32 closesocket(local_server_socket); #elif defined UNIX close(local_server_socket); #endif return 0; } // #ifdef WIN32 // fix this for UNIX char szHostName[100] = {""}; struct hostent *pHostEntry = NULL; // Get the name of this machine if (gethostname(szHostName, 100 )) // error occurred. icsa_log << "Error icsa_open - gethostname" << endl; else { // get the IP address from that name (will always succeed if // gethostname has succeeded ) pHostEntry = gethostbyname(szHostName); char temp_ip[16]; sprintf(temp_ip, "%d.%d.%d.%d\n", (int)(BYTE) pHostEntry->h_addr_list[0][0], (int)(BYTE) pHostEntry->h_addr_list[0][1], (int)(BYTE) pHostEntry->h_addr_list[0][2], (int)(BYTE) pHostEntry->h_addr_list[0][3]); string temp_ip_str = temp_ip; icsa_config.local_ip = norm_ip(temp_ip_str); ----------- Here is the socket error code. Note I am using non-blocking sockets. int socket_error() { #ifdef WIN32 DWORD err=0; err = WSAGetLastError(); WSASetLastError(0); if(err) { if(err == WSAEWOULDBLOCK) { icsa_log << "WSAEWOULDBLOCK" << endl; err = 0; return 1; } if(err == WSAENOTCONN || err == WSAECONNRESET) { icsa_log << " WSAENOTCONN || WSAECONNRESET " << endl; err = 0; return 3; } else { icsa_log << "Error Winsock= " << socket_error_string(err) << endl; err = 0; return 2; } } #else if(errno) { if(errno == EAGAIN || errno == EWOULDBLOCK) { icsa_log << "EAGAIN || EWOULDBLOCK" << endl; errno = 0; return 1; } else if(errno == 115) // Operation in progress { icsa_log << "Operation in progress" << endl; errno = 0; return 1; } else if(errno == 104) // Connection reset by peer. { icsa_log << "Connection reset by peer" << endl; errno = 0; return 3; } else { icsa_log << "Error Unix socket errno= " << errno << " " << strerror(errno) << endl; errno = 0; return 2; } } #endif return 0; } #ifdef WIN32 char *socket_error_string(int error) { switch (error) { case WSAEACCES: return "Network error: Permission denied"; case WSAEADDRINUSE: return "Network error: Address already in use"; case WSAEADDRNOTAVAIL: return "Network error: Cannot assign requested address"; case WSAEAFNOSUPPORT: return "Network error: Address family not supported by protocol family"; case WSAEALREADY: return "Network error: Operation already in progress"; case WSAECONNABORTED: return "Network error: Software caused connection abort"; case WSAECONNREFUSED: return "Network error: Connection refused"; case WSAECONNRESET: return "Network error: Connection reset by peer"; case WSAEDESTADDRREQ: return "Network error: Destination address required"; case WSAEFAULT: return "Network error: Bad address"; case WSAEHOSTDOWN: return "Network error: Host is down"; case WSAEHOSTUNREACH: return "Network error: No route to host"; case WSAEINPROGRESS: return "Network error: Operation now in progress"; case WSAEINTR: return "Network error: Interrupted function call"; case WSAEINVAL: return "Network error: Invalid argument"; case WSAEISCONN: return "Network error: Socket is already connected"; case WSAEMFILE: return "Network error: Too many open files"; case WSAEMSGSIZE: return "Network error: Message too long"; case WSAENETDOWN: return "Network error: Network is down"; case WSAENETRESET: return "Network error: Network dropped connection on reset"; case WSAENETUNREACH: return "Network error: Network is unreachable"; case WSAENOBUFS: return "Network error: No buffer space available"; case WSAENOPROTOOPT: return "Network error: Bad protocol option"; case WSAENOTCONN: return "Network error: Socket is not connected"; case WSAENOTSOCK: return "Network error: Socket operation on non-socket"; case WSAEOPNOTSUPP: return "Network error: Operation not supported"; case WSAEPFNOSUPPORT: return "Network error: Protocol family not supported"; case WSAEPROCLIM: return "Network error: Too many processes"; case WSAEPROTONOSUPPORT: return "Network error: Protocol not supported"; case WSAEPROTOTYPE: return "Network error: Protocol wrong type for socket"; case WSAESHUTDOWN: return "Network error: Cannot send after socket shutdown"; case WSAESOCKTNOSUPPORT: return "Network error: Socket type not supported"; case WSAETIMEDOUT: return "Network error: Connection timed out"; case WSAEWOULDBLOCK: return "Network error: Resource temporarily unavailable"; case WSAEDISCON: return "Network error: Graceful shutdown in progress"; default: return "Unknown network error"; } } #endif --------------- Here is socket close code void close_socket(void *socket) { #ifdef WIN32 shutdown(*(SOCKET*)socket,0); // no more sends. closesocket(*(SOCKET*)socket); #elif defined UNIX shutdown(*(int*)socket,0); // no more sends. if (close(*(int*)socket) == -1) icsa_log << "Error close_socket - unable to close socket= " << *(int*)socket << " " << errno << " " << strerror(errno) << endl; #endif } ----------- Here is send code. int send_socket(void *wrk_socket, char *packet_slider, int *send_len, int wait_msec) { #ifdef WIN32 SOCKET *send_sockt = (SOCKET*)wrk_socket; DWORD err; #else int *send_sockt = (int*)wrk_socket; int err; #endif int bytes_send, tot_bytes_send=0; bytes_send = send(*send_sockt, packet_slider, *send_len, 0); if (bytes_send > 0) { if (bytes_send == *send_len) return 0; *send_len -= bytes_send; tot_bytes_send += bytes_send; packet_slider += bytes_send; } if ((err=socket_error()) == 1 && wait_msec) { timeval local_time; long start_time=0, end_time=0; #ifdef WIN32 // *** This routine uses the polling method. int cycle_dx; int cycle_cnt = int(float(wait_msec) * med_time_cycles_per_ms + 0.5); // icsa_log << "Debug send_socket - min_wait_time= " << min_wait_time // << " med_time_cycles_per_ms= " << med_time_cycles_per_ms // << " cycle_cnt= " << cycle_cnt << endl; get_local_time(&local_time); start_time = (local_time.tv_sec % 1000000) * 1000 + local_time.tv_usec / 1000; do { for(cycle_dx=cycle_cnt; cycle_dx--;) { bytes_send = send(*send_sockt, packet_slider, *send_len, 0); if (bytes_send > 0) { tot_bytes_send += bytes_send; if (bytes_send == *send_len) { *send_len = tot_bytes_send; return 0; } *send_len -= bytes_send; packet_slider += bytes_send; } if ((err=socket_error()) == 1) icsa_sleep(1); else if (err) { *send_len = tot_bytes_send; return err; } } get_local_time(&local_time); end_time = (local_time.tv_sec % 1000000) * 1000 + local_time.tv_usec / 1000; } while((end_time - start_time) < wait_msec); #else timeval tv; int socket_selected = 0; tv.tv_sec = wait_msec / 1000; tv.tv_usec = wait_msec * 1000; // Should update on select to time remaining. // icsa_log << "tv.tv_sec= " << tv.tv_sec // << "tv.tv_usec= " << tv.tv_usec // << endl; // get_local_time(&local_time); start_time = (local_time.tv_sec % 1000000) * 1000 + local_time.tv_usec / 1000; do { fd_set select_set; FD_ZERO(&select_set); FD_SET(*send_sockt, &select_set); socket_selected = select(*send_sockt + 1, NULL, &select_set, NULL, &tv); if (!socket_selected) return 1; else if (socket_selected < 0) return socket_error(); // icsa_log << "socket_selected= " << socket_selected << endl; bytes_send = send(*send_sockt, packet_slider, *send_len, 0); if (bytes_send > 0) { tot_bytes_send += bytes_send; if (bytes_send == *send_len) { *send_len = tot_bytes_send; return 0; } *send_len -= bytes_send; packet_slider += bytes_send; } if ((err=socket_error()) == 1) icsa_sleep(1); else if (err) { *send_len = tot_bytes_send; return err; } get_local_time(&local_time); end_time = (local_time.tv_sec % 1000000) * 1000 + local_time.tv_usec / 1000; // icsa_log << "end_time= " << end_time << endl; } while((end_time - start_time) < wait_msec); #endif } *send_len = tot_bytes_send; return err; } --------- Here is receive code. Much of the extra code here is that I am keeping track of the number of bytes for each packet I construct so that I can get exactly that many bytes back. TCP will split or join packets you may send separately. int recv_socket(Socket_State *recv_skt, int wait_msec) { // icsa_log << "Debug recv_socket - start recv_skt= " << recv_skt // << " wait_msec= " << wait_msec << endl; // icsa_log << "Debug recv_socket - recv_skt->packet_bytes_unrcvd = " << recv_skt->packet_bytes_unrcvd // << endl; get_local_time(&recv_skt->last_act_time); recv_skt->packet_bytes_recvd = recv(recv_skt->socket, recv_skt->packet_slider, recv_skt->packet_bytes_unrcvd, 0); icsa_log << "Debug recv_socket - after recv recv_skt->packet_bytes_recvd = " << recv_skt->packet_bytes_recvd << endl; if (recv_skt->packet_bytes_recvd > 0) { if (recv_skt->packet_tot_bytes_recvd < 2 && recv_skt->packet_bytes_recvd > 2) { memcpy(&recv_skt->packet_len, recv_skt->packet, 2); recv_skt->packet_tot_bytes_recvd += recv_skt->packet_bytes_recvd; recv_skt->packet_bytes_unrcvd = recv_skt->packet_len - recv_skt->packet_tot_bytes_recvd; } else { recv_skt->packet_tot_bytes_recvd += recv_skt->packet_bytes_recvd; recv_skt->packet_bytes_unrcvd -= recv_skt->packet_bytes_recvd; } recv_skt->packet_slider += recv_skt->packet_bytes_recvd; } else if (recv_skt->packet_bytes_recvd == 0) return 3; int err; if ((err=socket_error()) == 1 && wait_msec) { timeval local_time; long start_time=0, end_time=0; #ifdef WIN32 // *** This routine uses the polling method. int cycle_dx; int cycle_cnt = int(float(wait_msec) * med_time_cycles_per_ms + 0.5); icsa_log << "Debug recv_socket - cycle_cnt= " << cycle_cnt << endl; start_time = (recv_skt->last_act_time.tv_sec % 1000000) * 1000 + recv_skt->last_act_time.tv_usec / 1000; do { for(cycle_dx=cycle_cnt; cycle_dx--;) { recv_skt->packet_bytes_recvd = recv(recv_skt->socket, recv_skt->packet_slider, recv_skt->packet_bytes_unrcvd, 0); if (recv_skt->packet_bytes_recvd > 0) { icsa_log << "Debug recv_socket - loop after recv recv_skt->packet_bytes_recvd = " << recv_skt->packet_bytes_recvd << endl; if (recv_skt->packet_tot_bytes_recvd < 2 && recv_skt->packet_bytes_recvd > 2) { memcpy(&recv_skt->packet_len, recv_skt->packet, 2); recv_skt->packet_tot_bytes_recvd += recv_skt->packet_bytes_recvd; recv_skt->packet_bytes_unrcvd = recv_skt->packet_len - recv_skt->packet_tot_bytes_recvd; } else { recv_skt->packet_tot_bytes_recvd += recv_skt->packet_bytes_recvd; recv_skt->packet_bytes_unrcvd -= recv_skt->packet_bytes_recvd; } recv_skt->packet_slider += recv_skt->packet_bytes_recvd; if (recv_skt->packet_len && recv_skt->packet_tot_bytes_recvd >= recv_skt->packet_len) return 0; } else if (recv_skt->packet_bytes_recvd == 0) return 3; if ((err=socket_error()) == 1) icsa_sleep(1); else if (err) return err; } get_local_time(&local_time); end_time = (local_time.tv_sec % 1000000) * 1000 + local_time.tv_usec / 1000; } while((end_time - start_time) < wait_msec); #else // UNIX timeval tv; int socket_selected = 0; tv.tv_sec = wait_msec / 1000; tv.tv_usec = wait_msec * 1000; // Should update on select to time remaining. icsa_log << "Debug recv_socket - Linux recv loop tv.tv_sec= " << tv.tv_sec << "tv.tv_usec= " << tv.tv_usec << endl; start_time = (recv_skt->last_act_time.tv_sec % 1000000) * 1000 + recv_skt->last_act_time.tv_usec / 1000; do { fd_set select_set; FD_ZERO(&select_set); FD_SET(recv_skt->socket, &select_set); socket_selected = select(recv_skt->socket + 1, &select_set, NULL, NULL, &tv); if (!socket_selected) return 1; else if (socket_selected < 0) return socket_error(); // icsa_log << "socket_selected= " << socket_selected << endl; recv_skt->packet_bytes_recvd = recv(recv_skt->socket, recv_skt->packet_slider, recv_skt->packet_bytes_unrcvd, 0); if (recv_skt->packet_bytes_recvd > 0) { icsa_log << "Debug recv_socket - loop after recv recv_skt->packet_bytes_recvd = " << recv_skt->packet_bytes_recvd << endl; if (recv_skt->packet_tot_bytes_recvd < 2 && recv_skt->packet_bytes_recvd > 2) { memcpy(&recv_skt->packet_len, recv_skt->packet, 2); recv_skt->packet_tot_bytes_recvd += recv_skt->packet_bytes_recvd; recv_skt->packet_bytes_unrcvd = recv_skt->packet_len - recv_skt->packet_tot_bytes_recvd; } else { recv_skt->packet_tot_bytes_recvd += recv_skt->packet_bytes_recvd; recv_skt->packet_bytes_unrcvd -= recv_skt->packet_bytes_recvd; } recv_skt->packet_slider += recv_skt->packet_bytes_recvd; if (recv_skt->packet_len && recv_skt->packet_tot_bytes_recvd >= recv_skt->packet_len) return 0; } else if (recv_skt->packet_bytes_recvd == 0) return 3; if ((err=socket_error()) == 1) icsa_sleep(1); else if (err) return err; get_local_time(&local_time); end_time = (local_time.tv_sec % 1000000) * 1000 + local_time.tv_usec / 1000; // icsa_log << "end_time= " << end_time << endl; } while((end_time - start_time) < wait_msec); #endif } return err; } ----------- Here is client connect code. Socket_State *connect_remote(Connect_Name *connect_name) { // Socket_State is my own creation and not likely useful for you. Socket_State *inet_skt; struct hostent *host=NULL; icsa_log << "Debug connect_remote - before get inet_client_skt" << endl; inet_skt = new Socket_State; inet_skt->connect_name = connect_name; inet_skt->socket = socket(AF_INET, SOCK_STREAM, 0); icsa_log << "Debug connect_remote - inet_skt->socket = " << inet_skt->socket << endl; // *** Perform non-blocking and checks for the new socket. #ifdef WIN32 if (inet_skt->socket == INVALID_SOCKET) return NULL; else #endif if (inet_skt->socket <= 0) return NULL; else set_new_socket(inet_skt); // if ((inet_skt = set_new_socket(inet_skt)) == NULL) return NULL; icsa_log << "Debug connect_remote - after get inet_client_skt" << endl; // make client address icsa_log << "Debug connect_remote - before make inet_client_addr" << endl; inet_skt->local_addr.sin_family = AF_INET; inet_skt->local_addr.sin_addr.s_addr = htonl(INADDR_ANY); inet_skt->local_addr.sin_port = htons(0); // Bind client port. icsa_log << "Debug connect_remote - before bind inet_client_skt" << endl; if (bind(inet_skt->socket, (struct sockaddr *)&inet_skt->local_addr, sizeof(inet_skt->local_addr)) == SOCKET_ERROR) { icsa_log << "Error connect_remote - unable to bind inet_skt->socket= " << inet_skt->socket << endl; skt_error(inet_skt); delete_socket_state(inet_skt); return NULL; } icsa_log << "Debug connect_remote - before make inet_client_server_addr address ip= " << connect_name->ip << endl; // inet_skt->remote_addr.sin_family = h->h_addrtype; inet_skt->remote_addr.sin_family = AF_INET; unsigned long int addr; string remote_ip = connect_name->ip; if (remote_ip.substr(0,1) >= "0" && remote_ip.substr(0,1) <= "9") { addr = inet_addr(remote_ip.c_str()); icsa_log << "Debug connect_remote - gethostbyaddr NULL path " << remote_ip << endl; memcpy((char *)&inet_skt->remote_addr.sin_addr.s_addr, &addr, sizeof(addr)); } else { icsa_log << "Debug connect_remote - gethostbyname path " << remote_ip << endl; host = gethostbyname(remote_ip.c_str()); if(host==NULL) { icsa_log << "Error connect_remote - unknown host from gethostbyname " << remote_ip << endl; delete_socket_state(inet_skt); return NULL; } else memcpy((char*)&inet_skt->remote_addr.sin_addr.s_addr, host->h_addr_list[0], host->h_length); } inet_skt->remote_addr.sin_port = htons(connect_name->server_port); // Connect to remote address. icsa_log << "Debug connect_remote - in connect_remote before connect" << endl; if (connect(inet_skt->socket, (struct sockaddr *) &inet_skt->remote_addr, sizeof(inet_skt->remote_addr)) == SOCKET_ERROR) { int skt_err =skt_error(inet_skt); if(skt_err == 2) { icsa_log << "Error connect_remote - Could not connect." << endl; delete_socket_state(inet_skt); return NULL; } } icsa_log << "Debug connect_remote - end after connect to remote address" << endl; return inet_skt; } ------------- Here is some server accept code. while(1) // *** Start of message receive and processing loop. { // *** do server accept for new, pending sockets. accept_skt->socket = accept(local_server_socket, (struct sockaddr*)&accept_skt->remote_addr, &addr_len); #ifdef WIN32 if (accept_skt->socket == INVALID_SOCKET) {} else #endif if(accept_skt->socket < 0) {} else if (accept_skt->socket == 0) { icsa_log << "zero accept_skt->socket= " << accept_skt->socket << endl; } else { icsa_log << "server_loop_old - before set_new_socket accept_skt->socket= " << accept_skt->socket << endl; set_new_socket(accept_skt); accept_skt = new Socket_State; } |