Windows下定长数据接收源码

使用流式套接字接收定长数据:

对于固定长度的消息,程序需要读取指定数目的字节,这种方法有利于模拟定长数据包的形态来处理底层提交的字节流数据,与回射程序接收代码不同之处在于:定长数据的总长度,接收结束的条件不是对方关闭连接。而是接收到足够长度的消息,有利于通信双方进行持续的数据交互。在流式套接字的功能框架上增加recvn()方法,处理定长数据的接收。

首先,服务器和客户端的工程中都有相同的SocketFrame.cpp和SocketFrame.h文件

SocketFrame.cpp:


#include "StdAfx.h"
#include "SocketFrame.h"
#include "ws2tcpip.h"
#include "mstcpip.h"
CSocketFrame::CSocketFrame(void)
{
}

CSocketFrame::~CSocketFrame(void)
{
}
/********************************************************
函数名:set_address
输入参数:char * hname:主机名 or 点分十进制表示的IP地址
          char * sname:端口号
		  struct sockaddr_in * sap:以sockaddr_in结构存储的地址(输出参数)
          char * protocol:字符串形式描述的协议类型,如"tcp"
输出参数:0表示成功,-1表示失败。
功能:根据给定的主机名或点分十进制表示的IP地址获得以sockaddr_in结构存储的地址
*********************************************************/
int CSocketFrame::set_address(char * hname, char * sname, struct sockaddr_in * sap, char * protocol)
{
	struct servent *sp;
	struct hostent *hp;
	char *endptr;
	unsigned short port;
	unsigned long ulAddr = INADDR_NONE;


    //对地址结构socketaddr_in初始化为0,并设置地址族为AF_INET
	memset( sap,0, sizeof( *sap ) );
	sap->sin_family = AF_INET;
	
	if ( hname != NULL )
	{
		//如果hname不为空,假定给出的hname为点分十进制表示的数字地址,转换地址为sockaddr_in类型
		ulAddr = inet_addr(hname);
		if ( ulAddr == INADDR_NONE || ulAddr == INADDR_ANY) {
			//printf("inet_addr 函数调用错误,错误号: %d\n", WSAGetLastError());
			//调用错误,表明给出的是主机名,调用gethostbyname获得主机地址
			hp = gethostbyname( hname );
			if ( hp == NULL ) {
				printf("未知的主机名,错误号: %d\n", WSAGetLastError());
				return -1;
			}
			sap->sin_addr = *( struct in_addr * )hp->h_addr;
        }      
		else
			sap->sin_addr.S_un.S_addr=ulAddr;		
	}
	else
		//如果调用者没有指定一个主机名或地址,则设置地址为通配地址INADDR_ANY
		sap->sin_addr.s_addr = htonl( INADDR_ANY );
	//尝试转换sname为一个整数
	port = (unsigned short )strtol( sname, &endptr, 0 );
	if ( *endptr == '\0' )
	{
		//如果成功则转换为网络字节顺序
		sap->sin_port = htons( port );
	}
	else
	{
		//如果失败,则假定是一个服务名称,通过调用getservbyname获得端口号
		sp = getservbyname( sname, protocol );
		if ( sp == NULL ) {
			printf("未知的服务,错误号: %d\n", WSAGetLastError());
			return -1;
		}
		sap->sin_port = sp->s_port;
	}
	return 0;
}

