Linux下套接字详解(六)----基于pthread的多线程的TCP套接字(阻塞/同步/并发)

上节我们实现了一个简单的多进程的服务器程序,这节,我们服务器的框架不做修改,只是将其修改为一个多线程的服务器程序。

直接上代码

server



#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <errno.h>

#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <signal.h>
#include <pthread.h>

#define TCP_SERVER_PORT     6666    /*  服务器的端口  */
#define BUFFER_SIZE         4096
#define IP_SIZE             20
#define MAX_FILENAME_SIZE   256
#define LISTEN_QUEUE        20

extern int errno;

/* 服务器接收从客户端传送来的文件  */
void
TcpServerPullFile(
            int         connFd,                     /*  服务器与客户端通讯的套接字文件  */
            struct      sockaddr_in clientAddr,     /*  与之通信的客户端的信息  */
            char        *fileServerRoot);           /*  上传文件的存储路径  */

/*  服务器将文件发送到客户端  */
void TcpServerPushFile(
                    int         connFd,                  /*  服务器与客户端通讯的套接字文件  */
                    struct      sockaddr_in clientAddr,  /*  与之通信的客户端的信息  */
                    char        *filePath);              /*  带发送至客户端的文件路径  */

/*  处理子进程退出的信号处理函数  */
void SignalChild(int signo);                        /* 信号的标示信息         */

/// 处理客户端的请求信息
void RaiseClientRequest(void *args);
        //int connFd,     /*  客户端的连接套接字描述符, 用于发送和接收数据  */
        //struct sockaddr_in  clientAddr); /*  客户端的信息, 用于显示一些客户端的信息  */

typedef struct arg_type
{
    int                 connFd;
    struct sockaddr_in  clientAddr;
} arg_type;

int main(int argc, char *argv[])
{
    /**********************************************************
     *
     *  创建并初始化服务器套接字
     *
     **********************************************************/
    struct sockaddr_in      serverAddr;
    int                     socketFd;

    bzero(&serverAddr, sizeof(serverAddr));     /*  全部置零  */

    /* 设置地址相关的属性 */
    serverAddr.sin_family         =   AF_INET;
    serverAddr.sin_addr.s_addr    =   htons(INADDR_ANY);
    serverAddr.sin_port           =   htons(TCP_SERVER_PORT);

    /*  创建套接字  */
    socketFd = socket(AF_INET, SOCK_STREAM, 0);
    if(socketFd < 0)
    {
        perror("socket create error\n");
        exit(-1);
    }
    else
    {
        printf("socket create success...\n");
        printf("创建套接字成功[errno = %d]...\n", errno);
    }

    /*  绑定端口  */
    /**********************************************************
     *
     *  命名服务器的套接字, 进行BIND端口绑定
     *
     **********************************************************/
    if(bind(socketFd, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) > 0)
    {
        perror("bind error\n");
        exit(-1);
    }
    else
    {
        printf("server bind port %d success...\n", TCP_SERVER_PORT);
        printf("服务器绑定端口%d成功...\n", TCP_SERVER_PORT);
    }

    /*  开始监听绑定的端口  */
    /**********************************************************
     *
     *  开始监听服务器绑定的端口
     *
     **********************************************************/
    if(listen(socketFd, LISTEN_QUEUE))
    {
        printf("Server listen error[errno = %d]...\n", errno);
        exit(-1);
    }
    else
    {
        printf("Server listen success...\n");
        printf("服务器开始监听...\n");
    }

    //  SIGCHLD 进程Terminate或Stop的时候,SIGCHLD会发送给它的父进程。
    //  缺省情况下该Signal会被忽略
    signal(SIGCHLD, SignalChild);                //子进程退出的信号处理

    while( 1 )
    {
        int connFd;
        struct sockaddr_in  clientAddr;
        socklen_t           length = sizeof(clientAddr);

        /* accept返回一个新的套接字与客户端进行通信  */
        /**********************************************************
         *
         *  ACCEPT返回一个新的套接字与客户端进行通信
         *
         **********************************************************/
        if((connFd = accept(socketFd, (struct sockaddr*)&clientAddr, &length)) < 0)
        {
            printf("accept error, errno = %d...\n", errno);
            continue;
        }
        else
        {
            ////////////////////////////////////////////////////////////////////////
            //
            //  这里填写服务器的处理代码
            //
            ////////////////////////////////////////////////////////////////////////
            printf("\n\naccept connect from client %s\n",inet_ntoa(clientAddr.sin_addr));
            printf("获取到从客户端%s的连接...\n\n\n", inet_ntoa(clientAddr.sin_addr));

            pthread_t   childThread;
            arg_type    args = {connFd, clientAddr};

            /*
             *  第一个参数为指向线程标识符的指针。
             *  第二个参数用来设置线程属性。
             *  第三个参数是线程运行函数的起始地址。
             *  最后一个参数是运行函数的参数。
            */
            if(pthread_create(&childThread, NULL, RaiseClientRequest, &args) != 0)
            {
                perror("pthread_create error...\n");
                break;
            }
        }
    }

    close(socketFd);
}

    /* 服务器接收从客户端传送来的文件  */
