网络编程:三次握手

服务器端:

// Server.cpp : Defines the entry point for the console application.
//

#include "stdio.h"
#include <iostream>
using namespace std;
#include <sstream> 
#include <string>
#include <WINSOCK2.H>
#pragma comment(lib, "wsock32.lib")

#define SERVER_EXIT_OK				0	//服务器正常退出
#define SERVER_DLL_REEOR			1	//调用Windows sockets DLL失败
#define SERVER_API_ERROR			2	//调用Windows sockets API失败
#define	SERVERPORT					5555//服务器TCP端口
#define MAX_NUM_BUF					64	//缓冲区最大尺寸

//变量
char	bufRecv[MAX_NUM_BUF];			//读缓冲区
char	bufSend[MAX_NUM_BUF];			//写缓冲区
SOCKET	sServer;						//服务器监听套接字
SOCKET	sClient;						//接受客户端套接字
BOOL	bConning;						//与客户端的连接状态

//函数
void	InitMember(void);				//初始化成员变量 
int		ExitClient(int nExit);			//客户端退出
BOOL	RecvLine(SOCKET s, char* buf);	//读取一行数据
BOOL	SendLine(SOCKET s, char* buf);	//发送一行数据
int		HandleSocketError(char *str);	//对Windows sockets API调用错误处理
void	ShowSocketMsg(char* str);		//显示错误信息

//主函数
int main(int argc, char* argv[])
{

	InitMember();					//初始化变量
	
	WORD	wVersionRequested;		//应用程序需要Windows sockets DLL的版本
	WSADATA	wsaData;				//Windows sockets DLL版本信息
	int		retVal;					//调用Windows sockets API返回值		
	//初始化Windows Sockets DLL
	wVersionRequested = MAKEWORD(1,1);//makeword是将两个byte型合并成一个word型,一个在高8位(b),一个在低8位(a)
	retVal = WSAStartup(wVersionRequested, &wsaData);//Windows异步套接字的启动命令
	//{1}一个WORD(双字节)型数值,指定了应用程序需要使用的Winsock规范的最高版本。
	//{2}指向WSADATA数据结构的指针,用来接收Windows Sockets实现的细节。
	if ( 0 != retVal ) 
	{
		ShowSocketMsg("Can not find a usable Windows Sockets dll!");
		return SERVER_DLL_REEOR;
	}	
	//确保WinSock DLL支持1.1
	if ( LOBYTE( wsaData.wVersion ) != 1 ||	HIBYTE( wsaData.wVersion ) != 1)
	{
		ShowSocketMsg("Can not find a usable Windows Sockets dll!");
		WSACleanup( );//中止Windows Sockets在所有线程上的操作.
		return SERVER_DLL_REEOR; 
	}
	
	
	//创建套接字
	sServer= socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);	
	/*SOCKET PASCAL FAR socket( int af, int type, int protocol);
  af:一个地址描述。目前仅支持AF_INET格式,也就是说ARPA Internet地址格式。
  type:新套接口的类型描述类型,如TCP(SOCK_STREAM)和UDP(SOCK_DGRAM)。
  protocol:套接口所用的协议。如调用者不想指定,可用0。
	
	若无错误发生,socket()返回引用新套接口的描述字。否则的话,返回INVALID_SOCKET错误*/
	if(INVALID_SOCKET == sServer)
	{
		return HandleSocketError("Failed socket()!");
	}
	
	//服务器套接字地址 
	SOCKADDR_IN addrServ;
	addrServ.sin_family = AF_INET;
	addrServ.sin_port = htons(SERVERPORT);//将一个无符号短整型数值转换为网络字节序
	addrServ.sin_addr.s_addr = INADDR_ANY;		
	/*sin_family指代协议族,在socket编程中只能是AF_INET
  sin_port存储端口号(使用网络字节顺序)
  sin_addr存储IP地址,使用in_addr这个数据结构*/
	//绑定套接字
	retVal = bind(sServer, (LPSOCKADDR)&addrServ, sizeof(SOCKADDR_IN));
	/*返回:成功返回0,失败返回-1.
  参数列表中,sockfd 表示已经建立的)socket编号(描述符);
  my_addr 是一个指向sockaddr结构体类型的指针;
  参数addrlen表示my_addr结构的长度,可以用sizeof函数获得。*/
	if(SOCKET_ERROR == retVal)
	{
		closesocket(sServer);						//关闭套接字
		return HandleSocketError("Failed bind()!");	//错误处理
	}

	int count=0;
	while(1){
		//开始监听 
		retVal = listen(sServer, 2);
		/*S:用于标识一个已捆绑未连接套接口的描述字。
  	  backlog:等待连接队列的最大长度*/
		if(SOCKET_ERROR == retVal)
		{
			closesocket(sServer);						//关闭套接字
			return HandleSocketError("Failed listen()!");//错误处理
		}
	

		//等待客户端的连接	
		cout << "Server succeeded!" << endl;
		cout << "Waiting for new clients..." << endl;


		//接受客户端请求
		sockaddr_in addrClient;
		int addrClientlen = sizeof(addrClient);
		sClient = accept(sServer,(sockaddr FAR*)&addrClient, &addrClientlen);
		/*s:套接口描述字,该套接口在listen()后监听连接。
  	addr:(可选)指针,指向一缓冲区,其中接收为通讯层所知的连接实体的地址。Addr参数的实际格式由套接口创建时所产生的地址族确定。
  	addrlen:(可选)指针,输入参数,配合addr一起使用,指向存有addr地址长度的整型数。
		*/
		if(INVALID_SOCKET == sClient)
		{
			closesocket(sServer);							//关闭套接字
			return HandleSocketError("Failed accept()!");	//错误处理
		}else{
			bConning = TRUE;								//客户端请求成功
		}
	
		//显示客户端的IP和端口
		char *pClientIP = inet_ntoa(addrClient.sin_addr);//如果正确,返回一个字符指针,指向一块存储着点分格式IP地址的静态缓冲区
		u_short  clientPort = ntohs(addrClient.sin_port);	//将一个无符号短整形数从网络字节顺序转换为主机字节顺序
		cout<<"Accept a client."<<endl;
		cout<<"IP: "<<pClientIP<<endl;
		cout<<"Port: "<<clientPort<<endl;


		//接收客户端数据
		if (!RecvLine(sClient, bufRecv))
		{
			cout<<"接收数据失败!\n";
			return	ExitClient(SERVER_API_ERROR);//退出
		}	
		//显示客户端数据
		cout << bufRecv<<endl;
		char temp[MAX_NUM_BUF];
		int i=0;
		while(bufRecv[i]!='\n')
		{
			temp[i]=bufRecv[i];
			i++;
		}
		temp[i]='\0';
		//向客户端发送数据
		stringstream ss;
		string s(temp);
		count++;

		ss<<count;
		string str="";
		str+=s;
		str+="你好!你是访问我服务的第";
		str+=ss.str();
		str+="个客户\n";

		strcpy(bufSend, str.c_str());
		if (!SendLine(sClient, bufSend))
		{
			cout<<"发送失败!\n";
			//return	ExitClient(SERVER_API_ERROR);
		}
			
	}

	//显示退出信息
	cout << "Server exiting..." << endl;
	
	//退出
	return ExitClient(SERVER_EXIT_OK);
}



