實驗名稱 Socket編程綜合實驗(1) 一、實驗目的: 1、理解進程通信的原理及通信過程 2、掌握基於TCP和UDP的工作原理 3、掌握基本的Socket網絡編程原理及方法 二、實驗內容 1、掌握簡單的基於流式套接字的編程技術:如實現簡單的聊天功能、實現簡單的信息服務功能等等。 2、掌握簡單的基於數據報式套接字的編程技術:如實現簡單的聊天功能、實現簡單的信息服務功能等等。 三、對所實現的功能進行描述,並附上相應的流程圖。 1、基於流式套接字:可以通過選擇,分別實現聊天、游戲:猜數字和應用:判斷是否為閏年功能。 2、基於數據報式套接字:可以通過選擇,分別實現聊天、游戲:猜數字和應用:判斷是否為閏年功能。
TCP實驗流程圖:
|
TCP客戶端 TCP服務器端
UDP實驗流程圖: UDP客戶端 UDP服務器端 四、實驗源代碼:(關鍵接口函數和代碼必須加注釋) 基於流式套接字TCP源代碼: Initsock.h文件: #pragma comment(lib,"WS2_32.lib")
class CinitSock { public: CinitSock(BYTE minorVer = 2, BYTE majorVer = 2) { // 初始化WS2_32.dll WSADATA wsaData; WORD sockVersion = MAKEWORD(minorVer, majorVer); if(::WSAStartup(sockVersion, &wsaData) != 0) { exit(0); } } ~CinitSock() { ::WSACleanup(); } };
TCPClient.cpp文件: #include"initsock.h" #include<stdio.h> #include<iostream> #include<string> #include<time.h> CinitSock initsock; #define BUF_SIZE 1024
int main() { SOCKET sHost; //與服務器進行通信的socket sockaddr_in servAddr; //服務器地址 char buf[BUF_SIZE]; //用於接受客戶端數據的緩沖區 int retVal; //調用各種socket函數的返回值 printf("*****************************************\n"); printf(" 客戶端 \n"); printf("*****************************************\n"); //###################創建TCP套接字############################## sHost = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); if(sHost == INVALID_SOCKET) { printf("socket error!\n"); WSACleanup(); return -1; } // 也可以在這里調用bind函數綁定一個本地地址 // 否則系統將會自動安排 // 填寫遠程地址信息 servAddr.sin_family = AF_INET; servAddr.sin_port = htons(9990); // 注意,這里要填寫服務器程序(TCPServer程序)所在機器的IP地址 // 如果你的計算機沒有聯網,直接使用127.0.0.1即可 servAddr.sin_addr.S_un.S_addr = inet_addr("10.115.5.62");
//######################連接服務器############################### retVal = connect(sHost,(LPSOCKADDR)&servAddr,sizeof(servAddr)); if(SOCKET_ERROR==retVal) { printf(" Failed connect!\n"); closesocket(sHost); WSACleanup(); return -1; } printf("連接成功,可以進行通信! \n");
//#########循環向服務器發送字符串,並接收服務器回復的信息########### char c; printf("請選擇功能:C.聊天(Chat) G.游戲:猜數字(Game) L.應用:判斷是否為閏年(Leap year):\n"); scanf("%c",&c); switch(c) { case'C': { //####################聊天功能################## printf("歡迎您!您可以開始聊天啦!\n"); printf("請先輸入C:\n"); char buf1[BUF_SIZE]; scanf("%s",&buf1); send(sHost,buf1,BUF_SIZE,0); //向服務器發送數據 while(true) { //向服務器發送數據 printf("請向服務器發送數據:"); char buf2[BUF_SIZE]; scanf("%s",&buf2); //接收輸入的數據 retVal=send(sHost,buf2,BUF_SIZE,0);//向服務器端發送數據 ZeroMemory(buf2,BUF_SIZE); //清空發送數據的緩沖區 if(SOCKET_ERROR == retVal) { printf("Failed send! \n"); closesocket(sHost); WSACleanup(); return 1; }
//獲取當前系統時間 SYSTEMTIME st; GetLocalTime(&st); char sDateTime[30]; sprintf(sDateTime,"%4d-%2d-%2d %2d:%2d:%2d",st.wYear,st.wMonth,st.wDay,st.wHour,st.wMinute,st.wSecond); //接收服務器回傳的數據 ZeroMemory(buf,BUF_SIZE);//清空接收數據的緩沖區 retVal=recv(sHost,buf,sizeof(buf)+1,0); printf("%s,收到來自服務器的數據[%s:%d] :%s\n",sDateTime, inet_ntoa(servAddr.sin_addr),servAddr.sin_port,buf); //如果收到“quit”,則退出 if(strcmp(buf,"quit")==0) { retVal=send(sHost,"quit",strlen("quit"),0); break; } }break; }break;
case'G': { //######################游戲功能####################### printf("歡迎您!您可以開始游戲啦!\n"); printf("請先輸入G:\n"); char buf3[BUF_SIZE]; scanf("%s",&buf3); send(sHost,buf3,BUF_SIZE,0); printf("請輸入一個1~1000之間的數:\n"); while(true) { char buf4[BUF_SIZE]; int m; scanf("%d",&m); itoa(m, buf4, 10);//sprintf(buf4,"%d",m)與itoa()同義 retVal=send(sHost,buf4,BUF_SIZE,0);//向服務器端發送數字 if(SOCKET_ERROR == retVal) { printf("Failed send! \n"); closesocket(sHost); WSACleanup(); return 1; } //獲取當前系統時間 SYSTEMTIME st; GetLocalTime(&st); char sDateTime[30]; sprintf(sDateTime,"%4d-%2d-%2d %2d:%2d:%2d",st.wYear,st.wMonth,st.wDay,st.wHour,st.wMinute,st.wSecond); //接收服務器回傳的數據 ZeroMemory(buf,BUF_SIZE);//清空接收數據的緩沖區 retVal=recv(sHost,buf,sizeof(buf)+1,0);//接收數據 printf("%s,收到來自服務器的數據[%s:%d] :%s\n", sDateTime, inet_ntoa(servAddr.sin_addr),servAddr.sin_port,buf); //如果收到“0”,則退出 if(strcmp(buf,"0")==0) { retVal=send(sHost,"0",strlen("0"),0); break; } } } case'L': { //################應用:判斷是否為閏年ê################ printf("歡迎您!您可以開始應用啦!\n"); printf("請先輸入L:\n"); char buf5[1024]; scanf("%s",&buf5); //接收輸入的數據 retVal=send(sHost,buf5,1024,0); //向服務器端發送數據 while(true) { char year[1024]; int y; printf("請輸入年份:\n"); scanf("%d",&y); //接收輸入的數據 itoa(y, year, 10);// sprintf(year,"%d",y);//將輸入的十進制數轉換成字符串存儲在緩沖區year中 retVal=send(sHost,year,1024,0); //向服務器端發送年份 ZeroMemory(year,1024); //清空發送數據的緩沖區 if(SOCKET_ERROR == retVal) { printf("Failed send! \n"); closesocket(sHost); WSACleanup(); return 1; }
//獲取當前系統時間 SYSTEMTIME st; GetLocalTime(&st); char sDateTime[30]; sprintf(sDateTime,"%4d-%2d-%2d %2d:%2d:%2d",st.wYear,st.wMonth,st.wDay,st.wHour,st.wMinute,st.wSecond); //接收服務器回傳的數據 ZeroMemory(buf,BUF_SIZE);//清空接收數據的緩沖區 retVal=recv(sHost,buf,sizeof(buf)+1,0);//接收數據 printf("%s,收到來自服務器的數據[%s:%d] :%s\n", sDateTime, inet_ntoa(servAddr.sin_addr),servAddr.sin_port,buf); //如果收到“0”,則退出 if(strcmp(buf,"0")==0) { retVal=send(sHost,"0",strlen("0"),0); break; } } } } printf("正在關閉socket...\n"); //釋放資源 closesocket(sHost); WSACleanup(); //暫停,按任意鍵退出 system("pause"); return 0; } TCPServer.cpp
#include"initsock.h" #include<stdio.h> #include<iostream> #include<string> # include<time.h> #define BUF_SIZE 1024 CinitSock initsock; // 初始化Winsock庫
int main() { SOCKET sListen; //服務器socket,用於監聽客戶端請求 SOCKET sClient; //客戶端socket,用於實現與客戶端的通信 int retVal; //調用各種socke函數的返回值 char buf[BUF_SIZE]; //用於接受客戶端數據的緩沖區 printf("*****************************************\n"); printf(" 服務器端 \n"); printf("*****************************************\n");
// ####################創建TCP套節字####################### sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if(sListen == INVALID_SOCKET) { printf("socket error! \n"); WSACleanup(); return -1; } //指定綁定的地址 struct sockaddr_in addrServ; //定義服務器地址 addrServ.sin_family=AF_INET; addrServ.sin_port = htons(9990); addrServ.sin_addr.S_un.S_addr = htonl(INADDR_ANY); //綁定到socket retVal=bind(sListen,(const struct sockaddr*)&addrServ,sizeof(SOCKADDR_IN)); if(SOCKET_ERROR == retVal) { printf("Failed bind! \n"); closesocket(sListen); WSACleanup(); return -1; } // #########################進入監聽模式####################### retVal=listen(sListen,1); if(SOCKET_ERROR == retVal) { printf("Failed listen! \n"); closesocket(sListen); WSACleanup(); return -1; }
// ####################接受客戶端的連接請求ó######################### printf("TCP Server start...\n"); sockaddr_in addrclient; //客戶端地址 int addrclientlen = sizeof(addrclient); // 接受一個新連接 sClient = accept(sListen, (sockaddr FAR*)&addrclient, &addrclientlen); if(sClient == INVALID_SOCKET) { printf("Failed accept!?"); closesocket(sListen); WSACleanup(); return -1; } printf("連接成功,可以進行通信! \n"); //################循環接受客戶端的數據,並向客戶端發送數據Y######################### ZeroMemory(buf,BUF_SIZE); //清空接收數據的緩沖區 retVal=recv(sClient,buf,BUF_SIZE,0); if(SOCKET_ERROR == retVal) { printf("Failed recv! \n"); closesocket(sListen); closesocket(sClient); WSACleanup(); return -1; } if(strcmp(buf,"C")==0) { //#######################聊天功能########################## printf("對方想要和你聊天!\n"); while(true) { ZeroMemory(buf,BUF_SIZE); //清空接收數據的緩沖區 retVal=recv(sClient,buf,BUF_SIZE,0); if(SOCKET_ERROR == retVal) { printf("Failed recv! \n"); closesocket(sListen); closesocket(sClient); WSACleanup(); return -1; }
//獲取當前系統時間 SYSTEMTIME st; GetLocalTime(&st); char sDateTime[30]; sprintf(sDateTime,"%4d-%2d-%2d %2d:%2d:%2d",st.wYear,st.wMonth,st.wDay,st.wHour,st.wMinute,st.wSecond); //打印輸出的信息 printf("%s,收到來自客戶端的數據 [%s:%d] :%s\n",sDateTime, inet_ntoa(addrclient.sin_addr),addrclient.sin_port,buf); //如果收到“quit”字符串,則退出 if(strcmp(buf,"quit")==0) { retVal=send(sClient,"quit",strlen("quit"),0); break; }
//向客戶端發送數據 printf("請向客戶端發送數據:"); std::string str; //接收輸入的數據 std::getline(std::cin,str); //將用戶輸入的信息復制到buf中 ZeroMemory(buf,BUF_SIZE); //清空發送數據的緩沖區 strcpy(buf,str.c_str()); //向客戶端發送數據 retVal=send(sClient,buf,strlen(buf),0); //向客戶端發送回顯字符串 if(SOCKET_ERROR == retVal) { printf("Failed send! \n"); closesocket(sClient); WSACleanup(); return -1; } } } if(strcmp(buf,"G")==0) { //##################游戲功能############################## printf("對方想要開始游戲!\n"); int num; srand(time(NULL)); num=1+(rand()%1000); //隨機產生一個整數 printf("隨機產生一個數:%d\n",num);
while(true) { ZeroMemory(buf,BUF_SIZE); //清空接收數據的緩沖區 retVal=recv(sClient,buf,BUF_SIZE,0);//接收來自客戶端的數據 if(SOCKET_ERROR == retVal) { printf("Failed recv! \n"); closesocket(sListen); closesocket(sClient); WSACleanup(); return -1; } //獲取當前系統時間 SYSTEMTIME st; GetLocalTime(&st); char sDateTime[30]; sprintf(sDateTime,"%4d-%2d-%2d %2d:%2d:%2d",st.wYear,st.wMonth,st.wDay,st.wHour,st.wMinute,st.wSecond); //打印輸出的信息 printf("%s,收到來自客戶端的數據[%s:%d] :%s\n",sDateTime, inet_ntoa(addrclient.sin_addr),addrclient.sin_port,buf); //如果收到“0”,則退出 if(strcmp(buf,"0")==0) { retVal=send(sClient,"0",strlen("0"),0); break; }
char buf0[BUF_SIZE]="太棒了!你已經猜到已了正確的數!結束游戲請輸入0\n"; char buf2[BUF_SIZE]="太低了,請重新輸入一個1~1000之間的數:\n"; char buf3[BUF_SIZE]="太高了,請重新輸入一個1~1000之間的數:\n"; int n; n = atoi(buf);//將緩沖區接收到的字符串轉成整型 if(num==n) { //向客戶端發送數據 retVal=send(sClient,buf0,strlen(buf0),0); //向客戶端發送回顯字符串 if(SOCKET_ERROR == retVal) { printf("Failed send! \n"); closesocket(sClient); WSACleanup(); return -1; } } else if(n<num) { retVal=send(sClient,buf2,strlen(buf2),0); //向客戶端發送回顯字符串 if(SOCKET_ERROR == retVal) { printf("Failed send! \n"); closesocket(sClient); WSACleanup(); return -1; } } else { retVal=send(sClient,buf3,strlen(buf3),0); //向客戶端發送回顯字符串 ZeroMemory(buf3,BUF_SIZE); //清空發送數據的緩沖區 if(SOCKET_ERROR == retVal) { printf("Failed send! \n"); closesocket(sClient); WSACleanup(); return -1; } } } } if(strcmp(buf,"L")==0) { printf("對方想實現應用:判斷年份是否為閏年!\n"); while(true) { ZeroMemory(buf,BUF_SIZE); //清空接收數據的緩沖區 retVal=recv(sClient,buf,BUF_SIZE,0);//接收來自客戶端的數據 if(SOCKET_ERROR == retVal) { printf("Failed recv! \n"); closesocket(sListen); closesocket(sClient); WSACleanup(); return -1; } //獲取當前系統時間 SYSTEMTIME st; GetLocalTime(&st); char sDateTime[30]; sprintf(sDateTime,"%4d-%2d-%2d %2d:%2d:%2d",st.wYear,st.wMonth,st.wDay,st.wHour,st.wMinute,st.wSecond); //打印輸出的信息 printf("%s,收到來自客戶端的數據[%s:%d] :%s\n",sDateTime, inet_ntoa(addrclient.sin_addr),addrclient.sin_port,buf); //如果收到“0”,則退出 if(strcmp(buf,"0")==0) { retVal=send(sClient,"0",strlen("0"),0); break; } char buf1[1024]="是閏年!\n"; char buf2[1024]="不是閏年!\n"; int year; year = atoi(buf); //判斷是否為閏年 if(year%4==0 && year%100!=0 || year%400==0) { retVal=send(sClient,buf1,strlen(buf1),0); //向客戶端發送回顯字符串 if(SOCKET_ERROR == retVal) { printf("Failed send! \n"); closesocket(sClient); WSACleanup(); return -1; } } else { retVal=send(sClient,buf2,strlen(buf2),0); //向客戶端發送回顯字符串 if(SOCKET_ERROR == retVal) { printf("Failed send! \n"); closesocket(sClient); WSACleanup(); return -1; } } } } //釋放socket printf("正在關閉socket...\n"); closesocket(sListen); closesocket(sClient); WSACleanup(); //暫停,按任意鍵退出 system("pause"); return 0; }
基於數據報式套接字UDP源代碼: UDPClient.cpp #include<WINSOCK2.H> #include<iostream> #include<string> #include <tchar.h> #pragma comment(lib,"WS2_32.lib")
int _tmain(int argc, _TCHAR* argv[]) { WSADATA wsaData; //WSADATA變量,用於初始化Windows Socket SOCKET SendSocket; //發送消息的socket //接收消息的socket sockaddr_in RecvAddr; //服務器端地址 sockaddr_in SenderAddr; //發送者的地址 int port = 27015; //服務器端監聽地址 int BufLen=1024; //緩沖區大小 int retVal; //調用各種socket函數的返回值 int SenderAddrSize=sizeof(SenderAddr); printf("*****************************************\n"); printf(" 客戶端 \n"); printf("*****************************************\n"); //###################初始化socket############################ WSAStartup(MAKEWORD(2,2),&wsaData); //創建接收數據報的socket SendSocket=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if(INVALID_SOCKET==SendSocket) { printf(" socket error! \n"); WSACleanup(); return -1; } //########################設置服務器地址####################### RecvAddr.sin_family=AF_INET; RecvAddr.sin_port = htons(27015); RecvAddr.sin_addr.S_un .S_addr = inet_addr("127.0.0.1"); //###########################向服務器發送、接收數據報################# printf("正在向服務器發送數據...\n");
char c; printf("請選擇功能:C.聊天(Chat) G.游戲:猜數字(Game) L.應用:判斷是否為閏年(Leap year):\n"); scanf("%c",&c); switch(c) { case'C': { //#########################聊天功能##################### printf("歡迎您!您可以開始聊天啦!\n"); printf("請先輸入C:\n"); char buf1[1024]; scanf("%s",&buf1); //接收輸入的數據 retVal=sendto(SendSocket,buf1,1024,0,(SOCKADDR *)&RecvAddr,sizeof(RecvAddr));//向服務器發送一個C while(true) { //向服務器發送數據 printf("請向服務器發送數據:"); char buf2[1024]; scanf("%s",&buf2); //接收輸入的數據 //向服務器發送數據 retVal=sendto(SendSocket,buf2,1024,0,(SOCKADDR *)&RecvAddr,sizeof(RecvAddr)); ZeroMemory(buf2,1024); //清空發送數據的緩沖區 if(SOCKET_ERROR == retVal) { printf("Failed send! \n"); closesocket(SendSocket); WSACleanup(); return 1; } char SendBuf2[1024]; //獲取當前系統時間 SYSTEMTIME st; GetLocalTime(&st); char sDateTime[30]; sprintf(sDateTime,"%4d-%2d-%2d %2d:%2d:%2d",st.wYear,st.wMonth,st.wDay,st.wHour,st.wMinute,st.wSecond); //接收服務器回傳的數據 ZeroMemory(SendBuf2,1024); //清空接收數據的緩沖區 retVal=recvfrom(SendSocket,SendBuf2,BufLen,0,(SOCKADDR *)&SenderAddr,&SenderAddrSize); printf("%s,收到來自服務器端的數據[%s:%d] :%s\n", sDateTime, inet_ntoa(SenderAddr.sin_addr),SenderAddr.sin_port,SendBuf2); //如果收到“quit”,則退出 if(strcmp(SendBuf2,"quit")==0) { retVal = send(SendSocket,"quit",strlen("quit"),0); break; } }break; }
case'G': { //#####################游戲功能##################### printf("歡迎您!您可以開始游戲啦!\n"); printf("請先輸入G:\n"); char buf3[1024]; scanf("%s",&buf3); retVal=sendto(SendSocket,buf3,1024,0,(SOCKADDR *)&RecvAddr,sizeof(RecvAddr));//向服務器發送一個G printf("請輸入一個1~1000之間的數:\n"); while(true) { char buf4[1024]; int m; scanf("%d",&m); itoa(m, buf4, 10);//sprintf(buf4,"%d",m);與itoa()同義 retVal=sendto(SendSocket,buf4,1024,0,(SOCKADDR *)&RecvAddr,sizeof(RecvAddr)); ZeroMemory(buf4,1024); //清空發送數據的緩沖區 if(SOCKET_ERROR == retVal) { printf("Failed send! \n"); closesocket(SendSocket); WSACleanup(); return 1; } char SendBuf2[1024]; //獲取當前系統時間 SYSTEMTIME st; GetLocalTime(&st); char sDateTime[30]; sprintf(sDateTime,"%4d-%2d-%2d %2d:%2d:%2d",st.wYear,st.wMonth,st.wDay,st.wHour,st.wMinute,st.wSecond); //接收服務器回傳的數據 ZeroMemory(SendBuf2,1024); //清空接收數據的緩沖區 retVal=recvfrom(SendSocket,SendBuf2,BufLen,0,(SOCKADDR *)&SenderAddr,&SenderAddrSize); printf("%s,接收到來自服務器端的數據[%s:%d] :%s\n",sDateTime, inet_ntoa(SenderAddr.sin_addr),SenderAddr.sin_port,SendBuf2); //如果收到“0”,則退出 if(strcmp(SendBuf2,"0")==0) { retVal = send(SendSocket,"0",strlen("0"),0); break; } } } case'L': { //###############應用:判斷是否為閏年############## printf("歡迎您!您可以開始應用啦!\n"); printf("請先輸入L?:\n"); char buf6[1024]; scanf("%s",&buf6); //接收輸入的數據 //向服務器發送數據 retVal=sendto(SendSocket,buf6,1024,0,(SOCKADDR *)&RecvAddr,sizeof(RecvAddr)); while(true) { char year[1024]; int y; printf("請輸入年份:\n"); scanf("%d",&y); //接收輸入的數據 sprintf(year,"%d",y);//itoa(y, year, 10); //將輸入的十進制數轉換成字符串存儲在緩沖區year中 retVal=sendto(SendSocket,year,1024,0,(SOCKADDR *)&RecvAddr,sizeof(RecvAddr)); ZeroMemory(year,1024); //清空發送數據的緩沖區 if(SOCKET_ERROR == retVal) { printf("Failed send! \n"); closesocket(SendSocket); WSACleanup(); return 1; }
//獲取當前系統時間 SYSTEMTIME st; GetLocalTime(&st); char sDateTime[30]; sprintf(sDateTime,"%4d-%2d-%2d %2d:%2d:%2d",st.wYear,st.wMonth,st.wDay,st.wHour,st.wMinute,st.wSecond); //接收服務器回傳的數據 char SendBuf2[1024]; ZeroMemory(SendBuf2,1024);//清空接收數據的緩沖區 retVal=recvfrom(SendSocket,SendBuf2,BufLen,0,(SOCKADDR *)&SenderAddr,&SenderAddrSize); printf("%s,收到來自服務器端的數據[%s:%d] :%s\n",sDateTime, inet_ntoa(SenderAddr.sin_addr),SenderAddr.sin_port,SendBuf2); } } } //發送完成,關閉socket printf("正在關閉socket...\n"); closesocket(SendSocket); // 釋放資源,並退出 printf("退出!"); WSACleanup(); return 0; } UDPServer.cpp #include<WINSOCK2.H> #include<iostream> #include<string> #include <tchar.h> #include<time.h> #pragma comment(lib,"WS2_32.lib")
int _tmain(int argc, _TCHAR* argv[]) { WSADATA wsaData; //WSADATA變量,用於初始化Windows Socket SOCKET RecvSocket; //接收消息的socket//發送消息的socket sockaddr_in RecvAddr; //服務器端地址 sockaddr_in SenderAddr; //發送者的地址 int port = 27015; //服務器端監聽地址 char RecvBuf[1024]; //接收數據的緩沖區 int BufLen=1024; //緩沖區大小 int retVal; //調用各種socket函數的返回值 int SenderAddrSize=sizeof(SenderAddr); printf("*****************************************\n"); printf(" 服務器端 \n"); printf("*****************************************\n"); //#########################初始化socket######################## WSAStartup(MAKEWORD(2,2),&wsaData); //創建接收數據報的socket RecvSocket=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if( INVALID_SOCKET == RecvSocket) { printf("socket error! \n"); WSACleanup(); return -1; } //#############將socket與指定端口0.0.0.0綁定################### RecvAddr.sin_family=AF_INET; RecvAddr.sin_port = htons(27015); RecvAddr.sin_addr .S_un .S_addr=inet_addr("127.0.0.1"); retVal=bind(RecvSocket,(SOCKADDR *)&RecvAddr,sizeof(RecvAddr)); if(SOCKET_ERROR == retVal) { printf("Failed bind! \n"); closesocket(RecvSocket); WSACleanup(); return -1; } printf("正y在ú接ó收?數簓據Y...\n"); //#############循環向客戶端接收、發送的數據################### //調用recvfrom()函數在綁定的socke上接收數據 retVal=recvfrom(RecvSocket,RecvBuf,BufLen,0,(SOCKADDR *)&SenderAddr,&SenderAddrSize); if(retVal == INVALID_SOCKET) { printf("Failed recvfrom!"); closesocket(RecvSocket); WSACleanup(); return -1; } //################################聊天功能####################### if(strcmp(RecvBuf,"C")==0) { printf("對方想要和你聊天!\n"); while(true) { ZeroMemory(RecvBuf,1024); //清空接收數據的緩沖區 //調用recvfrom()函數在綁定的socket上接數據 retVal=recvfrom(RecvSocket,RecvBuf,BufLen,0,(SOCKADDR *)&SenderAddr,&SenderAddrSize); if(retVal == INVALID_SOCKET) { printf("Failed recvfrom!"); closesocket(RecvSocket); WSACleanup(); return -1; } //獲取當前系統時間 SYSTEMTIME st; GetLocalTime(&st); char sDateTime[30]; sprintf(sDateTime,"%4d-%2d-%2d %2d:%2d:%2d",st.wYear,st.wMonth,st.wDay,st.wHour,st.wMinute,st.wSecond); //打印輸出的信息 printf("%s,收到來自客戶端的數據 [%s:%d] :%s\n",sDateTime, inet_ntoa(SenderAddr.sin_addr),SenderAddr.sin_port,RecvBuf); //如果收到“quit”,則退出 if(strcmp(RecvBuf,"quit") == 0) { retVal = send(RecvSocket,"quit",strlen("quit"),0); break; } //向客戶端發送數據 char RecvBuf2[1024]; printf("請向客戶端發送數據:"); std::string str; //接收輸入的數據 std::getline(std::cin,str); //將用戶輸入的信息復制到buf中 strcpy(RecvBuf2,str.c_str()); retVal=sendto(RecvSocket,RecvBuf2,BufLen,0,(SOCKADDR *)&SenderAddr,sizeof(SenderAddr)); ZeroMemory(RecvBuf2,1024); //清空發送數據的緩沖區 if(SOCKET_ERROR == retVal) { printf("Failed send! \n"); closesocket(RecvSocket); WSACleanup(); return -1; } } }
if(strcmp(RecvBuf,"G")==0) { //###################游戲功能########################### printf("對方想要開始游戲!\n"); int num; srand(time(NULL)); num=1+(rand()%1000); //隨機產生一個整數 printf("隨機產生一個數:%d\n",num);
while(true) { ZeroMemory(RecvBuf,1024); //清空接收數據的緩沖區 //調用recvfrom()函數在綁定的socke上接收數據 retVal=recvfrom(RecvSocket,RecvBuf,BufLen,0,(SOCKADDR *)&SenderAddr,&SenderAddrSize); if(retVal == INVALID_SOCKET) { printf("Failed recvfrom!"); closesocket(RecvSocket); WSACleanup(); return -1; } //獲取當前系統時間 SYSTEMTIME st; GetLocalTime(&st); char sDateTime[30]; sprintf(sDateTime,"%4d-%2d-%2d %2d:%2d:%2d",st.wYear,st.wMonth,st.wDay,st.wHour,st.wMinute,st.wSecond); //打印輸出的信息 printf("%s,收到來自客戶端的數據[%s:%d] :%s\n",sDateTime, inet_ntoa(SenderAddr.sin_addr),SenderAddr.sin_port,RecvBuf); //如果收到“0”,則退出 if(strcmp(RecvBuf,"0") == 0) { retVal = send(RecvSocket,"0",strlen("0"),0); break; } char buf0[1024]="太棒了!你已經猜到已了正確的數!結束游戲請輸入0\n"; char buf2[1024]="太低了,重新輸入一個1~1000之間的數:\n"; char buf3[1024]="太高了,重新輸入一個1~1000之間的數:\n";
int n; n = atoi(RecvBuf);//將緩沖區接收到的字符串轉成整型 if(num==n) { //向客戶端發送數據 retVal=sendto(RecvSocket,buf0,BufLen,0,(SOCKADDR *)&SenderAddr,sizeof(SenderAddr)); ZeroMemory(buf0,1024); //清空發送數據的緩沖區 if(SOCKET_ERROR == retVal) { printf("Failed send! \n"); closesocket(RecvSocket); WSACleanup(); return -1; } } else if(n<num) { retVal=sendto(RecvSocket,buf2,BufLen,0,(SOCKADDR *)&SenderAddr,sizeof(SenderAddr)); ZeroMemory(buf2,1024); //清空發送數據的緩沖區 if(SOCKET_ERROR == retVal) { printf("Failed send! \n"); closesocket(RecvSocket); WSACleanup(); return -1; } } else { retVal=sendto(RecvSocket,buf3,BufLen,0,(SOCKADDR *)&SenderAddr,sizeof(SenderAddr)); ZeroMemory(buf3,1024); if(SOCKET_ERROR == retVal) { printf("Failed send! \n"); closesocket(RecvSocket); WSACleanup(); return -1; } } } } //###################應用:判斷是否為閏年################# if(strcmp(RecvBuf,"L")==0) { printf("對方想實現應用:判斷年份是否為閏年!\n"); while(true) { ZeroMemory(RecvBuf,1024); //調用recvfrom()函數在綁定的socket上接收數據 retVal=recvfrom(RecvSocket,RecvBuf,BufLen,0,(SOCKADDR *)&SenderAddr,&SenderAddrSize); if(retVal == INVALID_SOCKET) { printf("Failed recvfrom!"); closesocket(RecvSocket); WSACleanup(); return -1; } //獲取當前系統時間 SYSTEMTIME st; GetLocalTime(&st); char sDateTime[30]; sprintf(sDateTime,"%4d-%2d-%2d %2d:%2d:%2d",st.wYear,st.wMonth,st.wDay,st.wHour,st.wMinute,st.wSecond); //打印輸出的信息 printf("%s,收到來自客戶端的數據[%s:%d] :%s\n",sDateTime, inet_ntoa(SenderAddr.sin_addr),SenderAddr.sin_port,RecvBuf); //如果收到“0”,則退出 if(strcmp(RecvBuf,"0") == 0) { retVal = send(RecvSocket,"0",strlen("0"),0); break; }
char buf1[1024]="是?閏è?年ê!?\n"; char buf2[1024]="不?是?閏è?年ê!?\n"; int year; year = atoi(RecvBuf);
//判斷是否為閏年 if(year%4==0 && year%100!=0 || year%400==0) { retVal=sendto(RecvSocket,buf1,BufLen,0,(SOCKADDR *)&SenderAddr,sizeof(SenderAddr)); //向客戶端發送回顯字符串 if(SOCKET_ERROR == retVal) { printf("Failed send! \n"); closesocket(RecvSocket); WSACleanup(); return -1; } } else { retVal=sendto(RecvSocket,buf2,BufLen,0,(SOCKADDR *)&SenderAddr,sizeof(SenderAddr));//向客戶端發送回顯字符串 if(SOCKET_ERROR == retVal) { printf("Failed send! \n"); closesocket(RecvSocket); WSACleanup(); return -1; } } } } //關閉socket,結束接收數據 printf("正在關閉socket...\n"); closesocket(RecvSocket); //釋放資源,退出 printf("退出!"); WSACleanup(); return 0; }
五、實驗總結與心得: 本次實驗聊天功能比較容易實現,編寫游戲和應用時出現了很多問題,如下: 1.發送數據后會收到很多“燙”。解決辦法:在發送后,接收前都加上ZeroMemory(),清空緩沖區,就不會發生字符串溢出的現象;另外,如果將ZeroMemory()放在了send()之前,就會清空輸入的內容,接收到的消息則為空,如果將ZeroMemory()放在recv()之后,就會清空接收到的內容,無法對此數據進行接下來的操作。 2.進行游戲:猜數字時,只能判斷一次,第二次不能輸出結果。解決辦法:仔細梳理流程,就會發現因為在服務器端的if語句前多加了一個循環體while,導致第二次無法正常接收語句。 3.使用int _tmain(int argc, _TCHAR* argv[])時,要加上語句#include<time.h>。 4.在游戲和應用中出現字符串和整型之間的相互轉換問題。解決辦法:用itoa()將整型轉成字符串,再發送數據;用atoi()將字符串轉成整型,再進行接下來的操作。Sprintf()也可以將整型轉換成字符串。 以上是此次實驗出現的主要問題,也存在其他小問題,比如地址錯誤、死循環等等,這些都需要仔細查看,理清思路,所有問題就會迎刃而解。
|