void
TcpServerPullFile(
            int         connFd,                     /*  服务器与客户端通讯的套接字文件  */
            struct      sockaddr_in clientAddr,     /*  与之通信的客户端的信息  */
            char        *fileServerRoot)            /*  上传文件的存储路径  */
{
    char    buffer[BUFFER_SIZE];
    char    filename[MAX_FILENAME_SIZE];
    char    fileServerPath[MAX_FILENAME_SIZE]/* = fileServerRoot*/;
    // 定义文件流
    FILE    *stream;

    int     count;              /*  发送文件名的字节数目  */
    int     dataLength;         /*  接收到的数据大小  */
    int     writeLength;        /* 实际写入的数据大小  */
    int     flag = 0;

    bzero(buffer, BUFFER_SIZE);
    /*
     *  向客户端提示输入文件路径提示...
     *
     * strcpy(buffer, "请输入要传输的文件的完整路径:");
    strcat(buffer, "\n");

    send(new_server_socket, buffer, BUFFER_SIZE, 0);
    bzero(buffer, BUFFER_SIZE);
    */

    /*  首先获取客户端发送过来的文件名  */
    count = recv(connFd, buffer, BUFFER_SIZE, 0);

    if(count < 0)
    {
        perror("获取文件名失败...\n");
        exit(1);
    }
    else
    {

        strncpy(filename, buffer, strlen(buffer) > MAX_FILENAME_SIZE ? MAX_FILENAME_SIZE : strlen(buffer));
        strcpy(fileServerPath, fileServerRoot);
        strcat(fileServerPath, filename);
        printf("\n获取客户端发送过来的文件名成功...\n");
        printf("文件名[%s]\n", filename);
        printf("文件存储路径[%s]\n\n", fileServerPath);
    }

    //  服务器接受数据, 首先打开一个文件流
    if((stream = fopen(fileServerPath, "w")) == NULL)
    {
        perror("file open error...\n");
        exit(1);
    }
    else
    {
        bzero(buffer,BUFFER_SIZE);
    }

    printf("正在接收来自%s的文件....\n",inet_ntoa(clientAddr.sin_addr));

    dataLength = 0;

    /*  先将数据接受到缓冲区buffer中,再写入到新建的文件中  */
    while((dataLength = recv(connFd, buffer, BUFFER_SIZE, 0)) > 0)
    {

        flag++;

        if(flag == 1)
        {
            printf("正在接收来自%s的文件....\n", inet_ntoa(clientAddr.sin_addr));
        }

        if(dataLength < 0)
        {
            printf("接收错误i\n");
            exit(1);
        }

        /*  向文件中写入数据  */
        writeLength = fwrite(buffer, sizeof(char), dataLength, stream);

        if(writeLength != dataLength)
        {
             printf("file write failed\n");
             exit(1);
        }
        bzero(buffer,BUFFER_SIZE);
    }

    if(flag > 0)
    {
        printf("%s的文件传送完毕\n", inet_ntoa(clientAddr.sin_addr));
    }
    if(flag==0)
    {
        printf("%s的文件传输失败\n", inet_ntoa(clientAddr.sin_addr));
    }

    fclose(stream);
    //rename("data",inet_ntoa(clientAddr.sin_addr));
    ///  BUG   这里其实有问题
    ///  因为客户端将文件发送完毕后, 服务器是不知道的,
    ///  因此当客户端文件发送完毕后, 服务器会陷入一个死等的循环
    ///  这时一个问题, 但是不是我们代码的重点,
    ///  因为我们的代码, 只是用于学习套接字网络编程
    ///
    ///  这个BUG其实很好处理, 因此我们在网络传输的过程中
    ///  客户端与服务器通信的数据肯定有我们自己的格式或者规范
    ///  比如 [request/response HEAD + LENGTH + DATA]的格式
    ///  要不然连基本的UDP丢包 和 TCP粘包问题都解决不了
    ///  一般情况下, 我们与客户

}