/*
 *	初始化成员变量
 */
void	InitMember(void)
{
	//初始化读和写缓冲区
	memset(bufRecv, 0, MAX_NUM_BUF);
	memset(bufSend, 0, MAX_NUM_BUF);

	//初始化
	sServer = INVALID_SOCKET;
	sClient = INVALID_SOCKET;

	//没有连接状态
	bConning = FALSE;
}


/*
 *	退出
 */
int		ExitClient(int nExit)
{
	closesocket(sServer);	//关闭监听套接字
	closesocket(sClient);	//关闭连接客户端套接接
	WSACleanup();			//卸载Windows sockets DLL 清理内存
	return nExit;			//退出
}
/*
 *	读一行数据
 */
BOOL	RecvLine(SOCKET s, char* buf)
{
	BOOL	retVal = TRUE;			//返回值
	BOOL	bLineEnd = FALSE;		//行结束
	int		nReadLen = 0;			//读入字节数
	int		nDataLen = 0;			//数据长度
	while (!bLineEnd && bConning)	//与客户端连接 没有换行
	{
		nReadLen = recv(s, buf + nDataLen, 1, 0);//每次接收一个字节
		/*s:一个标识已连接套接口的描述字。
  buf:用于接收数据的缓冲区。
  len:缓冲区长度。
  flags:指定调用方式。*/
		
		//错误处理
		if (SOCKET_ERROR == nReadLen)
		{
			int nErrCode = WSAGetLastError();//错误代码,获得上次失败操作的错误状态
			if (WSAENOTCONN == nErrCode)
			{
				ShowSocketMsg("The socket is not connected!");
				
			}else if(WSAESHUTDOWN == nErrCode)
			{
				ShowSocketMsg("The socket has been shut down!");
				
			}else if (WSAETIMEDOUT == nErrCode)
			{
				ShowSocketMsg("The connection has been dropped!");							
			}else if (WSAECONNRESET == nErrCode)
			{
				ShowSocketMsg("The virtual circuit was reset by the remote side!");
			}else{}	
			
			retVal = FALSE;	//读数据失败
			break;			//跳出循环						
		}
		
		
		if (0 == nReadLen)//客户端关闭
		{
			retVal = FALSE;	//读数据失败
			break ;			//跳出循环			
		}
		
		//读入数据
		if ('\n' == *(buf + nDataLen))	//换行符
		{
			bLineEnd = TRUE;			//接收数据结束
		}else{
			nDataLen += nReadLen;		//增加数据长度
		}	
	}
	
	return retVal;
}

