高级Socket编程技术

高级Socket编程技术

实验目的
1.了解阻塞与非阻塞模式Socket编程的工作原理。
2.了解Socket编程的5种模型的工作原理和使用方法。
3.掌握Select模型和WSAAsyncSelect模型的网络应用程序编程。
二、实验内容
1.参考教材编写工作于非阻塞模式的多线程TCP服务器应用程序和工作于非阻塞模式客户端应用程序,并进行数据收发测试。
2.编写基于WSAAsyncSelect模型、工作于并发方式的基于对话框的TCP服务器程序和客户端程序,并进行数据收发测试。
在这里插入图片描述

基于WSAAsyncSelect模型的服务器程序
在这里插入图片描述

基于WSAAsyncSelect模型的客户端程序
要求:(1)客户端程序和服务器端程序之间都能够多次发送消息给对方,也能够多次接收对方发送的消息。(2)将上述1小题的全部代码附在附录上,将2小题源代码打包和实验报告一并提交。
三、实验原理
1.非阻塞模式
概念
Ioctlsocket
函数功能是控制套接口的模式,可用于任一状态的任一套接口,可以调用次函数来讲Socket设置为非阻塞模式。
CreateThread函数
用于创建于一个专门的通信线程,实现多客户端通信。
WSAEWOULDBLOCK
Winsock异常 WSAEWOULDBLOCK说明Output Buffer 已经满了,无法再写入数据
2. WSAAsyncSelect模型工作原理
WSAAsyncSelect模型是Windows Sockets的一个异步I/O模型。应用程序可以用它在一个套接字上接收以Windows消息为基础的网络事件。应用程序创建套接字后,调用WSAAsyncSelect()函数注册感兴趣的网络事件,当事件发生时,windows窗口接收到消息,然后程序就可以对收到的网络事件进行处理。WSAAsyncSelect模型是非阻塞的。应用程序在调用recv()函数接收数据之前,调用WSAAsyncSelect()函数注册网络事件。WSAAsyncSelect()函数立即返回,线程继续运行。当系统中数据准备好时,向应用程序发送消息。程序接收到消息后调用recv()函数接收数据。
WSAAsyncSelect函数编程方法步骤
1)初始化套接字相关信息:
(2)开始启动一个事件通知。WSAAsyncSelect(Sock,hWnd,自定义消息,网络事件)
(3)响应窗口的自定义消息处理函数,其中lparam的高位字包含了可能出现的错误
代码,低字节表示发生的网络事件。wParam表示发生网络事件的套接字。
WSAGETSELECTERROR(lParam);查看是否出现错误,获取低字节位
WSAGETSELECTEVENT(lParam);查看发生了什么事件,获取高字节位
事件种类请查看MSDN,可用WSAGetLastError()来获取错误信息。
四、实验步骤
1.非阻塞模式下的客服端和服务器端信息的收发测试,如图,客户端1在1588端口上向服务器端发送数据,客户端2在3124端口上向服务器端发送数据。处于非阻塞状态。
在这里插入图片描述

在这里插入图片描述

  1. 基于WSAAsyncSelect模型、工作于并发方式的基于对话框的TCP服务器程序和客户端程序,并进行数据收发测试。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

五、实验小结
附:程序源代码
服务器(窗口)

#include "stdafx.h"
#include "Exp07_AsyncServer.h"

#include<WINSOCK2.h>
#pragma comment(lib,"ws2_32.lib")

#define MAX_LOADSTRING 100

#define WM_SOCKET WM_USER+0x10    //①自定义socket消息

// 全局变量:
HINSTANCE hInst;								// 当前实例
TCHAR szTitle[MAX_LOADSTRING];					// 标题栏文本
TCHAR szWindowClass[MAX_LOADSTRING];			// 主窗口类名

SOCKET sockSer,sockConn;			//服务器端要创建两个套接字
SOCKADDR_IN addrSer, addrCli;
int len =sizeof(SOCKADDR);
char sendbuf[256],recvbuf[256];
char clibuf[999]="客户端: >",serbuf[999]="服务器: >";

// 此代码模块中包含的函数的前向声明:
BOOL				InitInstance(HINSTANCE, int);
INT_PTR CALLBACK	AsyncSrvProc(HWND, UINT, WPARAM, LPARAM);