/*  服务器将文件发送到客户端
 *
 *  当用户选择了下载文件后,服务器将执行此操作
 *
 *  */
void TcpServerPushFile(
                    int         connFd,                  /*  服务器与客户端通讯的套接字文件  */
                    struct      sockaddr_in clientAddr,  /*  与之通信的客户端的信息  */
                    char        *filePath)              /*  带发送至客户端的文件路径  */
{
    //send file imformation

    char    buff[BUFFER_SIZE];
    char    filename[MAX_FILENAME_SIZE];
    int     count;
    FILE    *stream;

    /* 先将文件名发送给客户端
     * 2015-4-13 21:38 Modify
     * 发送文件名时只需要发送filePath最后的文件名filename就可以了
     * */
    bzero(buff, BUFFER_SIZE);
    strcpy(filename, strrchr(filePath, '/') + 1);
    strncpy(buff, filename, strlen(filename) > MAX_FILENAME_SIZE ? MAX_FILENAME_SIZE : strlen(filename));
    count = send(connFd, buff, BUFFER_SIZE, 0);
    printf("服务器待发送的文件名[%s]..\n", filename);

    if(count < 0)
    {
        perror("Send file information");
        exit(1);
    }

    /*  服务器开始读取并且发送文件 : */
    if((stream = fopen(filePath, "rb")) == NULL)
    {
        printf("%s not found!\n", filePath);
    }
    printf("服务器打开文件成功...\n");
    printf("正在向客户端发送文件...\n");
    bzero(buff, BUFFER_SIZE);

    int fileBlockLength = 0;
    while((fileBlockLength = fread(buff, sizeof(char), BUFFER_SIZE, stream)) > 0)
    {
        printf("读取了:%d个数据...\n",fileBlockLength);
        if((count =send(connFd, buff, fileBlockLength, 0)) < 0)
        {
            perror("Send file error...\n");
            perror("向客户端发送文件失败...\n");
            exit(1);
        }

        bzero(buff,BUFFER_SIZE);
    }

    fclose(stream);
    printf("服务器发送文件成功\n");

}