/*
 *	//发送一行数据
 */
BOOL	SendLine(SOCKET s, char* str)
{
	int retVal;//返回值
	retVal = send(s, str, strlen(str), 0);//一次发送
/*s:一个用于标识已连接套接口的描述字。
  buf:包含待发送数据的缓冲区。
  len:缓冲区中数据的长度。
  flags:调用执行方式。*/
	//错误处理
	if (SOCKET_ERROR == retVal)
	{
		int nErrCode = WSAGetLastError();//错误代码
		if (WSAENOTCONN == nErrCode)
		{
			ShowSocketMsg("The socket is not connected!");
			
		}else if(WSAESHUTDOWN == nErrCode)
		{
			ShowSocketMsg("The socket has been shut down!");
			
		}else if (WSAETIMEDOUT == nErrCode)
		{
			ShowSocketMsg("The connection has been dropped!");
		}else{}	
		
		return FALSE;	//发送失败
	}
	
	return TRUE;		//发送成功
}
	
/*
 *	错误处理
 */
int		HandleSocketError(char *str)
{
	ShowSocketMsg(str);		//显示错误消息	
	WSACleanup();			//卸载Windows socket DLL	
	return SERVER_API_ERROR;//退出应用程序
}

/*
 *	显示错误
 */
void	ShowSocketMsg(char* str)
{
	cout<<str;
	//MessageBox(NULL, str, "SERVER ERROR", MB_OK);
}


客户端:

// Client.cpp : Defines the entry point for the console application.
//

#include "stdio.h"

#include <windows.h>
#include <winsock.h>
#include <iostream>
using namespace std;
#pragma comment(lib, "wsock32.lib")


#define CLIENT_EXIT_OK				0	//客户端正常退出
#define CLIENT_DLL_REEOR			1	//调用Windows socket dll失败
#define CLIENT_API_ERROR			2	//调用Windows socket api失败
#define MAX_NUM_BUF					64	//缓冲区的最大长度
#define	SERVERPORT					5555//服务器TCP端口


//变量
char	bufRecv[MAX_NUM_BUF];			//读缓冲区
char	bufSend[MAX_NUM_BUF];			//写缓冲区
SOCKET	sHost;							//socket
BOOL	bConning;						//连接服务器状态

//函数
void	InitMember(void);				//初始化变量
int		ExitClient(int nExit);			//退出
BOOL	RecvLine(SOCKET s, char* buf);	//读取一行数据
void	ShowErrorMsg(void);				//显示错误信息

