c调用Windows中winsocket相关函数实现简单的Web服务器

Java程序通过封装Windows API,提供给用户相关Socket类,实现一个简单的Web服务器较好理解。用c语言直接调用Windows API 中的winsocket相关函数实现简单的Web服务器,相对来说就复杂些了。通过了解这个实现过程,也能更好的理解Java封装后,实现开发的快捷和易于学习。

下面是这个小程序的C语言源代码:

#include <stdio.h>
//Winsock2.h头文件,里面有多种函数可以帮助使用Winsock.
//Winsock是Windows下网络编程的规范,该规范是Windows下得到广泛应用的、开放的、支持多种协议的网络编程接口. 
#include <Winsock2.h>
//连接Ws2_32.lib这个库,ws2_32.lib是winsock2的库文件.
#pragma comment(lib,"ws2_32.lib")
main()
{
     //typedef unsigned short WORD; WORD是无符号短整型的别名. 
    WORD wVersionRequested;  
    //WSA(Windows Sockets Asynchronous,Windows异步套接字),不少标识符用它做前缀.  
    WSADATA wsaData; 
    int err;
   //程序使用1.1版本的Socket,宏MAKEWORD( 1, 1 )的值是257的短整数,占两个字节,每个字节都是1.
    wVersionRequested = MAKEWORD( 1, 1 ); 
    /*
    (1)WSAStartup()负责WSA(Windows Sockets Asynchronous,Windows异步套接字)的启动。当应用程序调用WSAStartup函数时,
    操作系统根据请求的Socket版本来搜索相应的Socket库,然后绑定找到的Socket库到该应用程序中。
    以后应用程序就可以调用所请求的Socket库中的其它Socket函数(Windows Sockets API函数)了。
    该函数执行成功后返回0。 
   (2)WSA是windows提供的一个与网络协议无关的编程接口,也就是说不仅支持TCP/IP协议族。
    (3) WSADATA是结构体类型,用来保存函数WSAStartup执行后返回的Windows Sockets初始化信息
       */
    err = WSAStartup( wVersionRequested, &wsaData );
    //返回值为0,WSAStartup()执行成功 
    if ( err != 0 ) {
        return;
    }
   //宏 LOBYTE和 HIBYTE分别检查 版本号的低8位和高8位是否是1. 
    if ( LOBYTE( wsaData.wVersion ) != 1 ||
    HIBYTE( wsaData.wVersion ) != 1 ) {
        //有一个不是1,注销 WSA的启动,并释放资源 
        WSACleanup();
        return;
    }
    /*
      socket(AF_INET,SOCK_STREAM,0)函数的三个参数分别是:
      (1)AF,address family,用来指定使用的地址族(也可理解为协议族,协议族不同,地址形式也不同)。AF_INET的值是2,表示使用IPV4的地址形式 
      (2)通信类型。SOCK_STREAM的值是1,指定使用 字节流的套接字  通信类型。 
      (3)协议类型。IPV4中仅有一种协议类型,只能指定为0. 
      如果函数调用成功,会返回一个标识这个套接字的文件描述符(是个整数,每次连接都不同),失败的时候返回-1。
    */ 
    SOCKET sockSrv=socket(AF_INET,SOCK_STREAM,0);
    //设置socket 地址属性 
    SOCKADDR_IN addrSrv;
     //htonl()函数将一个32位数从主机字节顺序转换成无符号长整型的网络字节顺序(host to net long) .
    addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
    addrSrv.sin_family=AF_INET;
    addrSrv.sin_port=htons(9000);
   //bind(SOCKET s,const struct sockaddr *name,int namelen);
    bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));
    //listen(SOCKET s,int backlog);backlog指监听队列最大监听连接数 
    listen(sockSrv,5);
   //addrClient用于存放客户端socket 的地址属性 
    SOCKADDR_IN addrClient;
    //SOCKADDR结构体,用于存储参与(IP)Windows/linux套接字通信的计算机上的一个internet协议(IP)地址,长度是16字节 
    int len=sizeof(SOCKADDR);
    while(1)
    {
        //accept(SOCKET s,struct sockaddr *addr,int *addrlen)获取客户端的socket 
        SOCKET sockConn=accept(sockSrv,(SOCKADDR*)&addrClient,&len);
        char sendBuf[500];
        //sprintf()函数把目标字符串装到 sendBuf数组中,第一个参数是指针类型。 
        sprintf(sendBuf,"HTTP/1.1 200 ok\r\nContent-Type:text/html\r\n\r\nWelcome %s to here!",inet_ntoa(addrClient.sin_addr));
        //strlen(sendBuf)+1,加1是让发送缓冲区大于要发送的字节数,第四个参数是传送方式(比如要传输带外数据),一般设置0即可。 
        send(sockConn,sendBuf,strlen(sendBuf)+1,0);
        char recvBuf[1500];//网络最大传输单元1500字节,在这两就设定这个大小就可以了 
        recv(sockConn,recvBuf,1500,0);
        printf("%s\n",recvBuf);
        closesocket(sockConn);
    }
}

代码使用 dev-C++5.11编辑和运行。在运行时注意添加一项编译配置, 在连接器命令行中添加“-lwsock32”命令。"工具"-"编译配置",如下图:

代码运行后,在浏览器中的访问如下图:

在服务器端看到浏览器的请求头如下图:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值