天天看点

c语言中网络套接字浅谈

##网络套接字编程##

1.计算机网络基础

每台计算机都需要一个IP地址以标识自己。IP地址由IP协议规定的32位的二进制数表示,最新的IPv6将IP地址升为128位。32位的IP地址主要分为两部分:前缀和后缀。前缀表示计算机所属的物理网络,后缀确定该网络上的唯一一台计算机。在互联网上,每一个物理网络都有唯一的网络号,根据网路号不同,可以将IP地址分5类,A类、B类和C类属于基本类,D类用于多播发送,E类属于保留。

    类型        范围

    A类        0.0.0.0~127.255.255.255

    B类        128.0.0.0~191.255.255.255

    C类        192.0.0.0~223.255.255.255

    D类        224.0.0.0~239.255.255.255

    E类        240.0.0.0~247.255.255.255

特殊的IP地址:

    a.网络地址:在IP地址中主机地址为0的表示网络地址,例如 128.111.0.0。

    b.广播地址:在网络号后跟所有位全是1的IP地址,表示广播地址。

    c.回送地址:127.0.0.1表示回送地址,用于测试。

开放系统互联,简称OSI,是国际标准化组织为了实现计算机网络的标准化而颁布的参考模型。该模型采用分层的划分原则,将网络中的数据传输划分了7层,每层使用下层的服务,并向上层提供服务。

    层次    名称        功能描述

    7    应用层        负责网络中应用程序与网络操作系统之间的联系。

    6    表示层        用于确定数据交换的格式,它能够解决应用程序之间在数据格式上的差异,并负责设备之间所需要的字符集和数据的转换。

    5    会话层        用户应用程序与网络层的接口,它能够建立与其他设备的连接,即会话。并且它能够对会话进行有效的管理

    4    传输层        提供会话层和网络层之间的传输服务,该服务从会话层获得数据,必要时对数据进行分割,然后传输层将数据传递到网络层,并确保数据能正确无误地转送到网络层。

    3    网络层        能够将传输的数据封包,然后通过路由选择、分段组合等控制,将信息从源设备传送到目标设备。

    2    数据链路层    主要是修正传输过程中的错误信号,它能够提供可靠的通过物理介质传输数据的方法。

    1    物理层        利用传输介质为数据链路层提供物理连接,它规范了网络硬件的特性、规格和传输速度。

地址分析,即将计算机的协议地址解析为物理地址(MAC地址,媒体访问控制地址)。通常,在网络上由地址解析协议(ARP)来实现地址解析。两机进行通信时,先从本地ARP缓存中查找对方IP对应的物理地址。若本机未发现,将发送ARP请求帧到本地网络上的所有主机,在ARP请求帧中包含了访问方主机的物理地址和IP地址,找到后双方互相添加对方物理地址和IP地址到自己的ARP缓存中。

域名系统,由于IP地址的易混淆,并且不容易记忆,出现了以主机名家上后缀名的方法标识一台主机,其后缀称为域名。eg:www.baidu.com,主机名:www,域名:baidu.com。这里的域名为二级域名,其中com为一级域名,表示商业组织,baidu为本地域名。为了能够利用域名进行不同主机间的通信,需要将域名解析为IP地址,称为域名解析。域名解析是通过域名服务器来完成的。

TCP/IP协议,即传输控制协议/网际协议,是互联网上最流行的协议,它能够实现互联网上不同类型操作系统的计算机互相通信。该协议将网络分为4层,分别对应OSI参考模型的7层结构。

    TCP/IP协议                OSI参考模型

    应用层(包括Telnet、FTP、SNTP协议)    会话层、表示层和应用层

    传输层(包括TCP、UDP协议)            传输层

    网络层(包括ICMP、IP、ARP等协议)        网络层

    数据链路层                物理层和数据链路层

TCP/IP协议簇包含多种协议,其中主要的协议有网际协议(IP)和传输控制协议(TCP)等。

    a.TCP协议