/// 处理客户端的请求信息
//void RaiseClientRequest(
//        int connFd,     /*  客户端的连接套接字描述符, 用于发送和接收数据  */
//        struct sockaddr_in  clientAddr) /*  客户端的信息, 用于显示一些客户端的信息  */
void RaiseClientRequest(void *args)
{
    arg_type            *arg        = (arg_type *)args;
    int                 connFd      = arg->connFd;
    struct sockaddr_in  clientAddr  = arg->clientAddr;

    printf("\n\n\n下面将依次测试  接收数据  发送数据  存储文件  推送文件\n\n\n");

    int count;
    char buffer[BUFFER_SIZE];

    //  首先测试接收客户端发送来的数据
    printf("===========recv data===========\n");
    bzero(buffer, BUFFER_SIZE);
    if((count = recv(connFd, buffer, BUFFER_SIZE, 0)) < 0)
    {
        printf("recv data error from %s error, errno = %d...\n", inet_ntoa(clientAddr.sin_addr), errno);
        printf("接收来自 %s 的数据错误, 错误码errno = %d....\n", inet_ntoa(clientAddr.sin_addr), errno);
    }
    else
    {
        printf("recv %d data : %s\n", count, buffer);
        printf("接收%d个数据 : %s\n", count, buffer);
    }
    printf("===========recv data===========\n\n\n");

    //  接着测试向客户端发送反馈数据
    printf("===========send data===========\n");
    bzero(buffer, BUFFER_SIZE);
    strcpy(buffer, "I am fine !");
    if((count = send(connFd, buffer, strlen(buffer) + 1, 0)) < 0)
    {
        printf("send data[%s] error[errno = %d]...\n", buffer, errno);
        printf("发送数据[%s] 失败[错误码 = %d]...\n", buffer, errno);
    }
    else
    {
        printf("send data[%s] success...\n", buffer);
        printf("发送数据[%s]成功...\n", buffer);
    }
    printf("===========send data===========\n\n\n");

    //  首先测试接收客户端发送来的数据
    printf("===========pull file============\n");
    TcpServerPullFile(connFd, clientAddr, "./sdata/"); /*  将客户端发送来的文件存储在./sdata目录下  */
    printf("===========pull file============\n");

    //  首先测试接收客户端发送来的数据
    //printf("===========pull file============\n");
    //TcpServerPushFile(connFd, clientAddr, "./sdata/spush"); /*  将客户端发送来的文件存储在./sdata目录下  */
    //printf("===========pull file============\n");

}

//  处理子进程退出的信号处理函数
void SignalChild(int signo)         //父进程对子进程结束的信号处理
{
    pid_t       pid;
    int         stat;
    //  SIGCHLD 20,17,18 B 子进程结束信号
    printf("get a signal %d\n", signo);

    while((pid = waitpid(-1, &stat, WNOHANG)) > 0)
    {
        printf("child %d terminated\n", pid);
        printf("子进程%d终止\n", pid);
    }

    return ;
}


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
  • 315
  • 316
  • 317
  • 318
  • 319
  • 320
  • 321
  • 322
  • 323
  • 324
  • 325
  • 326
  • 327
  • 328
  • 329
  • 330
  • 331
  • 332
  • 333
  • 334
  • 335
  • 336
  • 337
  • 338
  • 339
  • 340
  • 341
  • 342
  • 343
  • 344
  • 345
  • 346
  • 347
  • 348
  • 349
  • 350
  • 351
  • 352
  • 353
  • 354
  • 355
  • 356
  • 357
  • 358
  • 359
  • 360
  • 361
  • 362
  • 363
  • 364
  • 365
  • 366
  • 367
  • 368
  • 369
  • 370
  • 371
  • 372
  • 373
  • 374
  • 375
  • 376
  • 377
  • 378
  • 379
  • 380
  • 381
  • 382
  • 383
  • 384
  • 385
  • 386
  • 387
  • 388
  • 389
  • 390
  • 391
  • 392
  • 393
  • 394
  • 395
  • 396
  • 397
  • 398
  • 399
  • 400
  • 401
  • 402
  • 403
  • 404
  • 405
  • 406
  • 407
  • 408
  • 409
  • 410
  • 411
  • 412
  • 413
  • 414
  • 415
  • 416
  • 417
  • 418
  • 419
  • 420
  • 421
  • 422
  • 423
  • 424
  • 425
  • 426
  • 427
  • 428
  • 429
  • 430
  • 431
  • 432
  • 433
  • 434
  • 435
  • 436
  • 437
  • 438
  • 439
  • 440
  • 441
  • 442
  • 443
  • 444
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
  • 315
  • 316
  • 317
  • 318
  • 319
  • 320
  • 321
  • 322
  • 323
  • 324
  • 325
  • 326
  • 327
  • 328
  • 329
  • 330
  • 331
  • 332
  • 333
  • 334
  • 335
  • 336
  • 337
  • 338
  • 339
  • 340
  • 341
  • 342
  • 343
  • 344
  • 345
  • 346
  • 347
  • 348
  • 349
  • 350
  • 351
  • 352
  • 353
  • 354
  • 355
  • 356
  • 357
  • 358
  • 359
  • 360
  • 361
  • 362
  • 363
  • 364
  • 365
  • 366
  • 367
  • 368
  • 369
  • 370
  • 371
  • 372
  • 373
  • 374
  • 375
  • 376
  • 377
  • 378
  • 379
  • 380
  • 381
  • 382
  • 383
  • 384
  • 385
  • 386
  • 387
  • 388
  • 389
  • 390
  • 391
  • 392
  • 393
  • 394
  • 395
  • 396
  • 397
  • 398
  • 399
  • 400
  • 401
  • 402
  • 403
  • 404
  • 405
  • 406
  • 407
  • 408
  • 409
  • 410
  • 411
  • 412
  • 413
  • 414
  • 415
  • 416
  • 417
  • 418
  • 419
  • 420
  • 421
  • 422
  • 423
  • 424
  • 425
  • 426
  • 427
  • 428
  • 429
  • 430
  • 431
  • 432
  • 433
  • 434
  • 435
  • 436
  • 437
  • 438
  • 439
  • 440
  • 441
  • 442
  • 443
  • 444