/********************************************************
函数名:start_up
输入参数:无
输出参数:0:成功,-1:失败
功能:初始化Windows Sockets DLL,协商版本号
*********************************************************/
int CSocketFrame::start_up(void)
{
	WORD wVersionRequested;
    WSADATA wsaData;
    int iResult;

    // 使用 MAKEWORD(lowbyte, highbyte) 宏,在Windef.h 中声明
    wVersionRequested = MAKEWORD(2, 2);

    iResult = WSAStartup(wVersionRequested, &wsaData);
    if (iResult != 0) {
        //告知用户无法找到合适可用的Winsock DLL
        printf("WSAStartup 函数调用错误,错误号: %d\n",  WSAGetLastError());
        return -1;
    }

    // 确认WinSock Dll支持版本2.2
    // 注意,如果DLL支持的版本比2.2更高,根据用户调用前的需求,仍然返回2.2版本号,存储于wsaData.wVersion

    if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
        // 告知用户无法找到可用的WinSock DLL.                              
        printf("无法找到可用的Winsock.dll版本\n");
        WSACleanup();
        return -1;
    }

	return 0;
}
/********************************************************
函数名:clean_up
输入参数:无
输出参数:0:成功,-1:失败
功能:终止Windows Sockets DLL的使用,释放资源 
*********************************************************/
int CSocketFrame::clean_up(void)
{
	int iResult;
	iResult = WSACleanup();
    if (iResult == SOCKET_ERROR) {
        // WSACleanup调用失败                  
        printf("WSACleanup 函数调用错误,错误号: %d\n",  WSAGetLastError());
        return -1;
    }

	return 0;
}
/********************************************************
函数名:quit
输入参数:SOCKET s:服务器的连接套接字
输出参数:0:成功,-1:失败
功能:关闭套接字,释放dll
*********************************************************/
int CSocketFrame::quit(SOCKET s)
{
    int iResult=0;
    iResult = closesocket(s);
    if (iResult == SOCKET_ERROR){
        printf("closesocket 函数调用错误,错误号:%d\n", WSAGetLastError());
        return -1;
    }
    iResult = clean_up();
    return iResult;
}
/********************************************************
函数名:tcp_server
输入参数:char * hname:服务器主机名 or 点分十进制表示的IP地址
          char * sname:服务端口号
输出参数:创建服务器端流式套接字并配置,-1:表示失败
功能:创建流式套接字,根据用户输入的地址和端口号,绑定套接字的服务地址
      将其转换为监听状态
*********************************************************/
SOCKET CSocketFrame::tcp_server( char *hname, char *sname )
{
	sockaddr_in local;
	SOCKET ListenSocket;
	const int on = 1;
	int iResult = 0;

    //为服务器的本地地址local设置用户输入的IP和端口号
	if (set_address( hname, sname, &local, "tcp" ) !=0 )
		return -1;
	
	//创建套接字
	ListenSocket = socket( AF_INET, SOCK_STREAM, 0 );
	if (ListenSocket == INVALID_SOCKET) {
        printf("socket 函数调用错误,错误号: %ld\n", WSAGetLastError());
        clean_up();
        return -1;
    }

	//设置服务器地址可重用选项
	iResult = setsockopt( ListenSocket, SOL_SOCKET, SO_REUSEADDR, ( char * )&on, sizeof( on ));
	if ( iResult == SOCKET_ERROR){
		printf("setsockopt函数调用错误,错误号: %d\n", WSAGetLastError());
        quit(ListenSocket);
        return -1;
    }

    //绑定服务器地址
	iResult = bind( ListenSocket, (struct sockaddr *) & local, sizeof (local));
    if (iResult == SOCKET_ERROR) {
        printf("bind 函数调用错误,错误号: %d\n", WSAGetLastError());
        quit(ListenSocket);
        return -1;
    }

	//设置服务器为监听状态,监听队列长度为NLISTEN
    iResult = listen(ListenSocket, SOMAXCONN);
	if (iResult == SOCKET_ERROR){
		printf("listen 函数调用错误,错误号: %d\n", WSAGetLastError());
		quit(ListenSocket);
		return -1;
    }

	return ListenSocket;
}
/********************************************************
函数名:tcp_client
输入参数:char * hname:服务器主机名 or 点分十进制表示的IP地址
          char * sname:服务端口号
输出参数:创建客户端流式套接字,-1:表示失败
功能:创建流式套接字,根据用户输入的地址和端口号,向服务地址
      请求建立连接
*********************************************************/
SOCKET CSocketFrame::tcp_client( char *hname, char *sname )
{
	struct sockaddr_in peer;
	SOCKET ClientSocket;
	int iResult = 0;

	//指明服务器的地址peer为用户输入的IP和端口号
	if (set_address( hname, sname, &peer, "tcp" ) !=0 )
		return -1;

	//创建套接字
	ClientSocket = socket( AF_INET, SOCK_STREAM, 0 );
	if (ClientSocket == INVALID_SOCKET) {
        printf("socket 函数调用错误,错误号: %ld\n", WSAGetLastError());
        clean_up();
        return -1;
    }

    //请求向服务器建立连接
	iResult =connect( ClientSocket, ( struct sockaddr * )&peer, sizeof( peer ) );
	if (iResult == SOCKET_ERROR){
		printf("connect 函数调用错误,错误号: %d\n", WSAGetLastError());
		quit(ClientSocket);
		return -1;
    }

	return ClientSocket;
}
/********************************************************
函数名:recvn
输入参数:SOCKET s:服务器的连接套接字
          char * recvbuf:存放接收到数据的缓冲区 
		  int fixedlen:固定的预接收数据长度
输出参数:>0:实际接收到的字节数,-1:失败
功能:在流式套接字中接收固定长度的数据
********************************************************/
int CSocketFrame::recvn(SOCKET s, char * recvbuf, unsigned int fixedlen)
{
	int iResult;//存储单次recv操作的返回值
	int cnt;//用于统计相对于固定长度,剩余多少字节尚未接收
	cnt = fixedlen;
	while ( cnt > 0 )
	{
        iResult = recv(s, recvbuf, cnt, 0);
        if ( iResult < 0 )
		{
			//数据接收出现错误,返回失败
			printf("接收发生错误: %d\n", WSAGetLastError());
		    return -1;
		}
	    if ( iResult == 0 )
		{
			//对方关闭连接,返回已接收到的小于fixedlen的字节数
	        printf("连接关闭\n");
			return fixedlen - cnt;
		}
	    //printf("接收到的字节数: %d\n", iResult);
		//接收缓存指针向后移动
		recvbuf +=iResult;
		//更新cnt值
		cnt -=iResult;         
	}
	return fixedlen;
}
/********************************************************
函数名:recvvl
输入参数:SOCKET s:服务器的连接套接字
          char * recvbuf:存放接收到数据的缓冲区 
		  int recvbuflen:接收缓冲区长度
输出参数:>0:实际接收到的字节数,-1:失败,0:连接关闭
功能:在流式套接字中接收可变长度的数据
********************************************************/
int CSocketFrame::recvvl(SOCKET s, char * recvbuf, unsigned int recvbuflen)
{
	int iResult;//存储单次recv操作的返回值
	unsigned int reclen; //用于存储报文头部存储的长度信息
	//获取接收报文长度信息
    iResult = recvn(s, ( char * )&reclen, sizeof( unsigned int ));
	if ( iResult !=sizeof ( unsigned int ))
	{
		//如果长度字段在接收时没有返回一个整型数据就返回0(连接关闭)或-1(发生错误)
		if ( iResult == -1 )
		{
	         printf("接收发生错误: %d\n", WSAGetLastError());
		     return -1;
		}
		else
		{
			 printf("连接关闭\n");
             return 0;
		}
	}
	//转换网络字节顺序到主机字节顺序
	reclen = ntohl( reclen );
	if ( reclen > recvbuflen )
	{
		//如果recvbuf没有足够的空间存储变长消息,则接收该消息并丢弃,返回错误
		while ( reclen > 0)
		{
			iResult = recvn( s, recvbuf, recvbuflen );
			if ( iResult != recvbuflen )
			{
				//如果变长消息在接收时没有返回足够的数据就返回0(连接关闭)或-1(发生错误)
				if ( iResult == -1 )
				{
					 printf("接收发生错误: %d\n", WSAGetLastError());
					 return -1;
				}
				else
				{
					 printf("连接关闭\n");
					 return 0;
				}
			}
			reclen -= recvbuflen;
			//处理最后一段数据长度
			if ( reclen < recvbuflen )
				recvbuflen = reclen;
		}
		printf("可变长度的消息超出预分配的接收缓存\r\n");
		return -1;
	}
	//接收可变长消息
	iResult = recvn( s, recvbuf, reclen );
	if ( iResult != reclen )
	{
        //如果消息在接收时没有返回足够的数据就返回0(连接关闭)或-1(发生错误)
		if ( iResult == -1 )
		{
	         printf("接收发生错误: %d\n", WSAGetLastError());
		     return -1;
		}
		else
		{
			 printf("连接关闭\n");
             return 0;
		}
	}
	return iResult;
}
/********************************************************
函数名:udp_server
输入参数:char * hname:服务器主机名 or 点分十进制表示的IP地址
          char * sname:服务端口号
输出参数:创建服务器端流式套接字并配置,-1:表示失败
功能:创建流式套接字,根据用户输入的地址和端口号,绑定套接字的服务地址
      将其转换为监听状态
*********************************************************/
SOCKET CSocketFrame::udp_server( char *hname, char *sname )
{
	sockaddr_in local;
	SOCKET ServerSocket;
	const int on = 1;
	int iResult = 0;

    //为服务器的本地地址local设置用户输入的IP和端口号
	if (set_address( hname, sname, &local, "udp" ) !=0 )
		return -1;
	
	//创建套接字
	ServerSocket = socket( AF_INET, SOCK_DGRAM, 0 );
	if (ServerSocket == INVALID_SOCKET) {
        printf("socket 函数调用错误,错误号: %ld\n", WSAGetLastError());
        clean_up();
        return -1;
    }

	//设置服务器地址可重用选项
	iResult = setsockopt( ServerSocket, SOL_SOCKET, SO_REUSEADDR, ( char * )&on, sizeof( on ));
	if ( iResult == SOCKET_ERROR){
		printf("setsockopt函数调用错误,错误号: %d\n", WSAGetLastError());
        quit(ServerSocket);
        return -1;
    }

    //绑定服务器地址
	iResult = bind( ServerSocket, (struct sockaddr *) & local, sizeof (local));
    if (iResult == SOCKET_ERROR) {
        printf("bind 函数调用错误,错误号: %d\n", WSAGetLastError());
        quit(ServerSocket);
        return -1;
    }

	return ServerSocket;
}
/********************************************************
函数名:udp_client
输入参数:char * hname:服务器主机名 or 点分十进制表示的IP地址
          char * sname:服务端口号
		  BOOL flag:工作模式标识,true表示连接模式,false表示非连接模式
输出参数:创建客户端流式套接字,-1:表示失败
功能:创建数据报套接字,根据用户输入的地址和端口号
*********************************************************/
SOCKET CSocketFrame::udp_client( char *hname, char *sname, BOOL flag)
{
	struct sockaddr_in peer;
	SOCKET ClientSocket;
	int iResult = -1;

	//指明服务器的地址peer为用户输入的IP和端口号
	if (set_address( hname, sname, &peer, "udp" ) ==1 )
		return -1;

	//创建套接字
	ClientSocket = socket( AF_INET, SOCK_DGRAM, 0 );
	if (ClientSocket == INVALID_SOCKET) {
        printf("socket 函数调用错误,错误号: %ld\n", WSAGetLastError());
        clean_up();
        return -1;
    }

	if( flag == TRUE)
	{
		//连接模式
		//请求向服务器建立连接
	    iResult =connect( ClientSocket, ( struct sockaddr * )&peer, sizeof( peer ) );
	    if (iResult == SOCKET_ERROR){
		    printf("connect 函数调用错误,错误号: %d\n", WSAGetLastError());
		    quit(ClientSocket);
		    return -1;
		}
    }
	
	return ClientSocket;
}
/********************************************************
函数名:check_sum
输入参数:
		  USHORT *pchBuffer:待计算校验和的缓冲区
		  int iSize:待计算校验和缓冲区长度
输出参数:校验和
功能:计算校验和
*********************************************************/
USHORT CSocketFrame::check_sum(USHORT *pchBuffer, int iSize)
{
    unsigned long ulCksum=0;
    while (iSize > 1) 
    {
        ulCksum += *pchBuffer++;
        iSize -= sizeof(USHORT);
    }
    if (iSize) 
    {
        ulCksum += *(UCHAR*)pchBuffer;
    }
    ulCksum = (ulCksum >> 16) + (ulCksum & 0xffff);
    ulCksum += (ulCksum >>16);
    return (USHORT)(~ulCksum);
}
/********************************************************
函数名:raw_socket
输入参数:
		  BOOL bSendflag:首部控制选项
		  BOOL bRecvflag:接收控制选项
		  int iProtocol:协议设置,具体内容参考MSDN对协议的定义,如#define IPPROTO_IP 0
		  sockaddr_in *pLocalIP:指向本地IP地址的指针,返回参数,如果存在多个接口地址,获取用户选择的本地地址
输出参数:创建客户端流式套接字,-1:表示失败
功能:创建数据报套接字,根据用户输入的地址和端口号
*********************************************************/
SOCKET CSocketFrame::raw_socket( BOOL bSendflag, BOOL bRecvflag, int iProtocol, sockaddr_in *pLocalIP)
{
	SOCKET RawSocket;
	int iResult = 0;
	struct hostent *local;
    char HostName[DEFAULT_NAMELEN];
	struct in_addr addr;
    int in=0,i=0;
    DWORD dwBufferLen[10];
 	DWORD Optval= 1 ;
    DWORD dwBytesReturned = 0 ;


	//创建套接字
	RawSocket = socket( AF_INET, SOCK_RAW, iProtocol );
	if (RawSocket == INVALID_SOCKET) {
        printf("socket 函数调用错误,错误号: %ld\n", WSAGetLastError());
        clean_up();
        return -1;
    }

	if( bSendflag == TRUE)
	{
		//设置IP_HDRINCL表示要构造IP头,需#include "ws2tcpip.h"
	    iResult = setsockopt(RawSocket,IPPROTO_IP,IP_HDRINCL,(char*)&bSendflag,sizeof(bSendflag));
		if (iResult == SOCKET_ERROR){
		    printf("setsockopt 函数调用错误,错误号: %d\n", WSAGetLastError());
		    quit(RawSocket);
		    return -1;
		}
    }
	if( bRecvflag == TRUE)
	{
		//设置I/O控制选项,接收全部IP包
		//获取本机名称
	    memset( HostName, 0, DEFAULT_NAMELEN);
	    iResult = gethostname( HostName, sizeof(HostName));
	    if ( iResult ==SOCKET_ERROR) {
	      printf("gethostname 函数调用错误,错误号: %ld\n", WSAGetLastError());
	      quit(RawSocket);
	      return -1;
	    }
	
	    //获取本机可用IP
	    local = gethostbyname( HostName);
	    printf ("\n本机可用的IP地址为:\n");
	    if( local ==NULL)
	    {
	        printf("gethostbyname 函数调用错误,错误号: %ld\n", WSAGetLastError());
	        quit(RawSocket);
	        return -1;
	    }
	    while (local->h_addr_list[i] != 0) {
	        addr.s_addr = *(u_long *) local->h_addr_list[i++];
	        printf("\tIP Address #%d: %s\n", i, inet_ntoa(addr));
	    }
	   
	    printf ("\n请选择捕获数据待使用的接口号:");
	    scanf_s( "%d", &in);
	    
	    memset( pLocalIP, 0, sizeof(sockaddr_in));
	    memcpy( &pLocalIP->sin_addr.S_un.S_addr, local->h_addr_list[in-1], sizeof(pLocalIP->sin_addr.S_un.S_addr));
	    pLocalIP->sin_family = AF_INET;
	    pLocalIP->sin_port = 0;
	
	    //绑定本地地址
	    iResult = bind( RawSocket, (struct sockaddr *) pLocalIP, sizeof(sockaddr_in));
	    if( iResult == SOCKET_ERROR){
	        printf("bind 函数调用错误,错误号: %ld\n", WSAGetLastError());
	        quit(RawSocket);
			return -1;
	    }
	    printf(" \n成功绑定套接字和#%d号接口地址", in);
	
	    //设置套接字接收命令
	    iResult = WSAIoctl(RawSocket, SIO_RCVALL , &Optval, sizeof(Optval),  &dwBufferLen, sizeof(dwBufferLen), &dwBytesReturned , NULL , NULL );
	    if ( iResult == SOCKET_ERROR ){
	        printf("WSAIoctl 函数调用错误,错误号: %ld\n", WSAGetLastError());
	        quit(RawSocket);
			return -1;
	    }
	}
	return RawSocket;
}
SocketFrame.h:

