• 模拟telnet协议C语言客户端程序


    首先要了解telnet协议,一下两篇blog给了我初步的思路 https://www.cnblogs.com/liang-ling/p/5833489.html 这篇有比较基础的介绍 以及IAC命令含义解释 https://www.cnblogs.com/image-eye/archive/2012/03/28/2421726.html 这篇就很能抓住重点 另外这位博主使用的是C# 写的程序十分完整 我这里关于IAC指令处理有部分是直接借鉴他的程序 因此注释都没有修改仍然使用的是他的注释

    以上两篇大致看过之后 就可以了解本次po出来的程序了 内容较为朴素

    utils.h

    #ifndef __UTILS__H
    #define __UTILS__H
    
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netdb.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <ctype.h>
    #include <errno.h>
    #include <malloc.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <sys/ioctl.h>
    #include <stdarg.h>
    #include <fcntl.h>
    #include <fcntl.h>
    #include <sys/poll.h>
    #include <signal.h>
    #include <sys/wait.h>
    
    typedef signed long int ssize_t;
    typedef unsigned long int size_t;
    
    ssize_t readn(int fd, void *vptr,size_t n);
    ssize_t writen(int fd, const void *vptr, size_t n);
    ssize_t readline(int fd,void *vptr,size_t maxlen);
    
    #endif
    
    

    utils.c

    
    #include"utils.h"
    
    
    ssize_t readn(int fd, void *vptr,size_t n)
    {
    	size_t nleft;
    	ssize_t nread;
    	char *ptr;
    	
    	ptr = vptr;
    	nleft = n;
    	while(nleft >0)
    	{
    		if((nread = read(fd,ptr,nleft))<0)
    		{
    			if(errno == EINTR)//error:为EAGAIN,表示在非阻塞下,此时无数据到达,立即返回。
                    nread = 0;            //error:为EINTR,表示被信号中断了。
    			else
    				return (-1);
    		}
    		else if(nread == 0)
    			break;
    		else 
    			/* do nothing */
    		nleft -= nread;
    		ptr += nread;
    	}
    	return n-nleft;//实际读了多少字节
    }
    
    ssize_t writen(int fd, const void *vptr, size_t n)
    {
    	size_t nleft;
    	ssize_t nwritten;
    	const char *ptr;
    	
    	ptr = vptr;
    	nleft = n;
    	while(nleft > 0)
    	{
    		if((nwritten = write(fd,ptr,nleft)) < 0)
    		{
    			if(nwritten <0 &&  errno == EINTR)
    			{
    				nwritten = 0;
    			}
    			else
    				return (-1);
    		}
    		else if(nwritten == 0)   
    			break;
    		else //nwritten > 0
    		{
    			/*do nothing*/
    		}
    		nleft = nleft - nwritten;
    		ptr = ptr + nwritten;
    	}
    	return (n- nleft);//实际写了多少字节
    }
    
    ssize_t readline(int fd,void *vptr,size_t maxlen)
    {
    	ssize_t n =0,rc;
    	char c,*ptr;
    	
    	ptr = vptr;
    	while(1)
    	{
    		if((rc = read(fd,&c,1)) == 1)
    		{
    			*ptr++ = c;
    			n++;
    			if(c == '
    ')
    				break;
    		}
    		else if (rc == 0)
    		{
    			*ptr = '';
    			return (n -1);
    		}
    		else
    		{
    			if(errno == EINTR)
    				continue;
    			else
    				return (-1);
    		}
    	}
    	*ptr = '';
    	return n;
    }
    
    

    telnet.h

    
    #ifndef __TELNET__H
    #define __TELNET__H
    
    //<IAC CMD OP >
    
    #define IAC   255
    
    //command word
    #define NUL        0
    #define BEL        7
    #define BS         8
    #define HT         9
    #define LF         10
    #define VT         11
    #define FF         12
    #define CR         13
    #define SE         240
    #define NOP        241
    #define DM         242
    #define BRK        243
    #define IP         244
    #define AO         245
    #define AYT        246
    #define EC         247
    #define EL         248
    #define GA         249
    #define SB         250
    #define WILL       251
    #define WONT       252
    #define DO         253
    #define DONT       254
    
    typedef unsigned char uint8;
    typedef unsigned int  uint32;
    
    
    //operation options
    
    typedef enum tagOPERATION_OPTIONS
    {
        TOPT_BIN = 0,
        TOPT_ECHO = 1,
        TOPT_RECN = 2,
        TOPT_SUPP = 3
    	
    }OPERATION_OPTIONS;
    
    
    uint32 get_every_frame(uint8* recvbuf,uint32 len,uint8* sendbuf,uint32 sendlen);
    
    #endif
    
    

    telnet.c

    
    
    
    
    #include"telnet.h"
    #include<string.h>
    #include<stdio.h>
    #include <stdlib.h>
    
    #define MAXLINE 1024
    #define SEND    1
    #define IS      0
    
    
    
    static uint32 handle_telnetcmd_from_server(uint8* buf,uint32 len,uint8* resp,uint32 n);
    static uint32 process_every_frame(uint8* startByte,uint8* endByte,uint8* sendbuf,uint32 startSendByte);
    
    /* 2020-01-13 begin */
    extern uint32 SOCKFD ;
    extern void telnet_client_send_msg(int fd, const void *vptr, size_t n);
    /* 2020-01-13 end*/
    
    uint32 get_every_frame(uint8* recvbuf,uint32 len,uint8* sendbuf,uint32 sendlen)
    {
    	uint32 i =0,n=0,sum =0;
    	//uint8* p = sendbuf;
    	uint8* pRear = &recvbuf[len];
    	uint8* startByte = recvbuf;
    	uint8* endByte = recvbuf;
    	uint8* strSendToMQTT = NULL;
    	
    	strSendToMQTT = (uint8*)malloc(MAXLINE);
    	if(strSendToMQTT == NULL)
    	{
    		printf("==========strSendToMQTT malloc failed==============
    ");
    		return 0;
    	}
    	
    #if 1	
    	printf("-sum-receivelen----%d-------
    ",len);
    	printf("receive :<*");
    	
    	for(i =0 ;i<len;i++)
    	{
    		printf("%x*",recvbuf[i]);
    	}
    	printf("*>
    ");
    	
    #endif	
    	
    	
    	while(startByte != pRear)
    	{
    		if(*startByte == IAC)
    		{
    			sum = sum + n;
    			switch(*(++endByte))
    			{
    	    /*fa 250 */case SB:while(*(++endByte) != SE){};n = process_every_frame(startByte,endByte,sendbuf,sum);break;
    		/*fb 251 */case WILL:endByte +=2;n = process_every_frame(startByte,endByte,sendbuf,sum);break;
    		/*fc 252 */case WONT:endByte +=2;n = process_every_frame(startByte,endByte,sendbuf,sum);break;
            /*fd 253 */case DO:endByte +=2;n = process_every_frame(startByte,endByte,sendbuf,sum);break;
            /*fe 254 */case DONT:endByte +=2;n = process_every_frame(startByte,endByte,sendbuf,sum);break;	
    		/* 240 */case SE:break;
    		/* sss */default : break;
    			}
                            startByte = endByte;
    		}
    		else
    		{
                            /* 2020-01-13 begin :按照原来的写法 else分支会造成死循环,这里修改 */
    			i = 0;
    			memset(strSendToMQTT,0,MAXLINE);
    			while((startByte != pRear)&&(*startByte != IAC ))
    			{
    				strSendToMQTT[i++] = *startByte ;
    				++startByte;
    			}
    			strSendToMQTT[i] = '';
    			/* 2020-01-13 strSendToMQTT的字符串应当也要发送出去 */
    			telnet_client_send_msg(SOCKFD,strSendToMQTT,i+1);
                           /* 2020-01-13 end :按照原来的写法 else分支会造成死循环,这里修改 */
    		}
    	}
    	if(sum > sendlen)
    	{
    		printf("--error3---sum > MAXLINE-----
    ");
    	}
    	printf("--------------sum is %d ----
    ",sum);
    	return sum;
    }
    
    
    
    static uint32 process_every_frame(uint8* startByte,uint8* endByte,uint8* sendbuf,uint32 startSendByte)
    {
    	uint8 n = 0 ;
    	uint8* pstartByte = startByte;
    	
    	while(pstartByte != endByte)
    	{
    		n++;
    		pstartByte++;
    	}
    	return handle_telnetcmd_from_server(startByte,n,&sendbuf[startSendByte],MAXLINE);
    }
    
    static uint32 handle_telnetcmd_from_server(uint8* buf,uint32 len,uint8* resp,uint32 n)
    {
    	uint32 i =0;
    	uint8 *p = resp;
    	OPERATION_OPTIONS optionCode;
    	uint8 cmdCode,ch;
    	uint32 resplen =0;
    	memset(resp,0,len);
    	//first display cmd from server in string
    	
    #if 1
    	printf("--receivelen----%d-------
    ",len);
    	printf("receive :<*");
    	for(i =0 ;i<len;i++)
    	{
    		printf("%x*",buf[i]);
    	}
    	printf("*>
    ");
    	
    #endif
    
    	if(len < 3)
    	{
    		printf("IAC command length is %d less then 3
    ",len);
    		return -1;
    	}
    	
    	//获得命令码
    	cmdCode = buf[1];
    	//获得选项码
    	optionCode = buf[2];
    	
    	
    	//response requests from server
    	
    	*p = IAC;
    	resplen++;
    	if( optionCode == TOPT_SUPP)//if(optionCode == TOPT_ECHO || optionCode == TOPT_SUPP)
    	{
    		if (cmdCode == DO)
    		{
    			//我设置我应答的命令码为 251(WILL) 即为支持 回显或抑制继续进行
    			ch = WILL;
    			*(++p) = ch;
    			*(++p)= optionCode;
    			resplen += 2;
    
    		}
    		//如果命令码为 254(DONT)
    		else if (cmdCode == DONT)
    		{
    			//我设置我应答的命令码为 252(WONT) 即为我也会"拒绝启动" 回显或抑制继续进行
    			ch = WONT;
    			*(++p)= ch;
    			*(++p)= optionCode;
    			resplen += 2;
    
    		}
    		//如果命令码为251(WILL)
    		else if (cmdCode == WILL)
    		{
    			//我设置我应答的命令码为 253(DO) 即为我认可你使用回显或抑制继续进行
    			ch = DO;
    			*(++p)= ch;
    			*(++p)= optionCode;
    			resplen += 2;
    			//break;
    		}
    		//如果接受到的命令码为251(WONT) 
    		else if (cmdCode == WONT)
    		{
    			//应答  我也拒绝选项请求回显或抑制继续进行
    			ch = DONT;
    			*(++p)= ch;
    			*(++p)= optionCode;
    			resplen += 2;
    			//    break;
    		}
    		//如果接受到250(sb,标志子选项开始)
    		else if (cmdCode == SB)
    		{
    			/*
    			 * 因为启动了子标志位,命令长度扩展到了4字节,
    			 * 取最后一个标志字节为选项码
    			 * 如果这个选项码字节为1(send)
    			 * 则回发为 250(SB子选项开始) + 获取的第二个字节 + 0(is) + 255(标志位IAC) + 240(SE子选项结束)
    			*/
    			ch = buf[3];
    			if (ch == SEND)
    			{
    				ch = SB;
    				*(++p)= ch;
    				*(++p)= optionCode;
    				*(++p)= IS;
    				*(++p)= IAC;
    				*(++p)= SE;
    				resplen += 5;
    			}
    			else
    			{
    				printf("ch != SEND
    ");
    			}
    		}
    		else
    		{
    			/* do nothing */
    		}
    	}
    	else/* 如果选项码不是1 或者3  */
    	{
    		// 底下一系列代表,无论你发那种请求,我都不干
    		if (cmdCode == DO)
    		{
    			ch = WONT;
    			*(++p)= ch;
    			*(++p)= optionCode;
    			resplen += 2;
    		}
    		else if (cmdCode == DONT)
    		{
    			ch = WONT;
    			*(++p)= ch;
    			*(++p)= optionCode;
    			resplen += 2;
    		}
    		else if (cmdCode == WILL)
    		{
    			ch = DONT;
    			*(++p)= ch;
    			*(++p)= optionCode;
    			resplen += 2;
    		}
    		else if (cmdCode == WONT)
    		{
    			ch = DONT;
    			*(++p)= ch;
    			*(++p)= optionCode;
    			resplen += 2;
    		}
    		else
    		{
    			/* do nothing */
    		}
    			
    	}
    	
    #if 1
    	printf("--resplen---%d-------
    ",resplen);
    	printf("response :<*");
    	for(i =0 ;i<resplen;i++)
    	{
    		printf("%x*",resp[i]);
    	}
    	printf("*>
    ");
    #endif	
    	
    	
    	if(n < resplen )
    	{
    		printf("error n < resplen !!! 
    ");
    	}
    	if(resplen < 3 )
    	{
    		printf("resplen < 3 
    ");
    	}
    	return resplen;
    }
    
    
    

    client.c

    
    //gcc client.c -o client
    
    
    #include"utils.h"
    #include"telnet.h"
    
    
    #define IP_ADDRESS   "127.0.0.1"
    #define IP_PORT      23
    #define SERV_PORT    3333
    #define MAXLINE      1024
    
    typedef struct sockaddr SA;
    
    
    void str_cli(FILE *fp,uint32 sockfd);
    uint32 max(uint32 a,uint32 b);
    void ERR_EXIT(char* s);
    
    uint32 main(uint32 argc,uint32 **argv)
    {
    	uint32 sockfd,isReady=0;
    	struct sockaddr_in servaddr;
    	uint32 hname[128];
    
    	sockfd = socket(AF_INET,SOCK_STREAM,0);
    	bzero(&servaddr,sizeof(servaddr));
    	servaddr.sin_family = AF_INET;
    	servaddr.sin_port = htons(IP_PORT);
    	servaddr.sin_addr.s_addr = inet_addr(IP_ADDRESS);
    	printf("servaddr: IP is %s, Port is %d
    ",inet_ntoa(servaddr.sin_addr), ntohs(servaddr.sin_port));
    
    	while(connect(sockfd,(SA*)&servaddr,sizeof(servaddr))){};
    	printf("connect has been ready
    ");
    	
    	str_cli(stdin,sockfd);
    	exit(0);
    	return 0;
    }
    void ERR_EXIT(char* s)
    {
    	perror(s);
    	exit(EXIT_FAILURE);
    }
    void INFO_PRINT(char* s)
    {
    	printf("%s",s);
    }
    uint32 max(uint32 a,uint32 b)
    {
    	return (a>b?a:b);
    }
    
    void str_cli(FILE *fp,uint32 sockfd)
    {
    	uint32 maxfdp1,nready;//stdineof;
    	fd_set rset;
    	uint8 buf[MAXLINE];
    	uint8 respbuff[MAXLINE] = {0};;
    	uint32 resplen;
    	uint32 n;
    	uint8 echo_cmd[] = {0xff,0xfb,0x01};
    	//stdineof = 0;
    	FD_ZERO(&rset);
    	writen(sockfd,echo_cmd,3);
    	
    	for(;;)
    	{
    		//if(stdineof == 0)
    		FD_SET(fileno(fp),&rset);
    		FD_SET(sockfd,&rset);
    		maxfdp1 = max(fileno(fp),sockfd)+1;
    		nready = select(maxfdp1,&rset,NULL,NULL,NULL);
    		
    		if(nready < 0)
    		{
    			ERR_EXIT("ERROR!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
    		}
    		
    		if(FD_ISSET(sockfd,&rset))
    		{
    			memset(buf,0,MAXLINE);
    			if((n = read(sockfd,buf,MAXLINE))==0)
    			{
    				ERR_EXIT("str_cli:server termination prematurely");
    			}
    			buf[n] = '';
    			//printf("FD_ISSET(sockfd,&rset)-------------%s
    ",buf);
    			
    			if(buf[0] == IAC)
    			{
    				memset(respbuff,0,MAXLINE);
    				resplen = get_every_frame(buf,n,respbuff,MAXLINE);
    				writen(sockfd,respbuff,resplen);
    			}
    			else
    			{
    				telnet_client_send_msg(fileno(stdout),(char *)buf,n);
    			}
    			
    			//writen(fileno(stdout),buf,n);
    		}
    		if(FD_ISSET(fileno(fp),&rset))
    		{
    			memset(buf,0,MAXLINE);
    			if((n = readline(fileno(fp),(char *)buf,MAXLINE)) == 0)
    			{
    				//stdineof = 1;//此时碰到EOF 并且马上要发生FIN序列 所以标准输入不可读了
    				shutdown(sockfd,SHUT_WR);
    				FD_CLR(fileno(fp),&rset);
    				INFO_PRINT("nothing input!");
    				continue;
    			}
    			else if(n >0)
    			{
    				/* do nothing */
    			}
    			else
    			{
    				ERR_EXIT("some error occurred ");
    			}
    			//printf("FD_ISSET(fileno(fp),&rset)----%d--
    ",n);
    			//memset(buf,0,MAXLINE);
    			telnet_client_send_msg(sockfd,(char *)buf,n);
    		}
    	}
    }
    

    makefile

    all:Client_telnet
    	@echo ""
    	@echo "This is telnet Client compile......."
    	@echo ""
    	
    Client_telnet:client.o utils.o telnet.o
    	gcc -g -o Client_telnet client.o utils.o telnet.o
    	
    client.o:client.c utils.h telnet.h
    	gcc -g -c client.c
    	
    utils.o:utils.c utils.h
    	gcc -g -c utils.c 
    	
    telnet.o:telnet.c telnet.h
    	gcc -g -c telnet.c
    	
    clean :
    	-rm client.o utils.o telnet.o Client_telnet
    

    以上为本次程序使用的源码 程序在Linux系统上运行方式为 ./Client_telnet

    然后是运行截图如此下:

    以上

  • 相关阅读:
    登录页面实现
    DECODE异常: ORA01722: invalid number
    自定义百度地图样式
    百度地图在VUE项目中插入点位及点位信息弹框
    AtCoder Regular Contest 133 题解 A C
    Codeforces Round #767 (Div. 1) 题解 A – D2
    JDBC基本案例
    数据输入
    分支语句
    循环语句
  • 原文地址:https://www.cnblogs.com/y-c-y/p/11685405.html
Copyright © 2020-2023  润新知