client



#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <errno.h>

#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <signal.h>

#define TCP_SERVER_PORT     6666
#define MAX_FILENAME_SIZE   256
#define IP_SIZE             20

#define BUFFER_SIZE         4096

extern int errno;

//  客户端进行的处理逻辑
void RaiseServerResponse(int socketFd);

/* 从服务器上下载文件  */
void TcpClientPullFile(int socketFd, char *filePath);

/* 客户端将文件上传到服务器上 */
void TcpClientPushFile(int socketFd, char *filePath);

int main(int argc, char *argv[])
{
    char                serverIp[IP_SIZE];              /*  服务器的IP地址          */

    if(argc >= 2)                       /*  参数过多的时候,提示用户                */
    {
        printf("You have given to much parameters...\n");
        printf("Yous should give the IP address after %s\n without any other parametes...\n", (char *)argv[0]);
    }
    else if(argc == 1)                  /*  只有一个参数,则默认使用localhost(127.0.0.1)  */
    {
        strcpy(serverIp, "127.0.0.1");
    }
    else
    {
        strcpy(serverIp, argv[1]);
    }

    /**********************************************************
     *
     *  创建并初始化套接字
     *
     **********************************************************/
    struct sockaddr_in  serverAddr;          /*  服务器的套接字信息   */
    int                 socketFd;                      /*  客户端的套接字信息   */

    bzero(&serverAddr, sizeof(serverAddr));             /*  全部置零               */

    serverAddr.sin_family       =   AF_INET;                      /*  internet协议族         */
    serverAddr.sin_addr.s_addr  =   inet_addr(serverIp);        /*  设置所连接服务器的IP   */
    serverAddr.sin_port         =   htons(TCP_SERVER_PORT);       /*  设置连接的服务器端口   */

    /*  开始创建套接字                        */
    /*  SOCK_STREAM 面向连接的套接字,即TCP   */
    socketFd                    =   socket(AF_INET, SOCK_STREAM, 0);

    if(socketFd < 0)
    {
        printf("socket error\n");
        exit(-1);
    }

    /*  尝试连接服务器  */
    if(connect(socketFd, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) < 0)
    {
        printf("Can Not Connect To %s\n", serverIp);
        exit(1);
    }
    else
    {
        printf("connect to the server %s SUCCESS...\n", serverIp);
        printf("连接服务器成功...\n");
    }

    /**********************************************************
     *
     *  下面进行正常的套接字通信
     *
     **********************************************************/
    RaiseServerResponse(socketFd);

    for(;;);

    //  关闭套接字的文件描述符
    close(socketFd);

    return EXIT_SUCCESS;
}