#pragma once
#include "winsock2.h"
#include "stdio.h"
#pragma comment(lib,"ws2_32.lib")
//定义网络框架程序中所需的宏
#define TRUE			1
#define FALSE			0
#define	MAXLINE		    200	    // max text line length 
#define	DEFAULT_NAMELEN 100 //默认的名字长度
//首部结构定义
typedef struct tagIPHDR
{
	UCHAR hdr_len :4;  // length of the header
    UCHAR version :4;  // version of IP
	UCHAR	TOS;	   // Type of service
	USHORT	TotLen;	   // Total length
	USHORT	ID;		   // Identification
	USHORT	FlagOff;   // Flags and fragment offset
	UCHAR	TTL;	   // Time-to-live
	UCHAR	Protocol;  // Protocol
	USHORT	Checksum;  // Checksum
	ULONG IPSrc;	   // Internet address, source
	ULONG IPDst;	   // Internet address, destination
} IPHDR, *PIPHDR;

typedef struct tagUDPHDR	//UDP头定义
{
	USHORT src_portno; 
	USHORT dst_portno; 
	USHORT udp_length; 
	USHORT udp_checksum; 
} UDPHDR,*PUDPHDR;

typedef struct tagTCPHDR		//TCP首部定义
{
	USHORT  sport;            //Source port 
    USHORT  dport;            //Destination port 
    ULONG   seq;              //Sequence number 
    ULONG   ack;              //Ack number 
    BYTE    hlen;             // TCP header len (num of bytes << 2) 
    BYTE    flags;            // Option flags 
    USHORT  window;           // Flow control credit (num of bytes) 
    USHORT  check;            // Checksum 
    USHORT  urgent;           // Urgent data pointer 
} TCPHDR,*PTCPHDR;
//TCP标志字段定义
#define TFIN        0x01    // Option flags: no more data 
#define TSYN        0x02    // sync sequence nums 
#define TRST        0x04    // reset connection 
#define TPUSH       0x08    // push buffered data 
#define TACK        0x10    // acknowledgement 
#define TURGE       0x20    // urgent 


