5.win32网络编程(一)

来源:互联网 发布:网络话费卡怎么用 编辑:程序博客网 时间:2024/06/01 07:47

实习完毕,用习惯的mac没了,再回来windows这边的话,估计也是研究的win32网络编程的比较多了。

这篇文章的源代码,是来自之前我做过的一个网安实验——端口复用实验

自我感觉,网络编程都是有那么几个固定步骤的——仿佛套路一般。

这里我是参考了一篇博客的:http://www.cnblogs.com/tianya2543/p/3889495.html

运行环境 : win7下的vc++

具体的解析可以看源代码中的注释:

客户端 client.cpp:

/*----     win32网络编程几步走 (客户端)    ------*/ #include <WINSOCK2.H>#include <STDIO.H>#include <iostream>using namespace std;#pragma  comment(lib,"ws2_32.lib")int main(int argc, char* argv[]){    WORD sockVersion = MAKEWORD(2,2);    WSADATA data;     if(WSAStartup(sockVersion, &data) != 0)    {        return 0;    }    /* ---- 1、创建套接字(socket) ------ */     SOCKET sclient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);    if(sclient == INVALID_SOCKET)    {        printf("invalid socket !");        return 0;    }    sockaddr_in serAddr;    serAddr.sin_family = AF_INET;    serAddr.sin_port = htons(80);    serAddr.sin_addr.S_un.S_addr = inet_addr("172.16.0.15");     /* -------  2、向服务器发出连接请求(connect) ------ */     if (connect(sclient, (sockaddr *)&serAddr, sizeof(serAddr)) == SOCKET_ERROR)    {cout<<WSAGetLastError()<<endl;        printf("connect error !");        closesocket(sclient);        return 0;    }    char * sendData = "Y好,TCP服务端,我是客户端!\n";    /*------- 3、和服务器端进行通信(send/recv)-------- */     send(sclient, sendData, strlen(sendData), 0);    char recData[4096];    int ret = recv(sclient, recData, 4096, 0);    if(ret > 0)    {        recData[ret] = 0x00;        printf(recData);    }    /*------------------- 4、关闭套接字 -------------------*/     closesocket(sclient);    WSACleanup();    return 0;}
服务端 server.cpp:

/*--   探寻win32网络编程的套路和步骤 (服务端)   -- */ #include<stdio.h>#include<WINSOCK2.H>  //加入socket的头文件与链接库#pragma comment(lib,"Ws2_32.lib")  //端口复用程序包含监听与连接两种功能的Socketvoid proc(LPVOID d);  //工作线程int main(int argc,char * argv[]){WSADATA wsaData;WSAStartup(MAKEWORD(2,2),&wsaData);  //socket版本SOCKADDR_IN a,b;//一个是用于外部监听的地址,另一个是接收到accept时用于处理接收的结构a.sin_family=AF_INET; a.sin_addr.s_addr=inet_addr(argv[1]);a.sin_port=htons(80); SOCKET c;/*------------ 1、创建套接字(socket),返回套接字号 ---------*/ c = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); //int socket( int af, int type, int protocol);bool l = TRUE;  //这个l有什么用呢?setsockopt(c,SOL_SOCKET,SO_REUSEADDR,(char *)&l,sizeof(l)); //实现端口的重绑定/*------ 2、将套接字绑定到一个本地地址和端口上(bind) ----- */bind(c,(sockaddr *)&a,sizeof(a)); //c和a绑定/*--- 3、将套接字设为监听模式,准备接收客户请求(listen) ---*/listen(c,100);   //监听while(1){int x;x = sizeof(b);/* 4、等待客户请求到来;当请求到来后,接受连接请求,返回一个新的对应于此次连接的套接字(accept)*/SOCKET d=accept(c,(sockaddr *)&b,&x); //监听的socket只有一个,而处理接收到的Socket可有多个,个数由连接数决定CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)proc,(LPVOID)d,0,0); //开始处理线程}/*-------  6、返回,等待另一客户请求。 7、关闭套接字 -----*/closesocket(c);return 0;}void proc(LPVOID d){//这个线程的功能是如果鉴定是否是木马程序发送的信息,如果不是就发回给本地的web服务器 SOCKADDR_IN sa;   //用于连接web 80端口的Socket结构,sa.sin_family = AF_INET;sa.sin_addr.s_addr=inet_addr("127.0.0.1");sa.sin_port=htons(80);SOCKET web=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);connect(web,(sockaddr *)&sa,sizeof(sa));char buf[4096];SOCKET ss = (SOCKET) d;while(1){/*----- 5、用返回的套接字和客户端进行通信(send/recv) ----*/int n = recv(ss,buf,4096,0);if(n==0) break;if(n > 0 && buf[0] == 'y')//木马特征值为buf[0] == 'y'{send(ss,"hello!,my hacket master!",25,0);}else{send(web,buf,n,0);n=recv(web,buf,4096,0);if(n == 0) break;else send(ss,buf,n,0);}}closesocket(ss);}
再看到当记录的笔记:
sockaddr用于存储参与(IP)Windows套接字通信的计算机上的一个internet协议(IP)地址。
为了统一地址结构的表示方法,统一接口函数,使得不同的地址结构可以被bind()、connect()、recvfrom()、sendto()等函数调用。
但一般的编程中并不直接对此数据结构进行操作,而使用另一个与之等价的数据结构sockaddr_in。
struct sockaddr_in {  short int sin_family;   unsigned short int sin_port;   struct in_addr sin_addr;   unsigned char sin_zero[8];   };  sin
_family:指代协议族,在socket编程中只能是AF_INET,   Address family
  sin_port:存储端口号(使用网络字节顺序)
  sin_addr:存储IP地址,使用in_addr这个数据结构(Internet address) 
  sin_zero:是为了让sockaddr与sockaddr_in两个数据结构保持大小相同而保留的空字节。
