套接字阻塞模式

本文展示了如何使用C++在Windows环境下实现一个阻塞模式的TCP服务器和客户端。服务器通过`BlockTcpServer`类进行初始化、监听、接受连接、接收和发送数据。客户端`BlockTcpClient`类完成连接服务器、发送和接收数据的操作。代码包括错误处理和套接字API的使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

服务端: BlockTcpServer

 

/**************************** begin ************************/ 

 

// BlockServer.app
// class : BlockTcpServer
// Init,Bind,Listen,Accept,Recv,Send....

#include <iostream>
#include <WINSOCK2.H>
#pragma comment(lib, "wsock32.lib")

using namespace std;


class BlockTcpServer{
 enum{
  SERVER_EXIT_OK  = 0 ,   //服务器正常退出
  SERVER_API_OK  =  0 ,   //调用 Socket API 成功
  SERVER_DLL_ERROR =  1 ,   //调用Windows sockets DLL失败
  SERVER_API_ERROR =  2 ,   //调用 Socket API 失败
  SERVERPORT   =  5555, //定义服务器 TCP 端口
  MAX_NUM_BUF      =  64  , //缓冲区尺寸
  SOCKET_VERSION   = MAKEWORD(2,2)
 };
private:
 int   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);  //显示错误信息

public:
 int     ServerRun(void);

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

/**************************/

int main(int argc, char* argv[])
{
 BlockTcpServer bts;
 while(1){
  bts.ServerRun();
  Sleep(50);
 }
 return 1; 
}

/**************************/

 


int BlockTcpServer::ServerRun(void)
{
 if(SERVER_API_OK != InitMember()){
  ShowSocketMsg(" InitMember 失败 /n ");
  return -1;
 }

 //创建套接字
 sServer= socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 
 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;  
 //绑定套接字
 int retVal = bind(sServer, (LPSOCKADDR)&addrServ, sizeof(SOCKADDR_IN));
 if(SOCKET_ERROR == retVal)
 {
  closesocket(sServer);      //关闭套接字
  return HandleSocketError("Failed bind()!"); //错误处理
 }
 
 //开始监听
 retVal = listen(sServer, 1);
 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);
 if(INVALID_SOCKET == sClient)
 {
  closesocket(sServer);       //关闭套接字
  return HandleSocketError("Failed accept()!"); //错误处理
 }else{
  bConning = TRUE;        //客户端请求成功
 }
 
 //显示客户端的IP和端口
 char *pClientIP = inet_ntoa(addrClient.sin_addr);
 u_short  clientPort = ntohs(addrClient.sin_port); 
 cout<<"Accept a client."<<endl;
 cout<<"IP: "<<pClientIP<<endl;
 cout<<"Port: "<<clientPort<<endl;

 //接收客户端数据
 if (!RecvLine(sClient, bufRecv))
 {
  ShowSocketMsg(" RecvLine () 失败 .. /n");
  return ExitClient(SERVER_API_ERROR);//退出
 } 
 //显示客户端数据
 cout << bufRecv<<endl;

 //向客户端发送数据
 strcpy(bufSend, "Hello,Client!/n");
 if (!SendLine(sClient, bufSend))
 {
  ShowSocketMsg(" SendLine () 失败 .. /n");
  return ExitClient(SERVER_API_ERROR);
 }
 
 //显示退出信息
 cout << "Server exiting..." << endl;
 
 //退出
 return ExitClient(SERVER_EXIT_OK);
}

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

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

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


 WSADATA wsaData;    //Windows sockets DLL版本信息
 int  retVal;     //调用Windows sockets API返回值
 
 //初始化Windows Sockets DLL
 
 retVal = WSAStartup(SOCKET_VERSION, &wsaData);
 if ( 0 != retVal )
 {
  ShowSocketMsg("Can not find a usable Windows Sockets dll!");
  return SERVER_DLL_ERROR;
 } 
 //确保WinSock DLL支持2.2
 if ( LOBYTE( wsaData.wVersion ) != LOBYTE(SOCKET_VERSION) || HIBYTE( wsaData.wVersion ) != HIBYTE(SOCKET_VERSION) )
 {
  ShowSocketMsg("Can not find a usable Windows Sockets dll!");
  WSACleanup( );
  return SERVER_DLL_ERROR;
 }

 return SERVER_API_OK;

}


/*
 * 退出
 */
int  BlockTcpServer::ExitClient(int nExit)
{
 closesocket(sServer); //关闭监听套接字
 closesocket(sClient); //关闭连接客户端套接接
 WSACleanup();   //卸载Windows sockets DLL 清理内存
 return nExit;   //退出
}
/*
 * 读一行数据
 */