该协议是一种提供可靠数据传输的通用协议,它是TCP/IP体系结构中传输层上的协议。在发送数据时,应用层的数据传输到传输层,加上TCP的首部,数据就构成了报文。报文是网际层IP的数据,如果加上IP首部,就构成了IP数据报,TCP协议C语言数据描述如下:

    typedef struct HeadTCP

    {

        WORD    SourcePort;    //16位源端口号

        WORD    DePort;        //16位目的端口

        DWORD    SequanceNo;    //32位序号

        DWORD    ConfirmNo;    //32位确认序号

        BYTE    HeadLen;    //与Flag为一个组成部分,首部长度,占4位,保留6位,6位标识,共16位

        BYTE    Flag;

        WORD    WndSize;    //16位窗口大小

        WORD    CheckSum;    //16位校验和

        WORD    UrgPtr;        //16位i紧急指针

    }HEADTCP;

    b.IP协议

即网际协议,工作在网络层,主要提供无链接数据报传输。IP协议不保证数据报的发送,但可以最大限度地发送数据。IP协议C语言数据描述如下:

    typedef struct HeadIP

    {

        unsigned char headerlen:4;    //首部长度,占4位

        unsinged char version:4;    //版本,占4位

        unsigned char servertype;    //服务类型,占8位,即一个字节

        unsigned short totallen;    //总长度,占16位

        unsigned short id;        //与idoff构成标识,共占16位,前3位是标识,后13位时片偏移

        unsigned short idoff;

        unsigned char ttl;        //生存时间,占8位

        unsigned char proto;        //协议,占8位

        unsigned short checksum;    //首部检验和,占32位

        unsigned int sourceIP;        //源IP地址,占32位

        unsigned int destIP;        //目的IP地址,占32位

    }HEADIP;

    c.ICMP协议

即网际控制报文协议,负责网络上设备状态的发送和报文检查,可以将某个设备的故障信息发送到其他设备上。ICMP协议C语言数据描述如下:

    typedef struct HeadICMP

    {

        BYTE Type;    //8位类型

        BYTE Code;    //8位代码

        WORD ChkSum;    //16位检验和

    }HEADICMP;

    d.UDP协议

即用户数据报协议,是一个面向无连接的协议,可使两个应用程序不需要先建立连接,它为应用程序提供一次性的数据传输服务。UDP协议不提供差错恢复,不能提供数据重传,因此该协议传输数据安全性略差。UDP协议C语言数据描述如下:

    typedef struct HeadUDP

    {

        WORD SourcePort;    //16位源端口号

        WORD DePort;        //16位目的端口

        WORD Len;        //16位UDP长度

        WORD ChkSum;        //16位UDP校验和

    }HEADUDP;

端口,用于标识通信的应用程序。当应用程序(严格来说应该是进程)与某个端口绑定后,系统会将收到的给端口的数据送往该应用程序。端口是用一个16位的无符号整数值来表示的,范围为0~65535,低于256的端口被作为系统的保留端口,用于系统进程的通信,不在这一范围的端口号被称为自由端口,可以由进程自由使用。

2.套接字的引入

    套接字(socket)存在于通信区域(地址族)中,主要用于将通过套接字通信的进程的公有特性综合在一起。套接字通常只与同一区域的套接字交换数据。Windows Sockets只支持一个通信区域——AF_INET网络域,使用网际协议族通信的进程使用该域。网络字节顺序采用16位整数和32位整数的高位先存格式(大端)。

    所谓套接字,实际上是一个指向传输提供者的句柄,根据性质和作用的不同,可分为3种,分别为原始套接字,流式套接字(TCP)和数据包套接字(UDP)。

    a.TCP的套接字的socket编程

