Windows下TCP程式設計的一些工具函式——應付大部分的Windows TPC程式設計場景。
阿新 • • 發佈:2018-12-17
首先是這些函式的定義:
#define INTERNAL_LOG_ON 0 #define INTERNAL_LOG_OFF 1 #define INTERNAL_LOG_MODE INTERNAL_LOG_OFF //or INTERNAL_LOG_ON //#define INTERNAL_LOG_MODE INTERNAL_LOG_ON //or INTERNAL_LOG_OFF namespace WinTCP { void internalLog(const char* format, ...); //windows 下載入元件 bool initWSADATA(); //windows 下解除安裝元件 void uninitWSADATA(); //建立TCP套接字 //return: socket_fd int createWinTCPSocket(); //關閉套接字 void closeWinTCPSocket(int socket_fd); //伺服器繫結 //socket_fd 為伺服器套接字fd //port 為伺服器要繫結的埠號 //return 返回繫結成功與否 bool hostBind(int socket_fd, int port); //伺服器監聽 //socket_fd 為服務端套接字 //return 返回客戶端套接字fd int hostListen(int socket_fd); //連線服務端 //socket_fd 為客戶端fd //addr 為服務端的ip地址 //addr 為所連服務端的埠 //return : 成功或者失敗 bool connectToHost(int socket_fd, int addr, int port); //讀取資料 //socket_fd 為所要讀取的socket_fd //data 為緩衝區 //size 為緩衝區大小 //timeout 為超時時間(msec) //try_count 為失敗嘗試的次數 //return 實際讀取到的資料長度 int readData(int socket_fd, char* data, int size, int timeout, int try_count = 2); //寫入資料 //socket_fd 為所要寫入的socket_fd //data 為緩衝區 //size 為緩衝區大小 //timeout 為超時時間(msec) //try_count 為失敗嘗試的次數 //return 實際讀寫入的資料長度 int writeData(int socket_fd, char* data, int size, int timeout, int try_count = 2); }
然後是這些函式的實現:
#include <stdio.h> #include <stdarg.h> #include <WinSock2.h> #pragma comment(lib,"ws2_32.lib") namespace WinTCP { static bool hasInitWSADATA = false; void internalLog(const char* format, ...) { if(INTERNAL_LOG_MODE == INTERNAL_LOG_OFF) return; va_list argList; va_start(argList, format); vfprintf(stdout, format, argList); va_end(argList); } bool initWSADATA() { if(!hasInitWSADATA) { WSADATA wsa_data; hasInitWSADATA = ::WSAStartup(MAKEWORD(2,2), &wsa_data) == 0; if(!hasInitWSADATA) { int errorCode = ::WSAGetLastError(); internalLog("initWSADATA: hasInit(%d) errorCode(%d).\n", hasInitWSADATA, errorCode); } } return hasInitWSADATA; } void uninitWSADATA() { hasInitWSADATA = false; ::WSACleanup(); } int createWinTCPSocket() { SOCKET socket_fd; socket_fd = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); return socket_fd; } void closeWinTCPSocket(int socket_fd) { if(0>::closesocket(socket_fd)) { int errorCode = ::WSAGetLastError(); internalLog("closeWinTCPSocket: error(%d).\n", errorCode); } } bool hostBind(int socket_fd, int port) { SOCKADDR_IN server_addr; //伺服器配置 server_addr.sin_family = AF_INET; server_addr.sin_port = htons(port); //埠號範圍: 0 ~65535 server_addr.sin_addr.S_un.S_addr = htonl(INADDR_ANY); //INADDR_ANY意思是接受所有IP的連線 if(0>::bind(socket_fd,(SOCKADDR *)&server_addr,sizeof(SOCKADDR))) //繫結ip域socket,出錯返回SOCKET_ERROR { int errorCode = ::WSAGetLastError(); internalLog("hostBind: fd(%d) errorCode(%d).\n", socket_fd, errorCode); return false; } internalLog("hostBind: fd(%d) port(%d).\n", socket_fd, port); return true; } int hostListen(int socket_fd) { if(0 > ::listen(socket_fd, 5)) { int errorCode = ::WSAGetLastError(); internalLog("hostListen: fd(%d) listen error(%d).\n", socket_fd, errorCode); } SOCKADDR_IN client_addr; //儲存客戶端資訊 SOCKET fd_client; //客戶端套接字 int addr_len = sizeof(SOCKADDR); fd_client = ::accept(socket_fd,(SOCKADDR *)&client_addr,&addr_len); if(0>= fd_client) { int errorCode = ::WSAGetLastError(); internalLog("hostListen: fd(%d) accept error(%d).\n", socket_fd, errorCode); } internalLog("hostListen: fd(%d) accept client(%d).\n", socket_fd, fd_client); return fd_client; } bool connectToHost(int socket_fd, int addr, int port) { SOCKADDR_IN server_addr; server_addr.sin_family = AF_INET; //Internet協議 server_addr.sin_port = htons(port); server_addr.sin_addr.S_un.S_addr = addr;//inet_addr("127.0.0.1"); if(0>::connect(socket_fd,(SOCKADDR *)&server_addr,sizeof(SOCKADDR))) //連線失敗則返回SOCKET_ERROR { int errorCode = ::WSAGetLastError(); internalLog("connectToHost: fd(%d) errorCode(%d).\n", socket_fd, errorCode); return false; } internalLog("connectToHost: fd(%d) addr(%d) port(%d).\n", socket_fd, addr, port); return true; } int readData(int socket_fd, char* data, int size, int timeout, int try_count /*= 2*/) { timeout *= 1000; FD_SET read_fds; FD_SET except_fds; timeval t = {0, timeout}; int recv_size = 0; u_long blacked_on = 0; u_long blacked_off = 1; if(0>::ioctlsocket(socket_fd, FIONBIO, &blacked_off)) { int errorCode = ::WSAGetLastError(); internalLog("readData: fd(%d) set socket to blacked_off error(%d).\n", socket_fd, errorCode); return recv_size; } for(int i = 0; i < try_count; i++) { internalLog("readData: fd(%d) try(%d/%d) read data...\n", socket_fd, i, try_count); FD_ZERO(&read_fds); FD_ZERO(&except_fds); FD_SET(socket_fd, &read_fds); FD_SET(socket_fd, &except_fds); int res = ::select(0, &read_fds, 0, &except_fds, &t); //windows 中nfds沒有意義 if(res < 0) { int errorCode = ::WSAGetLastError(); internalLog("readData: fd(%d) select error(%d).\n", socket_fd, errorCode); } else if(res == 0) { internalLog("readData: fd(%d) timeout...\n", socket_fd); } else { if(FD_ISSET(socket_fd, &except_fds)) { int errorCode = ::WSAGetLastError(); internalLog("readData: fd(%d) exception(%d).\n", socket_fd, errorCode); } else if(FD_ISSET(socket_fd, &read_fds)) { int ret = ::recv(socket_fd, data + recv_size, size - recv_size, 0); if(ret > 0) { recv_size += ret; } } } if(recv_size == size) //*讀取到夠了的資料,返回 break; } if(0>::ioctlsocket(socket_fd, FIONBIO, &blacked_on)) { int errorCode = ::WSAGetLastError(); internalLog("readData: fd(%d) set socket to blacked_on error(%d).\n", socket_fd, errorCode); return recv_size; } return recv_size; } int writeData(int socket_fd, char* data, int size, int timeout, int try_count /*= 2*/) { FD_SET write_fds; FD_SET except_fds; timeval t = {0, timeout}; int write_size = 0; u_long blacked_on = 0; u_long blacked_off = 1; if(0>::ioctlsocket(socket_fd, FIONBIO, &blacked_off)) { int errorCode = ::WSAGetLastError(); internalLog("writeData: fd(%d) set socket to blacked_off error(%d).\n", socket_fd, errorCode); return write_size; } for(int i = 0; i < try_count; i++) { internalLog("writeData: fd(%d) try(%d/%d) write data...\n", socket_fd, i, try_count); FD_ZERO(&write_fds); FD_ZERO(&except_fds); FD_SET(socket_fd, &write_fds); FD_SET(socket_fd, &except_fds); int res = ::select(0, 0, &write_fds, &except_fds, &t); //windows 中nfds沒有意義 if(res < 0) { int errorCode = ::WSAGetLastError(); internalLog("writeData: fd(%d) select error(%d).\n", socket_fd, errorCode); } else if(res == 0) { internalLog("writeData: fd(%d) timeout...\n", socket_fd); } else { if(FD_ISSET(socket_fd, &except_fds)) { int errorCode = ::WSAGetLastError(); internalLog("writeData: fd(%d) exception(%d).\n", socket_fd, errorCode); } else if(FD_ISSET(socket_fd, &write_fds)) { int ret = ::send(socket_fd, data + write_size, size - write_size, 0); if(ret > 0) { write_size += ret; } } } if(write_size == size) //*讀取到夠了的資料,返回 break; } if(0>::ioctlsocket(socket_fd, FIONBIO, &blacked_on)) { int errorCode = ::WSAGetLastError(); internalLog("writeData: fd(%d) set socket to blacked_on error(%d).\n", socket_fd, errorCode); return write_size; } return write_size; } }
使用這些函式能應付幾乎全部的Windows TPC程式設計場景。