int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPTSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
	UNREFERENCED_PARAMETER(hPrevInstance);
	UNREFERENCED_PARAMETER(lpCmdLine);

 	// TODO: 在此放置代码。
	MSG msg;
	HACCEL hAccelTable;

	// 初始化全局字符串
	LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
	LoadString(hInstance, IDC_EXP07_ASYNCSERVER, szWindowClass, MAX_LOADSTRING);
	
	// 执行应用程序初始化:
	if (!InitInstance (hInstance, nCmdShow))
	{
		return FALSE;
	}

	hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_EXP07_ASYNCSERVER));

	// 主消息循环:
	while (GetMessage(&msg, NULL, 0, 0))
	{
		if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}

	return (int) msg.wParam;
}


//
//   函数: InitInstance(HINSTANCE, int)
//
//   目的: 保存实例句柄并创建主窗口
//
//   注释:
//
//        在此函数中,我们在全局变量中保存实例句柄并
//        创建和显示主程序窗口。
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   HWND hWnd;

   hInst = hInstance; // 将实例句柄存储在全局变量中

   hWnd =CreateDialog(hInstance, MAKEINTRESOURCE(IDD_SERVER), GetDesktopWindow(), AsyncSrvProc);

   if (!hWnd)
   {
      return FALSE;
   }

   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   return TRUE;
}



// “关于”框的消息处理程序。
INT_PTR CALLBACK AsyncSrvProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	UNREFERENCED_PARAMETER(lParam);
	char ip[16],port[5];	
	switch (message)
	{
	case WM_INITDIALOG:
		SetDlgItemText(hDlg,IDC_IP,"127.0.0.1");	//设置ip文本框的内容
		SetDlgItemText(hDlg,IDC_PORT,"5566");	//设置端口文本框的内容
		WSADATA wsaData;
		if(WSAStartup(MAKEWORD(2,2), &wsaData))	{
			MessageBox(hDlg,"Winsock加载失败","警告",0);
			WSACleanup();
			return (INT_PTR)TRUE;
		}

	case WM_SOCKET:   //三:自定义消息	
		if(WSAGETSELECTERROR(lParam))
		{
			MessageBoxA(NULL, "Socket错误", "tip", 0); 	
		}
		else{
		switch (WSAGETSELECTEVENT(lParam)) {	//选择要处理的事件
		case FD_ACCEPT:{   //接收请求事件				
			sockConn=accept(sockSer,(SOCKADDR*) &addrCli,&len);	
			WSAAsyncSelect(sockConn, hDlg,WM_SOCKET,FD_READ | FD_CLOSE); 
		    }
			break;
		case FD_READ:  {  		//可读事件			
			recv(sockConn,recvbuf,256,0); 	//接收消息
			strcat_s(clibuf,recvbuf);
			//将接收到的消息添加到列表框中
			SendDlgItemMessage(hDlg,IDC_RECVBUF,LB_ADDSTRING,0,(LPARAM)clibuf);
			strcpy_s(clibuf, "客户端: >"); } 	//重新给字符串赋值			
			break;			
		case FD_CLOSE: 	{ //关闭连接事件			
			MessageBoxA(NULL, "正常关闭连接", "tip", 0); 	}					
			break;		
		}//switch (WSAGETSELECTEVENT(lParam)) 
		}//else
        break;

	case WM_COMMAND:
		switch(LOWORD(wParam) ){ 
		case IDC_QUIT: 	//单击了退出按钮
			EndDialog(hDlg, LOWORD(wParam));
			closesocket(sockSer);	//关闭套接字	
			WSACleanup();		//卸载WinSock协议栈
			return TRUE;			
		case IDC_CREATE:		//单击了创建服务器按钮
			GetDlgItemText(hDlg,IDC_IP,ip,16);	//获取编辑框中的IP值
			GetDlgItemText(hDlg,IDC_PORT,port,5);
			EnableWindow(GetDlgItem(hDlg,IDC_CREATE),FALSE); //禁用按钮
			sockSer=socket(AF_INET,SOCK_STREAM,0);
			//②设置异步方式
			WSAAsyncSelect(sockSer, hDlg,WM_SOCKET,FD_ACCEPT); 
			addrSer.sin_family=AF_INET;
			addrSer.sin_port=htons(atoi(port));
			addrSer.sin_addr.S_un.S_addr=inet_addr(ip);
			bind(sockSer,(SOCKADDR*) &addrSer,len); 	//绑定套接字
			listen(sockSer,5);			//监听				
			break;
		case IDC_SEND:				//单击了发送按钮
			GetDlgItemText(hDlg,IDC_SENDBUF,sendbuf,256);
			send(sockConn,sendbuf,strlen(sendbuf)+1,0);	//发送消息
			SetDlgItemText(hDlg,IDC_SENDBUF,NULL);		//清空编辑框
			strcat_s(serbuf,sendbuf);
		    //将已发送的消息添加到列表框中
			SendDlgItemMessage(hDlg,IDC_RECVBUF,LB_ADDSTRING,0,(LPARAM)serbuf);
			strcpy_s(serbuf, "服务器: >");
			break;				
		}	//switch(LOWORD(wParam) )	
	}//switch (message)	

	return (INT_PTR)FALSE;
}