BOOL BlockTcpServer::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)
  {
   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 BlockTcpServer::SendLine(SOCKET s, char* str)
{
 int retVal;//返回值
 retVal = send(s, str, strlen(str), 0);//一次发送

 //错误处理
 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  BlockTcpServer::HandleSocketError(char *str)
{
 ShowSocketMsg(str);  //显示错误消息 
 WSACleanup();   //卸载Windows socket DLL 
 return SERVER_API_ERROR;//退出应用程序
}

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

 

/***************************  end ****************************/

 

 

客户端: BlockTcpClient

 

/**************************** begin ************************/

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

#include "stdafx.h"

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

using namespace std;


class   BlockTcpClient{
 enum{
  
        CLIENT_EXIT_OK  =  0 ,   //客户端正常退出
  CLIENT_DLL_ERROR =  1 ,   //调用Windows socket dll失败
  CLIENT_API_ERROR =  2 ,    //调用Windows socket api失败
  CLIENT_API_OK       =       0 ,
  MAX_NUM_BUF   =  64 ,   //缓冲区的最大长度
  SERVERPORT   =  5555 , //服务器TCP端口
  SOCKET_VERSION      =       MAKEWORD(2,2)
 };
private:
 int  InitMember(void);    //初始化变量
 int  ExitClient(int nExit);   //退出
 BOOL RecvLine(SOCKET s, char* buf); //读取一行数据
 void ShowErrorMsg(void);    //显示错误信息
 void ShowIpAndPort(SOCKADDR_IN & addr);

public:
 BlockTcpClient(void);
 ~BlockTcpClient(void);
 int     ClientRun(void);
 BOOL GetInit(void){ return Init; }
private:
 char bufRecv[MAX_NUM_BUF];   //读缓冲区
 char bufSend[MAX_NUM_BUF];   //写缓冲区
 SOCKET sHost;       //socket
 BOOL bConning;      //连接服务器状态
 BOOL Init;
};

 

/************************************/
int main(){
 BlockTcpClient btc; 
 if( !btc.GetInit() ) return -1;
 while(1){ 
  btc.ClientRun();
  //Sleep(30); 
 }  
 return 0;
}
/************************************/
BlockTcpClient::BlockTcpClient(void){
 Init = true;
 if(CLIENT_API_OK != InitMember()){
   //ShowSocketMsg(" InitMember 失败 /n ");
   cout << "InitMember 失败 /n" << endl;
   Init = false;
 } 
}
BlockTcpClient::~BlockTcpClient(void){
 WSACleanup();
}


int    BlockTcpClient::ClientRun(void)
{

 //初始化变量
 /*
 if(CLIENT_API_OK != InitMember()){
  //ShowSocketMsg(" InitMember 失败 /n ");
  cout << "InitMember 失败 /n" << endl;
  return -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_port = htons(SERVERPORT);

 cout << "Befor connect()." << endl;
 ShowIpAndPort(addrServ);

 //连接服务器
 int retVal = connect(sHost,(LPSOCKADDR)&addrServ, sizeof(SOCKADDR_IN));

 cout << "After connect()." << endl;
 ShowIpAndPort(addrServ);

 if(SOCKET_ERROR == retVal)
 {
  ShowErrorMsg();       //显示错误信息
  return ExitClient(CLIENT_API_ERROR); //退出
 }else{
  bConning = TRUE;      //连接服务器成功
 }    
 //连接服务器成功
 cout<<"Connect successfully!"<<endl;


 //向服务器发送数据
 strcpy(bufSend, "Hello, Server!/n");
 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;

 //退出
 return ExitClient(CLIENT_EXIT_OK);
}

/*
 * 显示错误信息
 */
void BlockTcpClient::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)
 {
  MessageBox(NULL, (char*)LocalLock(hlocal), "CLIENT ERROR", MB_OK); 
  LocalFree(hlocal);
 }
}


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


 WSADATA wsaData;    //Windows sockets DLL版本信息
 int  retVal;     //调用Windows sockets API返回值
 
 //初始化Windows Sockets DLL
 
 retVal = WSAStartup(SOCKET_VERSION, &wsaData);
 if ( 0 != retVal )
 {
  cout << "Can not find a usable Windows Sockets dll!" << endl;
  return CLIENT_DLL_ERROR;
 } 
 //确保WinSock DLL支持2.2
 if ( LOBYTE( wsaData.wVersion ) != LOBYTE(SOCKET_VERSION) || HIBYTE( wsaData.wVersion ) != HIBYTE(SOCKET_VERSION) )
 {
  cout << "Can not find a usable Windows Sockets dll!" << endl;
  //WSACleanup( );
  return CLIENT_DLL_ERROR;
 }

 return CLIENT_API_OK;
}

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

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


/*
 * 读取一行数据
 */
BOOL BlockTcpClient::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;
}

 

 //显示 Socket 的 IP和端口 SOCKADDR_IN
void BlockTcpClient::ShowIpAndPort(SOCKADDR_IN & addr)
{
 char *pIP = inet_ntoa(addr.sin_addr);
 u_short  sPort = ntohs(addr.sin_port); 
 cout<<"IP and Port :"<<endl;
 cout<<"IP: "<<pIP<<endl;
 cout<<"Port: "<<sPort<<endl;
}

 


// struct hostent {
//  char FAR *   h_name;
//  char FAR * FAR * h_aliases;
//  short    h_addrtype;
//  short    h_length;
//  char FAR * FAR * h_addr_list;
// };

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值