Windows C语言 Socket编程 server端(服务器)--初级(简单版)

下面这段代码实现了最基本的server代码,只适用于初学者研究最基本的socket服务端的基本流程。
同一时间只能对一个客户端进行通信。若想实现多客户端连接需要引入多线程的概念。
与本章配套的还有另外一篇文章“Windows C语言 Socket编程 client端(客户端)–初级(简单版)”

#include 
#include 

#pragma comment(lib,"ws2_32.lib")

static SOCKET socket_of_server;  //服务端(本地)的socket
static struct sockaddr_in s_sin; //用于存储本地创建socket的基本信息
static SOCKET socket_of_client;  //客户端(远程)的socket
static struct sockaddr_in c_sin; //用于存储已连接的客户端的socket基本信息
static int    c_sin_len;         //函数accept的第三个参数,c_sin的大小。

static void analysis(char* data, int datal);


int main(int argc, char* argv[])
{
    char revData[255];//这个地方一定要酌情设置大小,这决定了每次能获取多少数据
    int  ret;//recv函数的返回值 有三种状态每种状态的含义在下方有解释
    int port = 6666;//需要开启的服务端口

    WORD socket_version = MAKEWORD(2,2);
    WSADATA wsadata;
    if(WSAStartup(socket_version, &wsadata)!=0)
    {
        return 0;
    }
    /************************************************************************
     注:这个地方不要像下面这么写
     WSADATA *wsadata;
     if(WSAStartup(socket_version, wsadata)!=0)
     。。。
     这样的wsadata是没有分配空间的,这么写会提示wsadata没有初始化。
    ************************************************************************/
    socket_of_server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    //创建socket 并判断是否创建成功
    if(socket_of_server == INVALID_SOCKET)
    {
        printf("socket error\n");
        return 0;
    }

    s_sin.sin_family = AF_INET;  //定义协议族为IPV4
    s_sin.sin_port = htons(port);//规定端口号
    s_sin.sin_addr.S_un.S_addr = INADDR_ANY;
    /************************************************************************
     s_sin.sin_addr.S_un.S_addr = INADDR_ANY; 是在设定绑定在本机的哪个IP地址上,
     在哪个IP地址上进行监听。设定为INADDR_ANY代表0.0.0.0就是默认IP。
     正常个人编程时 这个地方无关紧要。但若真正应用时这个地方最好设置清楚。
     因为好多服务器是多个网卡,本机有多个IP。哪个网卡是连接服务所在局域网的就要
     设置为哪个。
    ************************************************************************/
    if(bind(socket_of_server, (LPSOCKADDR)&s_sin, sizeof(s_sin)) == SOCKET_ERROR)//绑定
    {
        printf("bind error\n");
    }

    if(listen(socket_of_server, 5) == SOCKET_ERROR)//监听
    {
        printf("listen error\n");
        return 0;
    }


    c_sin_len = sizeof(c_sin);
    while (1)
    {
        printf("waiting for connect\n");
        socket_of_client = accept(socket_of_server, (SOCKADDR *)&c_sin, &c_sin_len);
        /************************************************************************
         没有新的连接是 程序不会一直在这里循环。此时accept会处于阻塞状态。
         直到有新的连接,或者出现异常。
        ************************************************************************/
        if(socket_of_client == INVALID_SOCKET)
        {
            printf("accept error\n");
            continue; //继续等待下一次连接
        }
        else
        {
            printf("new connection , Ip = %s\n", inet_ntoa(c_sin.sin_addr));
            send(socket_of_client, "hello i am server \n", strlen("hello i am server \n"), 0);
            while(1)
            {
                //接收来自 这个客户端的消息
                ret = recv(socket_of_client, revData, 255, 0);        
                /************************************************************************
                recv函数 的实质就是从socket的缓冲区里拷贝出数据 
                返回值就是拷贝出字节数的大小。
                上面定义的载体(revData)大小是255,所以recv的第三个参数最大只能设置为255,
                如果设置为大于255的数值,当执行recv函数时恰好缓冲区的内容大于255,
                就会导致内存泄漏,导致ret值小于零,解除阻塞状态。因此这里最好将第三个参数
                设置为revData的大小,那么当缓冲区内的数据小于255的时候
                只需要执行一次recv就可以将缓冲区的内容都拷贝出来,但当缓冲区的数据大
                于255的时候,就要执行多次recv函数。当缓冲区内没有内容的时候,会处于阻塞
                状态,这个while函数会停在这里。直到新的数据进来或者出现异常。
                ************************************************************************/

                if(ret > 0)
                {
                    revData[ret] = 0x00;//正常情况下不必这么做,我这么做只是为了能按字串的形式输出它
                    analysis(revData, ret);
                }
                else if(ret == 0)
                {
                    //当ret == 0 说明客户端已断开连接。这里我们直接跳出循环去等待下次连接即可。
                    printf("lost connection , Ip = %s\n", inet_ntoa(c_sin.sin_addr));
                    break;
                }
                else//ret < 0
                {
                    //当ret < 0 说明出现了异常 例如阻塞状态解除,或者读取数据时出现指针错误等。
                    //所以我们这里要主动断开和客户端的链接,然后跳出循环去等待新的连接。
                    printf("something wrong of %s\n", inet_ntoa(c_sin.sin_addr));
                    closesocket(socket_of_client);
                    break;  
                }
            }
        }
    }

    closesocket(socket_of_server);
    WSACleanup();
    return 0;
}



static void analysis(char* data, int datal)
{
    printf("recv data:%s  datal:%d\n", data, datal);
    //在这里我们可以对已接收到的数据进行处理

    //一般情况下这里都是处理“粘包”的地方

    //解决粘包之后 将完整的数据发送给数据处理函数
}

你可能感兴趣的:(C/C++)