客户端(窗口)

#include "stdafx.h"
#include "Exp07_AsyncClient.h"

#include<WINSOCK2.h>
#pragma comment(lib,"ws2_32.lib")

#define WM_SOCKET WM_USER+0x10    //①自定义socket消息

#define MAX_LOADSTRING 100

// 全局变量:
HINSTANCE hInst;								// 当前实例
TCHAR szTitle[MAX_LOADSTRING];					// 标题栏文本
TCHAR szWindowClass[MAX_LOADSTRING];			// 主窗口类名

SOCKET sockCli;			//声明客户端套接字
SOCKADDR_IN addrSer;

// 此代码模块中包含的函数的前向声明:
BOOL				InitInstance(HINSTANCE, int);
INT_PTR CALLBACK	AsyncClientProc(HWND, UINT, WPARAM, LPARAM);

int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPTSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
	UNREFERENCED_PARAMETER(hPrevInstance);
	UNREFERENCED_PARAMETER(lpCmdLine);

 	// TODO: 在此放置代码。
	MSG msg;
	HACCEL hAccelTable;

	// 初始化全局字符串
	LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
	LoadString(hInstance, IDC_EXP07_ASYNCCLIENT, szWindowClass, MAX_LOADSTRING);	

	// 执行应用程序初始化:
	if (!InitInstance (hInstance, nCmdShow))
	{
		return FALSE;
	}

	hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_EXP07_ASYNCCLIENT));

	// 主消息循环:
	while (GetMessage(&msg, NULL, 0, 0))
	{
		if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}

	return (int) msg.wParam;
}



//
//   函数: InitInstance(HINSTANCE, int)
//
//   目的: 保存实例句柄并创建主窗口
//
//   注释:
//
//        在此函数中,我们在全局变量中保存实例句柄并
//        创建和显示主程序窗口。
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   HWND hWnd;

   hInst = hInstance; // 将实例句柄存储在全局变量中

   hWnd =CreateDialog(hInstance, MAKEINTRESOURCE(IDD_ASYNCCLIENT), GetDesktopWindow(), AsyncClientProc);

   if (!hWnd)
   {
      return FALSE;
   }

   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   return TRUE;
}