Same size as struct sockaddr
而其中in_addr结构的定义如下:
typedef struct in_addr {  union {  struct{ unsigned char s_b1,s_b2, s_b3,s_b4;} S_un_b;struct{ unsigned short s_w1, s_w2;} S_un_w;  unsigned long S_addr;  } S_un;  } IN_ADDR;
阐述下in_addr的含义,很显然它是一个存储ip地址的共用体有三种表达方式:
第一种用四个字节来表示IP地址的四个数字;
第二种用两个双字节来表示IP地址;
第三种用一个长整型来表示IP地址。
给in_addr赋值的一种最简单方法是使用inet_addr函数,它可以把一个代表IP地址的字符串赋值转换为in_addr类型
addrto.sin_addr.s_addr=inet_addr("192.168.0.2");
其反函数是inet_ntoa,可以把一个in_addr类型转换为一个字符串
htons是将整型变量从主机字节顺序转变成网络字节顺序, 
就是整数在地址空间存储方式变为:高位字节存放在内存的低地址处。
网络字节顺序是TCP/IP中规定好的一种数据表示格式,它与具体的CPU类型、操作系统等无关,
从而可以保证数据在不同主机之间传输时能够被正确解释,网络字节顺序采用big-endian排序方式。
所谓big-endian,可以参考博客: http://blog.csdn.net/andkylee/article/details/5361078
重点:     对于跨越多个字节的对象,一般它所占的字节都是连续的,它的地址等于它所占字节最低地址。
网络传输是以字节为单位进行的

但是里面有一段代码,我看了好久也没看懂原理,
#include <stdio.h>#include <assert.h>#include<iostream>using namespace std;int main( void ){        short test;        FILE* fp;                 test = 0x3132; //(31ASIIC码的’1’,32ASIIC码的’2’)        cout<<test<<endl; //等于 12594         if ((fp = fopen ("d:/test.txt", "wb")) == NULL)              assert(0);        cout<<fwrite(&test, sizeof(short), 1, fp)<<endl; //对了,fwrite是一个个字节写进去的!        fclose(fp);}
后来终于看懂了!首先,ascii的0代表的是空字符,0x31是一个字节,0x32是一个字节

按照大端小端的区别,就是大端12,小端21


原创粉丝点击