//主函数
int main()
{

	//初始化变量
	InitMember();

	WORD	wVersionRequested;		//应用程序需要Windows sockets DLL的版本
	WSADATA	wsaData;				//Windows sockets DLL版本信息
	int		retVal;					//调用Windows sockets API返回值

	//初始化Windows Sockets DLL
	wVersionRequested = MAKEWORD(1,1);	
	retVal = WSAStartup(wVersionRequested,(LPWSADATA)&wsaData);
	if ( 0 != retVal ) 
	{
		cout<<"Error";
		//MessageBox(NULL, "Can not find a usable Windows Sockets dll!", "ERROR", MB_OK);
		return CLIENT_DLL_REEOR;
	}

	//while(1){
	//创建Windows socket
	sHost = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if(INVALID_SOCKET == sHost)
	{
		ShowErrorMsg();			//显示错误信息
		WSACleanup();			//释放资源
		return CLIENT_API_ERROR;//退出
	}

	//准备连接服务器
	cout << "Client succeeded!" << endl;
	cout<<"Be ready to connect to server..."<<endl;

	
	
	//获取主机的信息
	LPHOSTENT hostEntry;
	char hostname[MAX_NUM_BUF];
	gethostname(hostname,MAX_NUM_BUF);			//获取主机名称
	hostEntry = gethostbyname(hostname);		//获取主机信息
	if(!hostEntry)
	{
		ShowErrorMsg();							//显示错误信息
		return ExitClient(CLIENT_API_ERROR);	//退出
	}
	//设置sockaddr_in
	SOCKADDR_IN addrServ;
	addrServ.sin_family = AF_INET;
	addrServ.sin_addr = *((LPIN_ADDR)*hostEntry->h_addr_list);
	//addrServ.sin_addr.s_addr = inet_addr("10.20.255.183");
	addrServ.sin_port = htons(SERVERPORT);
	//连接服务器
	retVal=connect(sHost,(LPSOCKADDR)&addrServ, sizeof(SOCKADDR_IN));
	if(SOCKET_ERROR == retVal)
	{
		ShowErrorMsg();							//显示错误信息
		return ExitClient(CLIENT_API_ERROR);	//退出
	}else{
		bConning = TRUE;						//连接服务器成功
	}     
	//连接服务器成功
	cout<<"Connect successfully!"<<endl;
	
	//向服务器发送数据
	int i=0;
	char c;
	char s[MAX_NUM_BUF];
	cout<<"输入您的姓名:\n";
	while((c=getchar())!='\n')
	{
		s[i++]=c;
	}
	s[i++]='\n';
	s[i]='\0';
	strcpy(bufSend, s);
	retVal = send(sHost, bufSend, strlen(bufSend), 0);
	if (SOCKET_ERROR == retVal)
	{
		ShowErrorMsg();							//显示错误信息
		return ExitClient(CLIENT_API_ERROR);	//退出
	}

	//从服务器接收数据
	if (!RecvLine(sHost, bufRecv))
	{
		ShowErrorMsg();							//显示错误信息
		return ExitClient(CLIENT_API_ERROR);	//退出
	}
	//显示服务器的应答
	cout<<bufRecv<<endl;
	//Sleep(2000);

	//退出
	return ExitClient(CLIENT_EXIT_OK);
}


/*
 *	显示错误信息
 */
void	ShowErrorMsg(void)
{
	int nErrCode = WSAGetLastError();//获取错误代码

	HLOCAL hlocal = NULL;  
	
	//获取错误的文本字符串
	BOOL fOk = FormatMessage(
		FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER,
		NULL, nErrCode, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
		(PTSTR)&hlocal, 0, NULL);
	
	//显示错误信息
	if (hlocal != NULL)
	{
		cout<<LocalLock(hlocal);
		//MessageBox(NULL, (char*)LocalLock(hlocal), "CLIENT ERROR", MB_OK);	
		LocalFree(hlocal);
	}
}


/*
 *	初始化成员变量
 */
void	InitMember(void)
{
	//初始化读和写缓冲区
	memset(bufRecv, 0, MAX_NUM_BUF);
	memset(bufSend, 0, MAX_NUM_BUF);
	//初始化
	sHost = INVALID_SOCKET;
	//没有连接状态
	bConning = FALSE;
}

/*
 *	退出
 */
int		ExitClient(int nExit)
{
	closesocket(sHost);		//关闭套接字
	WSACleanup();			//卸载Windows sockets DLL 清理内存

		//显示退出信息
	cout << "Client exiting..." << endl;
	Sleep(5000);
	return nExit;	//退出
}


/*
 *	读取一行数据
 */
BOOL	RecvLine(SOCKET s, char* buf)
{
	BOOL	retVal = TRUE;			//返回值
	BOOL	bLineEnd = FALSE;		//行结束
	int		nReadLen = 0;			//读入字节数
	int		nDataLen = 0;			//数据长度
	while (!bLineEnd && bConning)	//与客户端连接 没有换行
	{
		nReadLen = recv(s, buf + nDataLen, 1, 0);//每次接收一个字节		
		//错误处理
		if (SOCKET_ERROR == nReadLen)
		{			
			retVal= FALSE;	//读数据失败
			break;			//跳出循环						
		}		
		
		if (0 == nReadLen)//客户端关闭
		{
			retVal = FALSE;	//读数据失败
			break ;			//跳出循环			
		}
		
		//读入数据
		if ('\n' == *(buf + nDataLen))	//换行符
		{
			bLineEnd = TRUE;			//接收数据结束
		}else{
			nDataLen += nReadLen;		//增加数据长度
		}	
	}
	
	return retVal;
}

服务器端:


客户端:


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值