typedef struct tagFHDR		//UDP伪首部定义
{
	ULONG IPSrc;	
	ULONG IPDst;	
	UCHAR zero;
	UCHAR protocol;
	USHORT udp_length;
} FHDR,*PFHDR;


//ICMP数据报头
typedef struct tagICMPHDR
{
    UCHAR type;  //8位类型
    UCHAR code;  //8位代码
    USHORT cksum;  //16位校验和
    USHORT id;   //16位标识符
    USHORT seq;  //16位序列号
} ICMPHDR,*PICMPHDR;

#pragma pack()
//ICMP类型字段
const BYTE ICMP_ECHO_REQUEST = 8; //请求回显
const BYTE ICMP_ECHO_REPLY  = 0; //回显应答
const BYTE ICMP_TIMEOUT   = 11; //传输超时
const DWORD DEF_ICMP_TIMEOUT = 3000; //默认超时时间,单位ms
const int DEF_ICMP_DATA_SIZE = 32; //默认ICMP数据部分长度
const int MAX_ICMP_PACKET_SIZE = 1024; //最大ICMP数据报的大小
const int DEF_MAX_HOP = 30;    //最大跳站数
class CSocketFrame
{
public:
	CSocketFrame(void);
	~CSocketFrame(void);
	int set_address(char * hname, char * sname, struct sockaddr_in * sap, char * protocol);
	int start_up(void);
	int clean_up(void);
// EchoTCPServer.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"

#include "SocketFrame.h"
#include "winsock2.h"
#define ECHOPORT "7210"
int tcp_server_fun_recvn( SOCKET s ,int flen);
int main(int argc, char* argv[])
{
	CSocketFrame frame;
	int iResult = 0;
	SOCKET ListenSocket, ConnectSocket;
	int nlen =0;
	//输入参数合法性检查
	if (argc != 2)
	{
		printf("usage: EchoTCPServer-recvn <定长接收长度>");
		return -1;
	}
	//char *len =*argv[1];
	nlen = atoi(argv[1]);
	if ( nlen <= 0 )
	{		
		printf("不正确的输入:<定长接收长度>!");
		return -1;
	}
	
	//Windows Sockets Dll初始化
	frame.start_up();

	//创建服务器端的流式套接字并在指定端口号上监听
	ListenSocket = frame.tcp_server( NULL, ECHOPORT );
	if ( ListenSocket == -1 )
		return -1;
    printf("服务器准备好回射服务。。。\n");
	for ( ; ; ) {
        ConnectSocket = accept( ListenSocket, NULL, NULL );
		if( ConnectSocket != INVALID_SOCKET ){
			//建立连接成功
			printf("\r\n建立连接成功\n\n");
			//定长接收数据
			iResult = tcp_server_fun_recvn( ConnectSocket,nlen);
			//如果出错,关闭当前连接套接字,继续接收其它客户端的请求
			if(iResult == -1)
				printf("当前连接已关闭或出错!\n");
		}
		else{
			printf("accept 函数调用错误,错误号: %d\n", WSAGetLastError());
            frame.quit( ListenSocket );
            return -1;
        }

		//关闭连接套接字
		if ( closesocket( ConnectSocket ) == SOCKET_ERROR)
	        printf("closesocket 函数调用错误,错误号:%d\n", WSAGetLastError());;
	}
	frame.quit( ListenSocket );
    return 0;
}
/********************************************************
函数名:tcp_server_fun_recvn
输入参数:SOCKET s:服务器的连接套接字
          int flen:指定接收长度
输出参数:0:成功,-1:失败
功能:tcp_server:定长接收数据并统计字节数
********************************************************/
int tcp_server_fun_recvn( SOCKET s ,int flen)
{
	CSocketFrame frame;
	int iResult = 0;
	char    recvline[MAXLINE];
	do {
		memset( recvline, 0, MAXLINE );
		//接收定长数据
 	    iResult = frame.recvn( s, recvline, flen);
		if (iResult > 0){
			printf("服务器端接收到%d字节的数据:%s\n", iResult,recvline);
		}
		else{
			if (iResult == 0)
 	            printf("对方连接关闭,退出\n");
		    else{
 	            printf("recv 函数调用错误,错误号: %d\n", WSAGetLastError());
			    iResult = -1;
			}
		}
 	} while (iResult > 0);
	return iResult;
}


int quit(SOCKET s);USHORT check_sum(USHORT *pchBuffer, int iSize);SOCKET tcp_server( char *hname, char *sname );SOCKET udp_server( char *hname, char *sname );int recvn(SOCKET s, char * recvbuf, unsigned int fixedlen);int recvvl(SOCKET s, char * recvbuf, unsigned int recvbuflen);SOCKET tcp_client( char *hname, char *sname );SOCKET udp_client( char *hname, char *sname, BOOL flag); SOCKET raw_socket( BOOL bSendflag, BOOL bRecvflag, int iProtocol, sockaddr_in *pLocalIP);};


