您的位置:首页 > 理论基础 > 计算机网络

vc简易网络服务器、客户端实现

2015-01-08 11:56 399 查看
一、服务器部分代码
头文件:server.h#include <winsock2.h>
#include <stdio.h>
#include <windef.h>

#ifndef TCP_SERVER_H
#define TCP_SERVER_H

#pragma warning(disable : 4996)

#define MAX_CLIENT          (100)
#define LISTEN_PORT         (60000)
#define CLIENT_TIMEOUT      (15 * 60 * 60)  /*客户端中断15分钟则服务器自动掉线*/
#define MAX_TCP_PACKET_SIZE (1492)/*tcp最大数据包字节数*/

typedef struct __client_item
{
    SOCKET client;
    sockaddr_in form;
    unsigned int id;
    DWORD dwThreadId;
    HANDLE hThread;
}_client_item;

typedef struct __client_list
{
    DWORD client_cnt;
    _client_item client_item[MAX_CLIENT];
}_client_list;

DWORD WINAPI serve_client_thread_func( LPVOID param_ptr );

#endif/*TCP_SERVER_H*/
源文件:server.cpp#include "server.h"
#include <sys\stat.h>
#include <time.h>

_client_list client_list;

CRITICAL_SECTION cs_serve_main;
CRITICAL_SECTION cs_serve_thread;

_client_list *get_client_list(void)
{
    return &client_list;
}

void client_list_cnt_inc(void)
{
    _client_list *client_list_ptr = get_client_list();
    
    client_list_ptr->client_cnt++;
}

void client_list_cnt_dec(void)
{
    _client_list *client_list_ptr = get_client_list();

    client_list_ptr->client_cnt--;
}

unsigned int get_free_client_index(void)
{
    int i = 0;
    unsigned int id = 0;
    _client_list *client_list_ptr = get_client_list();

    for(i = 0; i < MAX_CLIENT; i++)
    {
        id = client_list_ptr->client_item[i].id;
        if( UINT_MAX == id )
        {
            break;
        }
    }

    return i;
}

_client_item *get_client_item(unsigned int index)
{
    _client_list *client_list_ptr = get_client_list();

    if( index >= MAX_CLIENT )
    {
        return NULL;
    }

    return &client_list_ptr->client_item[index];
}

void set_client_item(unsigned int index, _client_item *client_item_ptr)
{
    _client_list *client_list_ptr = get_client_list();
    
    if( index >= MAX_CLIENT )
    {
        return;
    }

    client_list_ptr->client_item[index] = *client_item_ptr;
}

SOCKET g_sk_server;