// “关于”框的消息处理程序。
INT_PTR CALLBACK AsyncClientProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	UNREFERENCED_PARAMETER(lParam);
	char ip[16], port[5];
	char sendbuf[256],recvbuf[256],ren[10];	
	char serbuf[256]="服务器: >", clibuf[256]="客户端: >";
	int len =sizeof(SOCKADDR);
	int res;

	switch (message){
	case WM_INITDIALOG: 				//对话框初始化
		SetDlgItemText(hDlg,IDC_IP,"127.0.0.1");
		SetDlgItemText(hDlg,IDC_PORT,"5566");
		WSADATA wsaData;
		WSAStartup(MAKEWORD(2,2), &wsaData);		//加载协议栈
		sockCli=socket(AF_INET,SOCK_STREAM,0);		//创建套接字
		//设置异步方式
		WSAAsyncSelect(sockCli, hDlg,WM_SOCKET, FD_READ |FD_CLOSE);	
		break;

	case WM_SOCKET:   //自定义消息		
		switch (WSAGETSELECTEVENT(lParam)){
		case FD_READ: {   //可读事件			
			recv(sockCli,recvbuf,256,0);		//接收信息
			strcat_s(serbuf,recvbuf);
			//将接收到的数据添加到列表框中
			SendDlgItemMessage(hDlg,IDC_RECVBUF,LB_ADDSTRING,0,(LPARAM)serbuf);
			strcpy_s(serbuf, "服务器: >");  }	//重新给字符串赋值			
			break;			
		case FD_CLOSE: {			 //关闭连接事件			
			MessageBoxA(NULL, "正常关闭连接", "tip", 0);		}
			break;		}		
        break;		

	case WM_COMMAND:{	
		switch(LOWORD(wParam))	{ 
		case IDC_QUIT:					//单击了退出按钮
			EndDialog(hDlg, LOWORD(wParam));
			closesocket(sockCli);
			WSACleanup();
			break;		
		case IDC_CONN: 					//单击了“连接服务器”按钮
			GetDlgItemText(hDlg,IDC_IP,ip,16);		//获取ip
			GetDlgItemText(hDlg,IDC_PORT,port,5);
			EnableWindow(GetDlgItem(hDlg,IDC_CONN),FALSE);	//禁用连接按钮
			addrSer.sin_family=AF_INET;
			addrSer.sin_port=htons(atoi(port));
			addrSer.sin_addr.S_un.S_addr=inet_addr(ip);		
			res =connect(sockCli,(SOCKADDR*)&addrSer,sizeof(SOCKADDR));
			//	 itoa(res,ren,10);
			// MessageBox(NULL,ren,"通知",0);	
			if(res ==10035 || res==-1)
				MessageBox(NULL,"客户端连接服务器成功","通知",0);				
			else	
				MessageBox(NULL,"客户端连接服务器失败","警告",0);		
			break;
		
		case IDC_SEND: 		//单击了“发送”按钮
			GetDlgItemText(hDlg,IDC_SENDBUF,sendbuf,256);		//获得发送框的内容
			send(sockCli,sendbuf,strlen(sendbuf)+1,0);		//发送消息
			SetDlgItemText(hDlg,IDC_SENDBUF,"");		//清空发送框
			strcat_s(clibuf,sendbuf);				
			SendDlgItemMessage(hDlg,IDC_RECVBUF,LB_ADDSTRING,0,(LPARAM)clibuf);
			strcpy_s(clibuf, "客户端: >");  //重新给字符串赋值	
			break;
		}//switch(LOWORD(wParam))
		}//case WM_COMMAND:
	}
	return (INT_PTR)FALSE;
}
服务器:

//
#include <afxwin.h>
  
#include "stdafx.h"
#include <WINSOCK2.H>   
#include <iostream>

#pragma comment(lib,"WS2_32.lib")   
#define BUF_SIZE    64      // 缓冲区大小

sockaddr_in addrClient;							// 客户端地址