/* 客户端将文件上传到服务器上 */
void TcpClientPushFile(int socketFd, char *filePath)
{
    FILE    *stream;
    char    buffer[BUFFER_SIZE];
    char    filename[MAX_FILENAME_SIZE];
    int     count = 0;

    bzero(buffer, BUFFER_SIZE);
    strcpy(filename, strrchr(filePath, '/') + 1);
    strncpy(buffer, filename, strlen(filename) > MAX_FILENAME_SIZE ? MAX_FILENAME_SIZE : strlen(filename));

    if((count = send(socketFd, buffer, BUFFER_SIZE, 0)) < 0)
    {
        perror("Send file information");
        exit(1);
    }
    printf("客户端待上传待文件名[%s]..\n", filename);

    /*  打开文件流  */
    if((stream = fopen(filePath, "r")) == NULL)
    {
        printf("Can't open the file [%s], errno = %d\n", filePath, errno);
        exit(-1);
    }
    else
    {
        printf("客户端打开文件成功\n");
    }

    printf("正在向服务器传上传文件...\n");
    count = 0;

    /*  清空缓冲区  */
    bzero(buffer, BUFFER_SIZE);

    /*  不断读取并发送数据  */
    while((count = fread(buffer, 1, BUFFER_SIZE, stream)) > 0)
    {
        // printf("count =%d\n", count);
        if(send(socketFd, buffer, count, 0) < 0)
        {
            printf("send file error...\n");
            break;
        }

        bzero(buffer, BUFFER_SIZE);  /*  再次将缓冲区清空  */
    }

    printf("向服务器发送文件成功...\n");

    /* 传送完毕后, 关闭文件流  */
    if(fclose(stream))
    {
        printf("file close error\n");
        exit(1);
    }
    else
    {
        printf("关闭文件流成功...\n");
    }
    ///  BUG   这里其实有问题
    ///  因为客户端将文件发送完毕后, 服务器是不知道的,
    ///  因此当客户端文件发送完毕后, 服务器会陷入一个死等的循环
    ///  这时一个问题, 但是不是我们代码的重点,
    ///  因为我们的代码, 只是用于学习套接字网络编程
    ///
    ///  这个BUG其实很好处理, 因此我们在网络传输的过程中
    ///  客户端与服务器通信的数据肯定有我们自己的格式或者规范
    ///  比如 [request/response HEAD + LENGTH + DATA]的格式
    ///  要不然连基本的UDP丢包 和 TCP粘包问题都解决不了
    ///  一般情况下, 我们与客户

    /*  关闭与服务器通讯的套接字  */
    //close(socketFd);
}

/* 从服务器上下载文件  */
void TcpClientPullFile(int socketFd, char *filePath)
{
    char    buff[BUFFER_SIZE];
    char    filename[MAX_FILENAME_SIZE];
    int     count, writeLength, dataLength;
    FILE    *stream;
    bzero(buff,BUFFER_SIZE);

    /*  首先获取服务器发送过来的文件名  */
    if((count = recv(socketFd, buff, BUFFER_SIZE, 0)) < 0)
    {
        perror("获取文件名失败...\n");
        exit(1);
    }

    strncpy(filename, buff, strlen(buff) > MAX_FILENAME_SIZE ? MAX_FILENAME_SIZE : strlen(buff));

    /*  开始接收文件  */
    printf("Preparing download file : %s", filename);

    /*  打开文件流  */
    if((stream = fopen(filename, "wb+")) == NULL)
    {
        perror("create file %s error...\n");
        perror("创建文件失败...\n");
        exit(1);
    }

    bzero(buff, BUFFER_SIZE);          /*  清空缓冲区  */
    dataLength = 0;
    while((dataLength = recv(socketFd, buff, BUFFER_SIZE, 0)) != 0)
    {
        if(dataLength < 0)  /* 如果接收文件失败  */
        {
            perror("download error...\n");
            perror("下载文件失败...\n");
            exit(1);
        }

        /*  将接收到的文件数据写入文件中  */
        writeLength = fwrite(buff, sizeof(char), dataLength, stream);
        if(writeLength < dataLength)   /*  如果写入的数据比实际接收到的数据少  */
        {
            perror("file write error...\n");
            perror("写入文件失败...\n");
            exit(1);
        }

        bzero(buff, BUFFER_SIZE);               /* 清空缓冲区  */
    }
    printf("下载来自服务器%s的文件成功\n", filename);
    printf("Receieved file:%s finished!\n", filename);

    fclose(stream);             /*  关闭文件流 */

}