int main()
{
    SOCKET server;
    WSADATA wsaData;
    sockaddr_in local;
    int nRet = 0;
    int sockaddr_in_sizeof = 0;
    unsigned int index = 0;
    _client_item *client_item_ptr = NULL;
    _client_item client_item = {0};
    _client_list *client_list_ptr = NULL;
    SOCKET client;
    sockaddr_in from;
    DWORD dwThreadId;
    HANDLE hThread;

    nRet = WSAStartup(0x101, &wsaData);

    if( 0 != nRet )
    {
        return 0;
    }
    

    // 现在我们来为sockaddr_in结构赋值。
    local.sin_family = AF_INET; // 地址族
    local.sin_addr.s_addr = INADDR_ANY; // 网际IP地址
    local.sin_port = htons(LISTEN_PORT); // 使用的端口

    // 由socket函数创建我们的SOCKET。
    server = socket(AF_INET, SOCK_STREAM, 0);
    //server = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP,NULL, 0, WSA_FLAG_OVERLAPPED); 

    // 如果socket()函数失败,我们就退出。
    if( server == INVALID_SOCKET )
    {
        return 0;
    }

    // bind将我们刚创建的套接字和sockaddr_in结构联系起来。
    // 它主要使用本地地址及一个特定的端口来连接套接字。
    // 如果它返回非零值,就表示出现错误。
    nRet = bind(server, (sockaddr*)&local, sizeof(local));
    if( 0 != nRet )
    {
        return 0;
    }

    // listen命令套接字监听来自客户端的连接。
    // 第二个参数是最大连接数。
    nRet = listen(server, MAX_CLIENT);
    if( 0 != nRet )
    {
        return 0;
    }

    g_sk_server = server;

    // 我们需要一些变量来保存客户端的套接字,因此我们在此声明之。
    InitializeCriticalSection(&cs_serve_main);/*初始化临界区*/
    InitializeCriticalSection(&cs_serve_thread);/*初始化临界区*/
    client_list_ptr = get_client_list();
    memset(client_list_ptr, 0xff, sizeof(_client_list));
    memset(&client, 0, sizeof(SOCKET));
    memset(&from, 0, sizeof(sockaddr_in));

    sockaddr_in_sizeof = sizeof(sockaddr_in);

    while( TRUE )
    {
        index = get_free_client_index();

        if( index >= MAX_CLIENT )
        {/*已经达到最大连接数*/
            continue;
        }

        client_item_ptr = get_client_item(index);

        client_item_ptr->client = accept(server, 
            (struct sockaddr*)&client_item_ptr->form, &sockaddr_in_sizeof);

        if( INVALID_SOCKET == client_item_ptr->client )
        {/*连接错误*/
            Sleep(500);
            continue;
        }else
        {
            printf("Connection from %s\n", inet_ntoa(client_item_ptr->form.sin_addr) );
        }

        /* 多线程开始*/
        EnterCriticalSection(&cs_serve_main);/*进入临界区*/
        hThread = CreateThread( 
            NULL,                        // no security attributes 
            0,                           // use default stack size  
            serve_client_thread_func,                  // thread function 
            client_item_ptr,                // argument to thread function 
            CREATE_SUSPENDED,                           // use default creation flags 
            &dwThreadId);                // returns the thread identifier 

        if( NULL != hThread )
        {
            client_item_ptr->hThread = hThread;
            client_item_ptr->dwThreadId = dwThreadId;
            client_item_ptr->id = index;
        }
        client_list_cnt_inc();

        LeaveCriticalSection(&cs_serve_main);/*离开临界区*/

        if( NULL != client_item_ptr->hThread )
        {
            ResumeThread(client_item_ptr->hThread);
        }

        client_item_ptr = NULL;
        /*end 多线程开始*/
    }

    //关闭套接字,并释放套接字描述符。
    closesocket(server);
    WSACleanup();

    DeleteCriticalSection(&cs_serve_thread);/*删除临界区*/
    DeleteCriticalSection(&cs_serve_main);/*删除临界区*/

    return 0;
}

void serve_client_thread_clear(SOCKET sock_client, 
                               sockaddr_in *sockaddr_client_ptr, 
                               UINT32 client_id, 
                               char *msg,
                               _client_item *client_item_ptr
                               )
{
    int msg_len = strlen(msg);

    if( msg_len > 0 )
    {
        send(sock_client, msg, msg_len + 1, 0);
    }

    closesocket(sock_client);/*关闭连接*/

    client_list_cnt_dec();
    
    memset(client_item_ptr, 0, sizeof(_client_item));
    set_client_item(client_id, client_item_ptr);
}