服务器 EchoTCPServer.cpp :

// EchoTCPServer.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"

#include "SocketFrame.h"
#include "winsock2.h"
#define ECHOPORT "7210"
int tcp_server_fun_recvn( SOCKET s ,int flen);
int main(int argc, char* argv[])
{
	CSocketFrame frame;
	int iResult = 0;
	SOCKET ListenSocket, ConnectSocket;
	int nlen =0;
	//输入参数合法性检查
	if (argc != 2)
	{
		printf("usage: EchoTCPServer-recvn <定长接收长度>");
		return -1;
	}
	//char *len =*argv[1];
	nlen = atoi(argv[1]);
	if ( nlen <= 0 )
	{		
		printf("不正确的输入:<定长接收长度>!");
		return -1;
	}
	
	//Windows Sockets Dll初始化
	frame.start_up();

	//创建服务器端的流式套接字并在指定端口号上监听
	ListenSocket = frame.tcp_server( NULL, ECHOPORT );
	if ( ListenSocket == -1 )
		return -1;
    printf("服务器准备好回射服务。。。\n");
	for ( ; ; ) {
        ConnectSocket = accept( ListenSocket, NULL, NULL );
		if( ConnectSocket != INVALID_SOCKET ){
			//建立连接成功
			printf("\r\n建立连接成功\n\n");
			//定长接收数据
			iResult = tcp_server_fun_recvn( ConnectSocket,nlen);
			//如果出错,关闭当前连接套接字,继续接收其它客户端的请求
			if(iResult == -1)
				printf("当前连接已关闭或出错!\n");
		}
		else{
			printf("accept 函数调用错误,错误号: %d\n", WSAGetLastError());
            frame.quit( ListenSocket );
            return -1;
        }

		//关闭连接套接字
		if ( closesocket( ConnectSocket ) == SOCKET_ERROR)
	        printf("closesocket 函数调用错误,错误号:%d\n", WSAGetLastError());;
	}
	frame.quit( ListenSocket );
    return 0;
}
/********************************************************
函数名:tcp_server_fun_recvn
输入参数:SOCKET s:服务器的连接套接字
          int flen:指定接收长度
输出参数:0:成功,-1:失败
功能:tcp_server:定长接收数据并统计字节数
********************************************************/
int tcp_server_fun_recvn( SOCKET s ,int flen)
{
	CSocketFrame frame;
	int iResult = 0;
	char    recvline[MAXLINE];
	do {
		memset( recvline, 0, MAXLINE );
		//接收定长数据
 	    iResult = frame.recvn( s, recvline, flen);
		if (iResult > 0){
			printf("服务器端接收到%d字节的数据:%s\n", iResult,recvline);
		}
		else{
			if (iResult == 0)
 	            printf("对方连接关闭,退出\n");
		    else{
 	            printf("recv 函数调用错误,错误号: %d\n", WSAGetLastError());
			    iResult = -1;
			}
		}
 	} while (iResult > 0);
	return iResult;
}