//  客户端进行的处理逻辑
void RaiseServerResponse(int socketFd)
{

    char                buffer[BUFFER_SIZE];            /*  数据缓冲区              */
    int                 count;                          /*  接受或者发送的数据大小  */

    printf("\n\n\n下面将依次测试  发送数据  接收数据  上传文件  下载文件\n\n\n");

    //  发送数据流
    printf("===========send data===========\n");
    bzero(buffer, BUFFER_SIZE);
    strcpy(buffer, "How are you ?");
    if((count = send(socketFd, buffer, strlen(buffer) + 1, 0)) < 0)
    {
        printf("send data[%s] error[errno = %d]...\n", buffer, errno);
        printf("发送数据[%s] 失败[错误码 = %d]...\n", buffer, errno);
    }
    else
    {
        printf("send data[%s] success...\n", buffer);
        printf("发送数据[%s]成功...\n", buffer);
    }
    printf("===========send data===========\n\n\n");

    //  接受数据流
    printf("===========recv data===========\n");
    bzero(buffer, BUFFER_SIZE);
    if((count = recv(socketFd, buffer, BUFFER_SIZE, 0)) < 0)
    {
        printf("recv data error[errno = %d]...\n", errno);
        printf("接收数据失败[错误码 = %d]...\n", errno);
    }
    else
    {
        printf("recv %d data : %s\n", count, buffer);
        printf("接收%d个数据 : %s\n", count, buffer);
    }
    printf("===========recv data===========\n\n\n");

    // 上传文件到服务器
    printf("===========push file===========\n");
    TcpClientPushFile(socketFd, "./cdata/cpush");
    printf("===========push file===========\n\n\n");

    // 上传文件到服务器
    //printf("===========push file===========\n");
    //TcpClientPullFile(socketFd, "./cdata/");
    //printf("===========push file===========\n\n\n");

    //close(socketFd);

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311

运行



转载:http://blog.csdn.net/gatieme/article/details/50791958

时间: 2016-05-18

Linux下套接字详解(六)----基于pthread的多线程的TCP套接字(阻塞/同步/并发)的相关文章

Linux 下 Make 命令实例详解

  Linux 下 Make 命令实例详解          Linux 下 make 命令是系统管理员和程序员用的最频繁的命令之一.管理员用它通过命令行来编译和安装很多开源的工具,程序员用它来管理他们大型复杂的项目编译问题.本文我们将用一些实例来讨论 make 命令背后的工作机制. Make 如何工作的 对于不知道背后机理的人来说,make 命令像命令行参数一样接收目标.这些目标通常存放在以 "Makefile" 来命名的特殊文件中,同时文件也包含与目标相对应的操作.更多信息,阅读关

Linux下DNS服务器搭建详解

Linux下DNS服务器搭建详解 简介: DNS服务器的作用就是就好比生活中的电话簿.114查号台一样,为各种网络程序找到对应目标主机的IP地址或对应的主机域名. DNS系统的作用: 正向解析:根据主机名称(域名)查找对应的IP地址(实际应用中最多的) 反向解析:根据IP地址查找对应的主机域名(不常用,一般用于搭建邮件服务器时.) 根据服务器与所提供域名解析记录的关系,将DNS服务器分为不同的角色: 缓存域名服务器 也称为 唯高速缓存服务器 通过向其他域名服务器查询获得域名->IP地址记录 将域

linux 下的yum命令详解_Linux

yum(全称为 Yellow dog Updater, Modified)是一个在Fedora和RedHat以及SUSE中的Shell前端软件包管理器.基於RPM包管理,能够从指定的服务器自动下载RPM包并且安装,可以自动处理依赖性关系,并且一次安装所有依赖的软体包,无须繁琐地一次次下载.安装.yum提供了查找.安装.删除某一个.一组甚至全部软件包的命令,而且命令简洁而又好记. yum的命令形式一般是如下:yum [options] [command] [package ...] 其中的[opt

Linux下的chkconfig命令详解_Linux

chkconfig命令 Linux下的chkconfig命令主要用来更新(启动或停止)和查询系统服务的运行级信息.谨记chkconfig不是立即自动禁止或激活一个服务,它只是简单的改变了符号连接. 使用语法 chkconfig [–add][–del][–list][系统服务] 或 chkconfig [–level <等级代号>][系统服务][on/off/reset] chkconfig在没有参数运行时,显示用法.如果加上服务名,那么就检查这个服务是否在当前运行级启动.如果是,返回true

Linux下安装Redis教程详解

安装Reids   Redis下载地址:http://www.redis.io/download # wget http://download.redis.io/releases/redis-2.8.13.tar.gz # cd redis-2.8.13/ # make zmalloc.o: In function `zmalloc_used_memory': /usr/local/webserver/redis-2.8.13/src/zmalloc.c:226: undefined refer

Linux下split 命令使用详解

按大小切割,每100M切割 split -b 100m filename 按行数切割,每100w行切割,并带上前缀 split -l10000000 pc.txt i_ 合并:cat x* > pc.txt split :切割文件 语 法:split [--help][--version][-b <字节>][-C <字节>][-l <行数>][要切割的文件][输出文件名] 补充说明:split可将文件切成较小的文件,预设每1000行会切成一个小文件 参 数: -l

linux下安装vmware过程详解

由于项目需要,要在Linux下虚拟一个Windows,经过查找些资料,发现可一用VMware来实现,当然还有其他一些虚拟机可以使用如Win4lin,bochs等,但经过试用,只有VMware还好用.以下就是安装的步骤. 首先到https://www.vmware.com/ 下载最新的安装包,比如VMware-workstation-4.0.5-6030.tar.gz,假设放在"/tmp"下.然后使用下面的命令解压后安装. 1.tar –zxvf VMware-workstation-4

Sendmail在Linux下架设Mail服务器详解

sendmail是最重要的邮件传输代理程序.理解电子邮件的工作模式是非常重要的.一般情况下,我们把电子邮件程序分解成用户代理,传输代理和投递代理. 用户代理用来接受用户的指令,将用户的信件传送至信件传输代理,如:outlook express.foxmail等.而投递代理则从信件传输代理取得信件传送至最终用户的邮箱,如:procmail. 当用户试图发送一封电子邮件的时候,他并不能直接将信件发送到对方的机器上,用户代理必须试图去寻找一个信件传输代理,把邮件提交给它.信件传输代理得到了邮件后,首先

Linux下彻底卸载mysql详解_Mysql

一.使用以下命令查看当前安装mysql情况,查找以前是否装有mysql rpm -qa|grep -i mysql 可以看到如下图的所示: 显示之前安装了:      MySQL-client-5.5.25a-1.rhel5      MySQL-server-5.5.25a-1.rhel5 2.停止mysql服务.删除之前安装的mysql 删除命令:rpm -e –nodeps 包名 rpm -ev MySQL-client-5.5.25a-1.rhel5 rpm -ev MySQL-serv