DWORD  WINAPI  AnswerThread(LPVOID  lparam)  
{  
    char   buf[BUF_SIZE];			// 用于接受客户端数据的缓冲区   
    int     retVal;							// 调用各种Socket函数的返回值   

    SOCKET  sClient=(SOCKET)(LPVOID)lparam;  
 
     // 循环接收客户端的数据,直接客户端发送quit命令后退出。  
	while(true)
	{
		ZeroMemory(buf,BUF_SIZE);						// 清空接收数据的缓冲区
		retVal = recv(sClient,buf,BUFSIZ,0);				// 接收来自客户端的数据,因为是非阻塞模式,所以即使没有数据也会继续
		if(SOCKET_ERROR == retVal)   
		{   
			int err = WSAGetLastError();				// 获取错误编码
			if(err == WSAEWOULDBLOCK)			// 接收数据缓冲区暂无数据
			{
				Sleep(100);
				continue;
			}
			else if(err == WSAETIMEDOUT || err == WSAENETDOWN)
			{
				printf("recv failed !\n");   
				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, Recv From Client [%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;
		}
		else		// 否则向客户端发送回显字符串
		{
			char    msg[BUF_SIZE];  
			sprintf(msg, "Message received - %s", buf); 
			while(true)
			{
				// 向服务器发送数据
				retVal = send(sClient, msg, strlen(msg),0);   
				if(SOCKET_ERROR == retVal)   
				{   
					int err = WSAGetLastError();
					if(err == WSAEWOULDBLOCK)			// 无法立即完成非阻塞套接字上的操作
					{
						Sleep(500);
						continue;
					}
					else
					{
						printf("send failed !\n");   
						closesocket(sClient);   
						WSACleanup();   
						return -1;   
					}
				}
				break;
			}	  
  
		}
	}
	// 关闭套接字
    closesocket(sClient);   
}

int _tmain(int argc, _TCHAR* argv[])
{
	WSADATA wsd;					// WSADATA变量,用于初始化Windows Socket   
    SOCKET  sServer;					// 服务器套接字,用于监听客户端请求
    SOCKET  sClient;					// 客户端套接字,用于实现与客户端的通信   
    int     retVal;							// 调用各种Socket函数的返回值   
  
    // 初始化套接字动态库   
    if(WSAStartup(MAKEWORD(2,2),&wsd) != 0)   
    {   
        printf("WSAStartup failed !\n");   
        return 1;   
    }     
    // 创建用于监听的套接字   
    sServer = socket(AF_INET,SOCK_STREAM, IPPROTO_IP);   
    if(INVALID_SOCKET == sServer)   
    {   
        printf("socket failed !\n");   
        WSACleanup();   
        return -1;   
    }     
	// 设置套接字为非阻塞模式
	int iMode = 1;
	retVal = ioctlsocket(sServer, FIONBIO, (u_long FAR*) &iMode);
	if(retVal == SOCKET_ERROR)
	{
		printf("ioctlsocket failed !\n");
		WSACleanup();
		return -1;
	}
    // 设置服务器套接字地址   
    SOCKADDR_IN addrServ;   
    addrServ.sin_family = AF_INET;   
    addrServ.sin_port = htons(9990);		// 监听端口为9990
    addrServ.sin_addr.S_un.S_addr = htonl(INADDR_ANY);    
    // 绑定套接字sServer到本地地址,端口9990  
    retVal = bind(sServer,(const struct sockaddr*)&addrServ,sizeof(SOCKADDR_IN));   
    if(SOCKET_ERROR == retVal)   
    {   
        printf("bind failed !\n");   
        closesocket(sServer);   
        WSACleanup();   
        return -1;   
    }     
    // 监听套接字   
    retVal = listen(sServer, SOMAXCONN);   
    if(SOCKET_ERROR == retVal)   
    {   
        printf("listen failed !\n");   
        closesocket(sServer);   
        WSACleanup();   
        return -1;   
    }     
	// 接受客户请求   
	printf("TCP Server start...\n");
	int addrClientlen = sizeof(addrClient);   
	// 循环等待
	while(true)
	{
		sClient = accept(sServer,(sockaddr FAR*)&addrClient,&addrClientlen);   
		if(INVALID_SOCKET == sClient)   
		{   
			int err = WSAGetLastError();
			if(err == WSAEWOULDBLOCK)			// 无法立即完成非阻塞套接字上的操作
			{
				Sleep(100);
				continue;
			}
			//else
			//{
			//	//printf("accept failed !\n");   
			//	//closesocket(sServer);   
			//	//WSACleanup();   
			//	//return -1;   
			//	Sleep(100);
			//	continue;
			//}
		} 
		// 创建专用通信线程
		DWORD dwThreadId;
		CreateThread(NULL, NULL, AnswerThread, (LPVOID)sClient, 0, &dwThreadId);
	}
    // 释放套接字   
    closesocket(sServer);   
    WSACleanup();   
	// 暂停,按任意键退出
	system("pause");
	return 0;
}

客户端:
#include "stdafx.h"
#include <winsock2.h>   
#include <string>
#include <iostream>
  
#pragma comment(lib,"WS2_32.lib")   

#define BUF_SIZE    64          // 缓冲区大小  

int _tmain(int argc, _TCHAR* argv[])
{
	WSADATA     wsd;					// 用于初始化Windows Socket   
    SOCKET      sHost;					// 与服务器进行通信的套接字   
    SOCKADDR_IN servAddr;			// 服务器地址   
    char        buf[BUF_SIZE];			// 用于接受数据缓冲区   
    int         retVal;							// 调用各种Socket函数的返回值 

	// 初始化Windows Socket
    if(WSAStartup(MAKEWORD(2,2),&wsd) != 0)   
    {   
        printf("WSAStartup failed !\n");   
        return 1;   
    }     

    // 创建套接字   
    sHost = socket(AF_INET,SOCK_STREAM,IPPROTO_IP);   
    if(INVALID_SOCKET == sHost)   
    {   
        printf("socket failed !\n");   
        WSACleanup();   
        return -1;   
    }

	// 设置套接字为非阻塞模式
	int iMode = 1;
	retVal = ioctlsocket(sHost, FIONBIO, (u_long FAR*) &iMode);
	if(retVal == SOCKET_ERROR)
	{
		printf("ioctlsocket failed !\n");
		WSACleanup();
		return -1;
	}

    // 设置服务器地址   
    servAddr.sin_family = AF_INET;   
    servAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");		// 用户需要根据实际情况修改
    servAddr.sin_port = htons(9990);								
	int sServerAddlen = sizeof(servAddr);												// 计算地址的长度       
	// 循环等待
	while(true)
	{
		// 连接服务器   
		Sleep( 200 );
		retVal = connect(sHost,(LPSOCKADDR)&servAddr,sizeof(servAddr));   
		Sleep( 200 );
		if(SOCKET_ERROR == retVal)   
		{   
			int err = WSAGetLastError();
			if(err == WSAEWOULDBLOCK || err == WSAEINVAL)			// 无法立即完成非阻塞套接字上的操作
			{
				//Sleep(500);
				continue;
			}
			else if(err == WSAEISCONN)												// 已建立连接
			{
				break;
			}
			else
			{
				//continue;

				printf("connect failed !\n");   
				closesocket(sHost);   
				WSACleanup();   
				return -1;   
			}
		}   
	}

	// 循环向服务器发送字符串,并显示反馈信息。
	// 发送quit将使服务器程序退出,同时客户端程序自身也将退出
	while(true)
	{
		// 向服务器发送数据   
		printf("Please input a string to send: ");		
		std::string str;
		std::getline(std::cin, str);		  // 接收输入的数据
		ZeroMemory(buf,BUF_SIZE);   
		strcpy(buf,str.c_str());              // 将用户输入的数据复制到buf中 
		
		while(true)
		{
			// 向服务器发送数据
			retVal = send(sHost,buf,strlen(buf),0);   
			if(SOCKET_ERROR == retVal)   
			{   
				int err = WSAGetLastError();
				if(err == WSAEWOULDBLOCK)			// 无法立即完成非阻塞套接字上的操作
				{
					Sleep(500);
					continue;
				}
				else
				{
					printf("send failed !\n");   
					closesocket(sHost);   
					WSACleanup();   
					return -1;   
				}
			}
			break;
		}	  
		
		int count=0;
		while(true)
		{
			ZeroMemory(buf,BUF_SIZE);					// 清空接收数据的缓冲区
			retVal = recv(sHost,buf,sizeof(buf)+1,0);   // 接收服务器回传的数据   
			if(SOCKET_ERROR == retVal)   
			{   
				int err = WSAGetLastError();			// 获取错误编码
				if(err == WSAEWOULDBLOCK)			    // 接收数据缓冲区暂无数据
				{
					Sleep(100);
					count=count+1;
					if (count<10){
					printf("waiting back msg !\n");
					continue;
					} 
					else{
						printf("recv back msg failed! \n");
					break;
					}
				}
				else if(err == WSAETIMEDOUT || err == WSAENETDOWN)
				{
					printf("recv failed !\n");   
					closesocket(sHost);   
					WSACleanup();   
					return -1;
				}
				break;
			}   
			break;
		}	
		//ZeroMemory(buf,BUF_SIZE);						// 清空接收数据的缓冲区
		//retVal = recv(sHost,buf,sizeof(buf)+1,0);     // 接收服务器回传的数据   

		printf("Recv From Server: %s\n\n",buf);   
		
		if(strcmp(buf, "quit") == 0)          // 如果收到quit,则退出
		{
			printf("quit!\n");
			break;
		}
	}
  
    closesocket(sHost);   
    WSACleanup();  
	system("pause");
    return 0;  
}
  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

半夏风情

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值