DWORD WINAPI serve_client_thread_func(LPVOID param_ptr) 
{
    char buf[MAX_TCP_PACKET_SIZE];
    _client_item *client_item_ptr = (_client_item *)param_ptr;
    SOCKET sock_client = 0;
    sockaddr_in sockaddr_client = {0};
    unsigned int client_id = 0;
    _client_list *client_list_ptr = get_client_list();
    int recv_size = 0;
    time_t sock_free_cur_time = 0;
    time_t sock_free_kill_time = 0;
    
    int n_ret = 0;
    fd_set fdread;
    timeval tv;

    while( NULL == client_item_ptr->hThread );

    sock_client = client_item_ptr->client;
    sockaddr_client = client_item_ptr->form;
    client_id = client_item_ptr->id;

    sprintf(buf, "%s:%d you id=%d\n", inet_ntoa(sockaddr_client.sin_addr), 
        sockaddr_client.sin_port, client_id);

    send(sock_client, buf, strlen(buf) + 1, 0);

    do 
    {
        /*这部分代码在此处必须每次都执行*/
        FD_ZERO(&fdread);//初始化fd_set
        FD_SET(sock_client, &fdread);//分配套接字句柄到相应的fd_set
        tv.tv_sec = 1;//这里我们打算让select等待1s后返回,避免被锁死,也避免马上返回
        tv.tv_usec = 0;
        /*end 这部分代码在此处必须每次都执行*/

        select(0, &fdread, NULL, NULL, &tv);
        n_ret = FD_ISSET(sock_client, &fdread);
        if( 0 == n_ret )/*没有数据*/
        {
            continue;
        }

        recv_size = recv(sock_client, buf, sizeof(buf), 0);
        if( recv_size <= 0)
        {/*socket 错误,断开连接并退出*/
            /*超时,线程退出*/
            printf("client time out ip=%s:port=%d:id=%d\n", 
                inet_ntoa(sockaddr_client.sin_addr), sockaddr_client.sin_port, client_id);

            EnterCriticalSection(&cs_serve_thread);/*进入临界区*/

            memset(buf, 0, sizeof(buf));
            serve_client_thread_clear(sock_client, &sockaddr_client, client_id, buf, client_item_ptr);

            LeaveCriticalSection(&cs_serve_thread);/*离开临界区*/

            ExitThread(0);/*退出线程*/
        }
        sock_free_cur_time = 0;

        if( ('q' == buf[0]) && (0 == buf[1]) )
        {/*客户端主动退出*/
            sprintf(buf, "client exit server ip=%s:port=%d:id=%d\n", 
                inet_ntoa(sockaddr_client.sin_addr), sockaddr_client.sin_port, client_id);

            EnterCriticalSection(&cs_serve_thread);/*进入临界区*/

            serve_client_thread_clear(sock_client, &sockaddr_client, client_id, buf, client_item_ptr);

            LeaveCriticalSection(&cs_serve_thread);/*离开临界区*/

            ExitThread(0);/*退出线程*/
        }
        printf("ip=%s:port=%d:id=%d say:%s\n", inet_ntoa(sockaddr_client.sin_addr), 
                sockaddr_client.sin_port, client_id, buf);
    }while( TRUE );

    return 0; 
}
2、客户端代码
头文件:client.h#include <winsock2.h>
#include <stdio.h>
#include <windef.h>

#ifndef TCP_CLIENT_H
#define TCP_CLIENT_H

#pragma warning(disable : 4996)

#define TCP_SERVER_ADDR     "192.168.0.2"
#define TCP_SERVER_PORT     (60000)
#define MAX_TCP_PACKET_SIZE (1492)/*tcp最大数据包字节数*/

#endif/*end TCP_CLIENT_H*/
源文件:client.cpp#include "client.h"

int main()
{
    int nRet = 0;
    WORD wVersionRequested;//版本号
    WSADATA wsaData;
    SOCKET sock;
    SOCKADDR_IN server_addr;
    char buf[MAX_TCP_PACKET_SIZE];
    int recv_size = 0;
    
    wVersionRequested = MAKEWORD(1, 1);//1.1版本的套接字
    nRet = WSAStartup(wVersionRequested, &wsaData);
    if( nRet )
    {
        return nRet;
    }
    
    if (LOBYTE(wsaData.wVersion) != 1 || 
        HIBYTE(wsaData.wVersion) != 1)
    {
        return -1;
    }//判断高低字节是不是1,如果不是1.1的版本则退出    
    
    sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if( INVALID_SOCKET == sock )
    {
        return -1;
    }

    server_addr.sin_addr.S_un.S_addr = inet_addr(TCP_SERVER_ADDR);
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(TCP_SERVER_PORT);
    nRet = connect(sock, (SOCKADDR*)&server_addr, sizeof(SOCKADDR) );
    if( 0 != nRet )
    {
        return nRet;
    }

    recv(sock, buf, sizeof(buf), 0);
    printf(buf);

    do 
    {
        memset(buf, 0, sizeof(buf));
        printf("please write some thing:");
        gets(buf);

        send(sock, buf, strlen(buf) + 1, 0);
        if( ('q'== buf[0]) && (0 == buf[1]) )
        {
            recv_size = recv(sock, buf, sizeof(buf), 0);
            printf(buf);
            break;
        }
    }while( TRUE );

    closesocket(sock);
    WSACleanup();//必须调用这个函数清除参数

    return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