客户端EchoTCPClient.cpp :

// EchoTCPClient.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"

#include "SocketFrame.h"
int tcp_client_fun_recvn(FILE *fp,SOCKET s);
#define ECHOPORT "7210"
int main(int argc, char* argv[])
{
	CSocketFrame frame;
	int iResult;
	SOCKET ClientSocket;

	//输入参数合法性检查
	if (argc != 2)
	{
		printf("usage: EchoTCPClient-recvn <IPaddress>");
		return -1;
	}

	//Windows Sockets Dll初始化
	frame.start_up();
	
	//创建客户端的流式套接字,并与服务器建立连接
    printf("连接建立成功,请输入回射字符串。。。\n");
	ClientSocket = frame.tcp_client( ( char *)argv[1], ECHOPORT );
	if ( ClientSocket == -1 )
		return -1;

	//开始回射请求的发送与接收
	iResult = tcp_client_fun_recvn(stdin,ClientSocket);

	frame.quit( ClientSocket );
    return iResult;
	return 0;
}

/********************************************************
函数名:tcp_client_fun_recvn
输入参数:FILE *fp:指向FILE类型的对象
          SOCKET s:服务器的连接套接字
输出参数:0:成功,-1:失败
功能:测试服务器定长接收能力,根据用户输入,持续向服务器发送数据
*********************************************************/
int tcp_client_fun_recvn(FILE *fp,SOCKET s)
{
	int iResult;
    char sendline[MAXLINE],recvline[MAXLINE];
	memset(sendline,0,MAXLINE);
	memset(recvline,0,MAXLINE);

	//循环发送用户的输入数据,并接收服务器返回的应答,直到用户输入"Q"结束
	while(fgets(sendline,MAXLINE,fp)!=NULL)
	{
		if( *sendline == 'Q'){
			printf("input end!\n");
			// 数据发送结束,调用shutdown()函数声明不再发送数据,此时客户端仍可以接收数据
			iResult = shutdown(s, SD_SEND);
			if (iResult == SOCKET_ERROR) {
				printf("shutdown failed with error: %d\n", WSAGetLastError());
			}
			return 0;
		}
		iResult = send(s,sendline,strlen(sendline),0);
		if(iResult == SOCKET_ERROR)
		{
			printf("send 函数调用错误,错误号: %ld\n", WSAGetLastError());
			return -1;
		}
		printf("\r\n客户端发送数据:%s\r\n", sendline);
		memset(sendline,0,MAXLINE);
	}
	return iResult;
}




  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
