MFC類庫為我們提供了“方便、好用”的CAsyncSocket和CSocket,但是MFC實現過於複雜,有些資源使用不當會帶來很多問題。像CAsyncSocket和CSocket它們是實現是很複雜的,裡面實現非同步訊息是通過表單的訊息機制來實現的,程式設計得不好就會常常出現初始化時的錯誤,運行一段時間程式也常常出現莫名其妙的錯誤導致程式崩潰。當然程式崩潰肯定不會是MFC的錯,有時候你不得不採用逐行注釋代碼的方法來確定什麼地方導致程式的錯誤,這個過程是相當痛苦的。
我喜歡用windows api的SOCKET,什麼操作和錯誤都是掌握在自己的手中。沒有了CAsyncSocket的非同步訊息機制自己用線程來做,網路編程多線程是第一課。經過長期的實賤,封裝了如下SOCKET函數,其實使用也挺簡單的:
1
。用戶端函數:#include<stdlib.h>#include<winsock.h>#include<stdio.h>#include<string.h> #ifndef INADDR_NONE#define INADDR_NONE 0xffffffff#endif //******************************************************////本函數負責與服務平台聯絡////****************************************************** SOCKET SocketConnect(const char *host,const char *service,const char *transport){ struct protoent *protoin;//傳輸協議資訊 struct sockaddr_in ipaddr;//主機的IP地址資訊 struct hostent *hostin;//主機的資訊 struct servent *servin;//伺服器(主機)資訊 int sock,type;//通訊端描述符 //將ipaddr結構快速清零 memset(&ipaddr,0,sizeof(ipaddr)); //地址結構 ipaddr.sin_family=AF_INET; //從伺服器類型得到連接埠號碼 if(servin=getservbyname(service,transport)) ipaddr.sin_port=servin->s_port;//連接埠號碼 else if((ipaddr.sin_port=htons((u_short)atoi(service)))==0) { printf("get server information error/n"); WSACleanup(); return INVALID_SOCKET; } //由傳輸協議得到對應的傳輸協議編碼 if((protoin=getprotobyname(transport))==0) { printf("get protocol information error/n"); WSACleanup(); return INVALID_SOCKET; } //從主機名稱擷取主機IP if(hostin=gethostbyname(host)) memcpy(&ipaddr.sin_addr,hostin->h_addr,hostin->h_length);//// else if((ipaddr.sin_addr.s_addr=inet_addr(host))==INADDR_NONE) { printf("get host IP infomation error/n"); WSACleanup(); return INVALID_SOCKET; } //根據傳輸類型給變數賦值 if(strcmp(transport,"udp")==0) type=SOCK_DGRAM; else type=SOCK_STREAM; //建立通訊端描述符 sock=socket(PF_INET,type,protoin->p_proto); if(sock==INVALID_SOCKET) { printf("creat socket error/n"); WSACleanup(); return INVALID_SOCKET; } //串連指定IP地址機器的指定服務連接埠,如果串連失敗,則退出 if(connect(sock,(struct sockaddr*)&ipaddr,sizeof(ipaddr))==SOCKET_ERROR) { printf("connect socket error!please start server first/n"); WSACleanup(); return INVALID_SOCKET; } return sock;} //************************************//UDP傳輸類型//************************************SOCKET UDPConnect(const char *host,const char *service){ return SocketConnect(host,service,"udp");} //************************************//TCP傳輸類型//************************************SOCKET TCPConnect(const char *host,const char *service){ return SocketConnect(host,service,"tcp");}
2
。用戶端函數調用例子(TCP)
:
//
啟動程式後即串連伺服器,串連成功後接收控制台輸入,發送到服務端#include<stdio.h>#include<string.h>#include<stdlib.h>#include<winsock.h>#pragma comment(lib,"wsock32") #ifndef INADDR_NONE#define INADDR_NONE 0xffffffff#endif SOCKET TCPConnect(const char*,const char*);void TCPecho(char *,char*); #define LINELEN 128#define WAVERS MAKEWORD(2,0)//WORD MAKEWORD(byte bLow,byte bHigh); void main(int argc,char *argv[]){ char *host="localhost";//IP 位址 char *service="3333";//預設連接埠號碼 WSADATA wsadata; switch(argc) { case 1: host="localhost";//使用預設IP break; case 2: host=argv[1];//使用命令列給的IP break; case 3: host=argv[1]; service=argv[2];//使用命令列IP和連接埠號碼 break; default: printf("argment error/n"); exit(1); } if(WSAStartup(WAVERS,&wsadata)!=0)//初始化Winsock { printf("initalize failed/n"); WSACleanup();//清除Winsock exit(1); } TCPecho(host,service); WSACleanup(); exit(0);} void TCPecho(char *host,char *service){ char buf[LINELEN+1]; SOCKET s; int outchars; s=TCPConnect(host,service);//採用TCP協議串連服務程式 while(fgets(buf,sizeof(buf),stdin))//迴圈調用得到使用者輸入,當輸入的資料為斷行符號時退出 { buf[LINELEN]='/0'; outchars=strlen(buf); send(s,buf,outchars,0);//發送訊息 if(buf[0]=='/n') break; } closesocket(s);}
3
。服務端函數:#include<string.h>#include<winsock.h>#include<stdlib.h>#include<stdio.h>////SocketServer//SOCKET SocketServer(const char *service,const char*transport,int qlen){ struct sockaddr_in ipaddr;//主機IP地址 struct servent *servin;// 主機資訊 struct protoent *protoin;//傳輸類型 int sock,type;// 通訊端描述符 memset(&ipaddr,0,sizeof(ipaddr)); ipaddr.sin_family=AF_INET; ipaddr.sin_addr.s_addr=INADDR_ANY; // 從伺服器名得到服務連接埠 if(servin=getservbyname(service,transport)) ipaddr.sin_port=htons(ntohs((u_short)servin->s_port)); else if((ipaddr.sin_port=htons((u_short)atoi(service)))==0) { printf("get portnumber error/n"); WSACleanup(); return INVALID_SOCKET; } //從傳輸協議得到對應的編號 if((protoin=getprotobyname(transport))==0) { printf("get protocol number error/n"); WSACleanup(); return INVALID_SOCKET; } //根據傳輸協議給對應的變數賦值 if(strcmp(transport,"udp")==0) type=SOCK_DGRAM; else type=SOCK_STREAM; //建立通訊端 sock=socket(PF_INET,type,protoin->p_proto); if(sock==INVALID_SOCKET) { printf("creat socket error/n"); WSACleanup(); return INVALID_SOCKET; } //綁定本地IP if(bind(sock,(struct sockaddr*)&ipaddr,sizeof(ipaddr))==SOCKET_ERROR) { printf("socket bind error/n"); WSACleanup(); return SOCKET_ERROR; } //如果是串流(TCP)使通訊端處於監聽狀態,等待來自客戶機的串連,參數qlen之指定等待 //隊列長度 if(type==SOCK_STREAM) { if(listen(sock,qlen)==SOCKET_ERROR) { printf("socket listen errror/n"); WSACleanup(); return SOCKET_ERROR; } } return sock;} //************************************//UDPServer//************************************SOCKET UDPServer(const char *service){ return SocketServer(service,"udp",0);} //************************************//TCPServer//************************************SOCKET TCPServer(const char *service,int qlen){ return SocketServer(service,"tcp",qlen);}
4
。伺服器例子(TCP)
:
//接受用戶端串連,每接收到用戶端發送而來的資料則列印在控制台#include<winsock.h>#include<string.h>#include<stdio.h>#include<stdlib.h>#pragma comment(lib,"wsock32") #define QLEN 5#define BUFSIZE 5#define WAVERS MAKEWORD(2,0)//WORD MAKEWORD(byte bLow,byte bHigh); SOCKET TCPServer(const char*service,int qlen);void main(int argc,char*argv[]){ char *service="3333";//預設連接埠號碼 struct sockaddr_in fsin; SOCKET msock,ssock; WSADATA wsadata; int alen,cc; char buf[BUFSIZE]; switch(argc) { case 1: break;//採用預設連接埠號碼 case 2: service=argv[1];//採用命令列連接埠號碼 break; default: printf("argment error/n"); exit(1); } if(WSAStartup(WAVERS,&wsadata)!=0)//初始化Winsock { printf("initalize failed/n"); WSACleanup();//清除Winsock exit(1); } msock=TCPServer(service,QLEN);//採用TCP協議 while(1) { alen=sizeof(struct sockaddr); ssock=accept(msock,(struct sockaddr*)&fsin,&alen);//accept阻塞接收用戶端請求 if(ssock==INVALID_SOCKET) { printf("initialize failed/n"); WSACleanup(); exit(1); } while(cc=recv(ssock,buf,sizeof(buf)-1,0))//接收用戶端資訊,當收到的資訊為零時,則退出 { buf[cc]='/0'; printf("%s",buf); } printf("connect close..."); closesocket(ssock); break; }} 以上是兩個完整的用戶端和服務端程式,可建立兩個VC控制台工程編譯運行,進行串連測試。