TCP是面向连接的可靠的传输协议。利用TCP协议进行通信时,首先要建立通信双方的连接。一旦连接建立完成,就可以进行通信。TCP提供了数据确认和数据重传的机制,保证了发送的数据一定能到达通信的对方。

    服务器端程序流程如下:

    1).创建套接字socket

    2).将创建的套接字绑定(bind)到本地的地址和端口上

    3).设置套接字的状态为监听状态(listen),准备接受客户端的连接请求

    4).接受请求(accpet),同时返回得到一个用于连接的新套接字

    5).使用这个新套接字进行通信(通信函数使用send/recv)

    6).通信完毕,释放套接字资源(closesocket)

    客户端程序流程如下:

    1).创建套接字socket

    2).向服务器发出连接请求(connect)

    3).请求连接后与服务器进行通信操作(send/recv)

    4).释放套接字资源(closesocket)

    b.UDP的套接字的socket编程

    UDP是无连接的、不可靠的传输协议。采用UDP进行通信时,不需要建立连接,可以直接向一个IP地址发送数据,但是不能保证对方能收到。对于UDP来说,可以把服务器称为接受端,客户端就是发送数据的发送端。

    发送端程序流程如下:

    1).创建套接字socket

    2).将套接字绑定(bind)到一个本地地址和端口上

    3).等待接收数据(recvfrom)

    4).释放套接字资源(closesocket)

    接收端程序流程如下:

    1).创建套接字socket

    2).向服务器发送数据(sendto)

    3).释放套接字资源(closesocket)

基于UDP的套接字编程时,利用的是sendto和recvfrom这两个函数实现数据的发送和接收;而基于TCP的套接字编程时,发送数据是调用send函数,接受数据使用的是recv函数。

3.套接字函数

    a.WSAStartup函数

该函数的功能是初始化套接字库。wVersionRequested:表示调用者使用的Windows Socket的版本,高字节记录修订版本,低字节记录主版本。eg:Windows Socket 的版本为2.1,则高字节记录1,低字节记录2。IpWSAData:是一个WSADATA结构指针,该结构详细记录了Windows套接字的相关信息。

    该函数原型如下:

        int WSAStartup(WORD wVersion,LPWSADATA IpWSAData);

注意:该函数用于初始化Ws2_32.dll动态链接库,在使用套接字函数之前,一定要初始化该动态链接库。

    b.socket函数

该函数的功能是创建一个套接字。af:表示一个地址家族,通常为AF_INET。tpe:表示套接字类型,如果为SOCK_STREAM,表示创建面向连接的流式套接字;为SOCK_DGRAM,表示创建面向无连接的数据报套接字;为SOCK_RAM,表示创建原始套接字。对于这些值,用户可以在Winsock2.h头文件中找到。potocol:表示套接口所用的协议,如果用户不指定,可以设置为0。返回值:创建的套接字句柄。

    该函数的原型如下:

        SOCKET socket(int af,int type, int protocol);

在代码中,如果socket函数调用成功,它就会返回一个新的SOCKET数据类型的套接字描述符。使用定义好的套接字socket_server进行保存。

    c.bind函数

该函数的功能是将套接字绑定到指定的端口和地址上。s:表示套接字标识。name:是一个sockaddr结构指针,该结构中包含了要结合的地址和端口号。namelen:确定name缓冲区的长度。返回值:如果函数执行成功,返回值为0,否则为SOCKET_ERROR。

    该函数的原型如下:

        int bind (SOCKET s,const struct sockaddr FAR* name,int namelen);

在创建了套接字之后,应该将该套接字绑定到本地的某个地址和端口上,这时就需要该函数了。

    d.listen函数

该函数的功能时将套接字设置为监听模式。对于流式套接字,必须处于监听模式才能够接收客户端套接字的连接。s:表示套接字标识。backlog:表示等待连接的最大队列长度。eg:如果backlog被设置为2,此时有3个客户端同时发出连接请求,那么前2个客户端连接会放置在等待队列中,第3个客户端会得到错误信息。

    该函数的原型如下:

        int listen(SOCKET s,int backlog);

设置套接字为监听状态,为连接做准备,最大等待的数目为5。

    e.accpet函数

该函数的功能是u接受客户端的连接,在流式套接字中,只有在套接字处于监听状态,才能接受客户端的连接。s:是一个套接字,它应处于监听状态。addr:是一个sockaddr_in结构指针,包含一组客户端的端口号、IP地址等信息。addrlen:用于接收参数addr的长度。返回值:一个新的套接字,它对应于已接受的客户端连接,对于该客户端的所有后续操作,都应该使用这个新个套接字。

    该函数的原型如下:

        SOCKET accept(SOCLET s, struct sockaddr FAR* addr,int FAR* addrlen);

    f.closesocket函数