宅急送物流管理系统是一个基于浏览器的B/S架构系统,用于进行各种数据信息的管理功能。它包括前后台系统,实现了线上操作和各项功能,如用户管理、登录注册、权限管理等,同时还能对各类宅急送物流管理系统相关的实体进行管理。 在实现宅急送物流管理系统的课程设计中,需要先收集其他宅急送物流管理系统产品进行分析,然后深入进行web系统的数据库设计,并基于MVC模式进行代码编写。页面搭建方面可以使用Layui进行快速开发,最终完成宅急送物流管理系统各个模块的开发。 随着改革开放和经济发展,我国物流行业快速发展,但在某些地区,物流业的发展还不能适应人们的需求。随着经济的快速发展和信息化的普及,在线物流投递和查询变得越来越重要。为了提高宅急送物流的管理水平,开发了JSP宅急送物流管理系统。该系统包括前端和后端两大板块,功能模块涵盖了系统用户管理、订单管理、车辆管理、企业公告、新闻资讯管理、品牌促销管理、配送网点管理、线路设置、运费设置、系统管理等。其中,用户注册、订单下定和订单查询模块是最重要的功能。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [基于web的宅急送物流管理系统(完整源码+论文全套+教学视频)](https://blog.csdn.net/weixin_39978749/article/details/117236793)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [JSP宅急送物流管理系统](https://blog.csdn.net/QQ55318293/article/details/128171857)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值