该函数功能书关闭套接字。s:标识一个套接字。如果参数s设置了SO_DONTLINGER选项,则调用该函数后会立即返回,但此时如果有数据尚未传送完毕,则会继续传递数据,然后才关闭套接字。

    该函数的原型如下:

        int closesocket(SOCKET s);

在代码中,socket_receive是一个套接字,当不使用时就可以利用closesocket函数将其套接字的资源进行释放。

    g.connect函数

该函数的功能是发送一个连接请求。s:表示一个套接字。name:表示套接字s要连接的主机地址和端口号。namelen:是name缓冲区的长度。返回值:如果函数执行成功,返回值为0,否则为SOCKET_ERROR。用户可以通过WSAGETLASTERROR得到其错误i描述。

    该函数的原型如下:

        int connect(SOCKET s, const socketaddr FAR* name,int namelen);

    h.htons函数

该函数的功能是将一个16位的无符号短整型数据由主机排列方式转换为网络排列方式。hostshort:是一个主机排列方式的无符号短整型数据。返回值:函数返回值是16位的网络排列方式数据。

    该函数的原型如下:

        u_short htons(u_short hostshort);

    i.htonl函数

该函数的功能是将一个无符号长整型数据由主机排列方式转换为网络排列方式。hostlong:表示一个主机排列方式的无符号长整型数据。返回值:32位的网络排列方式数据。

    该函数的原型如下:

        u_long htonl(u_long hostlong);

    g.inet_addr函数

该函数的功能是将一个由字符表示的地址转换为32位的无符号长整型数据。cp:表示一个IP地址的字符串。返回值:32位无符号长整数。

    该函数的原型如下:

        unsigned long inet_addr(const char FAR*cp);

    k.recv函数

该函数的功能是从面向连接的套接字中接收数据。s:表示一个套接字。buf:表示接收数据的缓冲区。len:表示buf的长度。flags:表示函数的调用方式。如果为MSG_PEEK则表示查看传来的数据,在序列前端的数据会被复制一份到返回缓冲区中,但是这个数据不会从序列中移走;如果为MSG_OOB,则表示用来处理Out_Of_Band数据,也就是外带数据。

    该函数的原型如下:

        int recv(SOCKET s,char FAR* buf,int len,int flags);

    l.send函数

该函数的功能是在面向连接方式的套接字间发送数据。s:表示一个套接字。buf:表示存放要发送数据的缓冲区。len:表示缓冲区长度。flags:表示函数的调用方式。

    该函数的原型如下:

        int send(SOCKET s,const char FAR*buf,int len,int flags);

    m.recvfrom函数

该函数用于接收一个数据宝信息并保存源地址。s:表示准备接收数据的套接字。buf:指向缓冲区的指针,用来接收数据。len:表示缓冲区的长度。flags:通过设置这个值可以影响函数调用的行为。from:是一个指向地址结构的指针,用来接收发送数据方的地址信息。fromlen:表示缓冲区的长度。

    该函数的原型如下:

        int recvfrom(SOCKET s, char FAR* buf int len,intflags,const struct sockaddr FAR* to,int tolen);

    n.sendto函数

该函数的功能是向一个特定的目的发送数据。s:表示一个(可能已经建立连接的)套接字的标识符。buf:指向缓冲区的指针,该缓冲区包含将要发送的数据。len:表示缓冲区的长度。flags:通过设置这个值可以影响函数的调用的行为。to:指定目标套接字的地址。tolen:表示缓冲区的长度。

    该函数的原型如下:

        int sendto(SOCKET s, char FAR* buf int len,intflags,const struct sockaddr FAR* to,int tolen);

    o.WSACleanup函数

该函数的功能是释放为Ws2_32.dll动态链接库初始化时分配的资源。

    该函数的原型如下:

        int WSACleanup(void);

继续阅读