├── .gitignore ├── README.md └── Socket网络编程.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TCP-IP_Socket 2 | tcp/ip socket 高级编程课程中对应代码! 3 | 代码Linux下都可运行! 4 | 根据课程视频整理并手敲的! 5 | -------------------------------------------------------------------------------- /Socket网络编程.md: -------------------------------------------------------------------------------- 1 | # Socket网络编程 2 | 3 | ## TCP客户/服务器模型 4 | 5 | ### 回射客户/服务器模型 6 | 7 | **echosrv.cpp** 8 | 9 | ```c++ 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #define ERR_EXIT(m) \ 22 | do { \ 23 | perror(m); \ 24 | exit(EXIT_FAILURE); \ 25 | } while(0) 26 | 27 | void do_service(int conn) { 28 | //接收 29 | char recvbuf[1024]; 30 | while(1) { 31 | memset(recvbuf, 0, sizeof(recvbuf)); 32 | int ret = read(conn, recvbuf, sizeof(recvbuf)); 33 | if(ret == 0) { 34 | printf("client close\n"); 35 | break; 36 | } 37 | else if(ret == -1) 38 | ERR_EXIT("read"); 39 | fputs(recvbuf, stdout); 40 | write(conn, recvbuf, ret); 41 | } 42 | } 43 | 44 | int main() { 45 | //创建套接字 46 | int listenfd; 47 | if((listenfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) 48 | ERR_EXIT("socket"); 49 | 50 | //地址初始化 51 | struct sockaddr_in servaddr; //IPv4地址结构 52 | memset(&servaddr, 0, sizeof(servaddr)); 53 | servaddr.sin_family = AF_INET; 54 | servaddr.sin_port = htons(5188); 55 | servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//绑定本机的任意地址 56 | 57 | int on = 1; 58 | if(setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) 59 | ERR_EXIT("setsockopt"); 60 | 61 | //绑定 62 | if(bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) 63 | ERR_EXIT("bind"); 64 | 65 | //监听 66 | if(listen(listenfd, SOMAXCONN) < 0) 67 | ERR_EXIT("listen"); 68 | 69 | //连接 70 | struct sockaddr_in peeraddr; 71 | socklen_t peerlen = sizeof(peeraddr); 72 | int conn; 73 | 74 | pid_t pid; 75 | while(1) { 76 | if((conn = accept(listenfd, (struct sockaddr*)&peeraddr, &peerlen)) < 0) 77 | ERR_EXIT("accept"); 78 | printf("ip=%s port=%d\n", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port)); 79 | 80 | pid = fork(); 81 | if(pid == -1) 82 | ERR_EXIT("fork"); 83 | if(pid == 0) { 84 | //子进程不需要处理监听 85 | close(listenfd); 86 | do_service(conn); 87 | exit(EXIT_SUCCESS); 88 | } 89 | else 90 | //父进程不需要处理连接 91 | close(conn); 92 | } 93 | 94 | return 0; 95 | } 96 | ``` 97 | 98 | **echocli.cpp** 99 | 100 | ```c++ 101 | #include 102 | #include 103 | #include 104 | #include 105 | #include 106 | #include 107 | #include 108 | #include 109 | #include 110 | 111 | #define ERR_EXIT(m) \ 112 | do { \ 113 | perror(m); \ 114 | exit(EXIT_FAILURE); \ 115 | } while(0) 116 | 117 | int main() { 118 | //创建套接字 119 | int sock; 120 | if((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) 121 | ERR_EXIT("socket"); 122 | 123 | //地址初始化 124 | struct sockaddr_in servaddr; //IPv4地址结构 125 | memset(&servaddr, 0, sizeof(servaddr)); 126 | servaddr.sin_family = AF_INET; 127 | servaddr.sin_port = htons(5188); 128 | servaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); 129 | 130 | if(connect(sock, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) 131 | ERR_EXIT("connect"); 132 | 133 | char sendbuf[1024] = {0}; 134 | char recvbuf[1024] = {0}; 135 | while(fgets(sendbuf, sizeof(sendbuf), stdin) != NULL) { 136 | write(sock, sendbuf, strlen(sendbuf)); 137 | read(sock, recvbuf, sizeof(recvbuf)); 138 | fputs(recvbuf, stdout); 139 | memset(sendbuf, 0, sizeof(sendbuf)); 140 | memset(recvbuf, 0, sizeof(recvbuf)); 141 | } 142 | 143 | close(sock); 144 | 145 | return 0; 146 | } 147 | ``` 148 | 149 | ### 点对点聊天程序实现 150 | 151 | **p2pcrv.cpp** 152 | 153 | ```c++ 154 | #include 155 | #include 156 | #include 157 | #include 158 | #include 159 | #include 160 | #include 161 | #include 162 | #include 163 | #include 164 | #include 165 | 166 | #define ERR_EXIT(m) \ 167 | do { \ 168 | perror(m); \ 169 | exit(EXIT_FAILURE); \ 170 | } while(0) 171 | 172 | void handler(int sig) { 173 | printf("recv a sig = %d\n", sig); 174 | exit(EXIT_SUCCESS); 175 | } 176 | 177 | int main() { 178 | //创建套接字 179 | int listenfd; 180 | if((listenfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) 181 | ERR_EXIT("socket"); 182 | 183 | //地址初始化 184 | struct sockaddr_in servaddr; //IPv4地址结构 185 | memset(&servaddr, 0, sizeof(servaddr)); 186 | servaddr.sin_family = AF_INET; 187 | servaddr.sin_port = htons(5188); 188 | servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//绑定本机的任意地址 189 | 190 | int on = 1; 191 | if(setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) 192 | ERR_EXIT("setsockopt"); 193 | 194 | //绑定 195 | if(bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) 196 | ERR_EXIT("bind"); 197 | 198 | //监听 199 | if(listen(listenfd, SOMAXCONN) < 0) 200 | ERR_EXIT("listen"); 201 | 202 | //连接 203 | struct sockaddr_in peeraddr; 204 | socklen_t peerlen = sizeof(peeraddr); 205 | int conn; 206 | 207 | if((conn = accept(listenfd, (struct sockaddr*)&peeraddr, &peerlen)) < 0) 208 | ERR_EXIT("accept"); 209 | printf("ip = %s port = %d\n", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port)); 210 | 211 | pid_t pid; 212 | pid = fork(); 213 | if(pid == -1) 214 | ERR_EXIT("fork"); 215 | if(pid == 0) { 216 | //子进程用于发送数据 217 | signal(SIGUSR1, handler); 218 | char sendbuf[1024] = {0}; 219 | while(fgets(sendbuf, sizeof(sendbuf), stdin) != NULL) { 220 | write(conn, sendbuf, strlen(sendbuf)); 221 | memset(sendbuf, 0, sizeof(sendbuf)); 222 | } 223 | printf("child close\n"); 224 | exit(EXIT_SUCCESS); 225 | } 226 | else { 227 | //父进程用于接收数据 228 | char recvbuf[1024]; 229 | while(1) { 230 | memset(recvbuf, 0, sizeof(recvbuf)); 231 | int ret = read(conn, recvbuf, sizeof(recvbuf)); 232 | if(ret == -1) 233 | ERR_EXIT("read"); 234 | else if(ret == 0) { 235 | printf("peer close\n"); 236 | break; 237 | } 238 | fputs(recvbuf, stdout); 239 | } 240 | printf("parent close\n"); 241 | kill(pid, SIGUSR1); 242 | exit(EXIT_SUCCESS); 243 | } 244 | 245 | close(conn); 246 | close(listenfd); 247 | 248 | return 0; 249 | } 250 | ``` 251 | 252 | **p2pcli.cpp** 253 | 254 | ```c++ 255 | #include 256 | #include 257 | #include 258 | #include 259 | #include 260 | #include 261 | #include 262 | #include 263 | #include 264 | #include 265 | 266 | #define ERR_EXIT(m) \ 267 | do { \ 268 | perror(m); \ 269 | exit(EXIT_FAILURE); \ 270 | } while(0) 271 | 272 | void handler(int sig) { 273 | printf("recv a sig = %d\n", sig); 274 | exit(EXIT_SUCCESS); 275 | } 276 | 277 | int main() { 278 | //创建套接字 279 | int sock; 280 | if((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) 281 | ERR_EXIT("socket"); 282 | 283 | //地址初始化 284 | struct sockaddr_in servaddr; //IPv4地址结构 285 | memset(&servaddr, 0, sizeof(servaddr)); 286 | servaddr.sin_family = AF_INET; 287 | servaddr.sin_port = htons(5188); 288 | servaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); 289 | 290 | if(connect(sock, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) 291 | ERR_EXIT("connect"); 292 | 293 | pid_t pid; 294 | pid = fork(); 295 | if(pid == -1) 296 | ERR_EXIT("fork"); 297 | if(pid == 0) { 298 | //子进程用于接收数据 299 | char recvbuf[1024] = {0}; 300 | while(1) { 301 | memset(recvbuf, 0, sizeof(recvbuf)); 302 | int ret = read(sock, recvbuf, sizeof(recvbuf)); 303 | if(ret == -1) 304 | ERR_EXIT("read"); 305 | else if(ret == 0) { 306 | printf("peer close\n"); 307 | break; 308 | } 309 | fputs(recvbuf, stdout); 310 | } 311 | printf("child close\n"); 312 | close(sock); 313 | kill(getppid(), SIGUSR1); 314 | } 315 | else { 316 | //父进程用于发送数据 317 | signal(SIGUSR1, handler); 318 | char sendbuf[1024] = {0}; 319 | while(fgets(sendbuf, sizeof(sendbuf), stdin) != NULL) { 320 | write(sock, sendbuf, strlen(sendbuf)); 321 | memset(sendbuf, 0, sizeof(sendbuf)); 322 | } 323 | printf("parent close\n"); 324 | close(sock); 325 | } 326 | 327 | close(sock); 328 | 329 | return 0; 330 | } 331 | ``` 332 | 333 | ### 改进回射客户/服务器模型 334 | 335 | **封装定长接收readn函数、定长发送writen函数、自定义协议解决TCP的粘包问题** 336 | 337 | **echosrv1.cpp** 338 | 339 | ```c++ 340 | #include 341 | #include 342 | #include 343 | #include 344 | #include 345 | #include 346 | #include 347 | #include 348 | #include 349 | #include 350 | 351 | #define ERR_EXIT(m) \ 352 | do { \ 353 | perror(m); \ 354 | exit(EXIT_FAILURE); \ 355 | } while(0) 356 | 357 | struct packet { 358 | int len; 359 | char buf[1024]; 360 | }; 361 | 362 | ssize_t readn(int fd, void *buf, size_t count) {//ssize_t 有符号整数 size_t 无符号整数 363 | size_t nleft = count;//剩余的字节数 364 | ssize_t nread;//已接收的字节数 365 | char *bufp = (char*)buf; 366 | 367 | while(nleft > 0) { 368 | if((nread = read(fd, bufp, nleft)) < 0) { 369 | if(errno == EINTR)//被信号中断 370 | continue; 371 | return -1; 372 | } 373 | else if(nread == 0)//对等方关闭了 374 | return count - nleft; 375 | bufp += nread; 376 | nleft -= nread; 377 | } 378 | return count; 379 | } 380 | 381 | ssize_t writen(int fd, const void *buf, size_t count) { 382 | size_t nleft = count;//剩余要发送的字节数 383 | ssize_t nwritten;//已发送的字节数 384 | char *bufp = (char*)buf; 385 | 386 | while(nleft > 0) { 387 | if((nwritten = write(fd, bufp, nleft)) < 0) { 388 | if(errno == EINTR)//被信号中断 389 | continue; 390 | return -1; 391 | } 392 | else if(nwritten == 0) 393 | continue; 394 | bufp += nwritten; 395 | nleft -= nwritten; 396 | } 397 | return count; 398 | } 399 | 400 | void do_service(int conn) { 401 | //接收 402 | struct packet recvbuf; 403 | int n; 404 | while(1) { 405 | memset(&recvbuf, 0, sizeof(recvbuf)); 406 | int ret = readn(conn, &recvbuf.len, 4); 407 | if(ret == -1) 408 | ERR_EXIT("read"); 409 | else if(ret < 4) { 410 | printf("client close\n"); 411 | break; 412 | } 413 | 414 | n = ntohl(recvbuf.len); 415 | ret = readn(conn, recvbuf.buf, n); 416 | if(ret == -1) 417 | ERR_EXIT("read"); 418 | else if(ret < n) { 419 | printf("client close\n"); 420 | break; 421 | } 422 | fputs(recvbuf.buf, stdout); 423 | writen(conn, &recvbuf, 4 + n); 424 | } 425 | } 426 | int main() { 427 | //创建套接字 428 | int listenfd; 429 | if((listenfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) 430 | ERR_EXIT("socket"); 431 | 432 | //地址初始化 433 | struct sockaddr_in servaddr; //IPv4地址结构 434 | memset(&servaddr, 0, sizeof(servaddr)); 435 | servaddr.sin_family = AF_INET; 436 | servaddr.sin_port = htons(5188); 437 | servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//绑定本机的任意地址 438 | 439 | int on = 1; 440 | if(setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) 441 | ERR_EXIT("setsockopt"); 442 | 443 | //绑定 444 | if(bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) 445 | ERR_EXIT("bind"); 446 | 447 | //监听 448 | if(listen(listenfd, SOMAXCONN) < 0) 449 | ERR_EXIT("listen"); 450 | 451 | //连接 452 | struct sockaddr_in peeraddr; 453 | socklen_t peerlen = sizeof(peeraddr); 454 | int conn; 455 | 456 | pid_t pid; 457 | while(1) { 458 | if((conn = accept(listenfd, (struct sockaddr*)&peeraddr, &peerlen)) < 0) 459 | ERR_EXIT("accept"); 460 | printf("ip=%s port=%d\n", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port)); 461 | 462 | pid = fork(); 463 | if(pid == -1) 464 | ERR_EXIT("fork"); 465 | if(pid == 0) { 466 | //子进程不需要处理监听 467 | close(listenfd); 468 | do_service(conn); 469 | exit(EXIT_SUCCESS); 470 | } 471 | else 472 | //父进程不需要处理连接 473 | close(conn); 474 | } 475 | 476 | return 0; 477 | } 478 | ``` 479 | 480 | **echocli1.cpp** 481 | 482 | ```c++ 483 | #include 484 | #include 485 | #include 486 | #include 487 | #include 488 | #include 489 | #include 490 | #include 491 | #include 492 | 493 | #define ERR_EXIT(m) \ 494 | do { \ 495 | perror(m); \ 496 | exit(EXIT_FAILURE); \ 497 | } while(0) 498 | 499 | struct packet { 500 | int len; 501 | char buf[1024]; 502 | }; 503 | 504 | ssize_t readn(int fd, void *buf, size_t count) {//ssize_t 有符号整数 size_t 无符号整数 505 | size_t nleft = count;//剩余的字节数 506 | ssize_t nread;//已接收的字节数 507 | char *bufp = (char*)buf; 508 | 509 | while(nleft > 0) { 510 | if((nread = read(fd, bufp, nleft)) < 0) { 511 | if(errno == EINTR)//被信号中断 512 | continue; 513 | return -1; 514 | } 515 | else if(nread == 0)//对等方关闭了 516 | return count - nleft; 517 | bufp += nread; 518 | nleft -= nread; 519 | } 520 | return count; 521 | } 522 | 523 | ssize_t writen(int fd, const void *buf, size_t count) { 524 | size_t nleft = count;//剩余要发送的字节数 525 | ssize_t nwritten;//已发送的字节数 526 | char *bufp = (char*)buf; 527 | 528 | while(nleft > 0) { 529 | if((nwritten = write(fd, bufp, nleft)) < 0) { 530 | if(errno == EINTR)//被信号中断 531 | continue; 532 | return -1; 533 | } 534 | else if(nwritten == 0) 535 | continue; 536 | bufp += nwritten; 537 | nleft -= nwritten; 538 | } 539 | return count; 540 | } 541 | 542 | int main() { 543 | //创建套接字 544 | int sock; 545 | if((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) 546 | ERR_EXIT("socket"); 547 | 548 | //地址初始化 549 | struct sockaddr_in servaddr; //IPv4地址结构 550 | memset(&servaddr, 0, sizeof(servaddr)); 551 | servaddr.sin_family = AF_INET; 552 | servaddr.sin_port = htons(5188); 553 | servaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); 554 | 555 | if(connect(sock, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) 556 | ERR_EXIT("connect"); 557 | 558 | struct packet sendbuf; 559 | struct packet recvbuf; 560 | memset(&sendbuf, 0, sizeof(sendbuf)); 561 | memset(&recvbuf, 0, sizeof(recvbuf)); 562 | int n; 563 | while(fgets(sendbuf.buf, sizeof(sendbuf.buf), stdin) != NULL) { 564 | n = strlen(sendbuf.buf); 565 | sendbuf.len = htonl(n); 566 | writen(sock, &sendbuf, 4 + n); 567 | 568 | int ret = readn(sock, &recvbuf.len, 4); 569 | if(ret == -1) 570 | ERR_EXIT("read"); 571 | else if(ret < 4) { 572 | printf("client close\n"); 573 | break; 574 | } 575 | 576 | n = ntohl(recvbuf.len); 577 | ret = readn(sock, recvbuf.buf, n); 578 | if(ret == -1) 579 | ERR_EXIT("read"); 580 | else if(ret < n) { 581 | printf("client close\n"); 582 | break; 583 | } 584 | fputs(recvbuf.buf, stdout); 585 | memset(&sendbuf, 0, sizeof(sendbuf)); 586 | memset(&recvbuf, 0, sizeof(recvbuf)); 587 | } 588 | 589 | close(sock); 590 | 591 | return 0; 592 | } 593 | ``` 594 | 595 | **利用readline函数解决TCP粘包问题** 596 | 597 | **echosrv2.cpp** 598 | 599 | ```c++ 600 | #include 601 | #include 602 | #include 603 | #include 604 | #include 605 | #include 606 | #include 607 | #include 608 | #include 609 | #include 610 | 611 | #define ERR_EXIT(m) \ 612 | do { \ 613 | perror(m); \ 614 | exit(EXIT_FAILURE); \ 615 | } while(0) 616 | 617 | ssize_t readn(int fd, void *buf, size_t count) {//ssize_t 有符号整数 size_t 无符号整数 618 | size_t nleft = count;//剩余的字节数 619 | ssize_t nread;//已接收的字节数 620 | char *bufp = (char*)buf; 621 | 622 | while(nleft > 0) { 623 | if((nread = read(fd, bufp, nleft)) < 0) { 624 | if(errno == EINTR)//被信号中断 625 | continue; 626 | return -1; 627 | } 628 | else if(nread == 0)//对等方关闭了 629 | return count - nleft; 630 | bufp += nread; 631 | nleft -= nread; 632 | } 633 | return count; 634 | } 635 | 636 | ssize_t writen(int fd, const void *buf, size_t count) { 637 | size_t nleft = count;//剩余要发送的字节数 638 | ssize_t nwritten;//已发送的字节数 639 | char *bufp = (char*)buf; 640 | 641 | while(nleft > 0) { 642 | if((nwritten = write(fd, bufp, nleft)) < 0) { 643 | if(errno == EINTR)//被信号中断 644 | continue; 645 | return -1; 646 | } 647 | else if(nwritten == 0) 648 | continue; 649 | bufp += nwritten; 650 | nleft -= nwritten; 651 | } 652 | return count; 653 | } 654 | 655 | ssize_t recv_peek(int sockfd, void *buf, size_t len) {//接收数据后不将数据从缓冲区移除 656 | while(1) { 657 | int ret = recv(sockfd, buf, len, MSG_PEEK); 658 | if(ret == -1 && errno == EINTR)//被信号中断 659 | continue; 660 | return ret; 661 | } 662 | } 663 | 664 | ssize_t readline(int sockfd, void *buf, size_t maxline) {//只能用于套接口 665 | int ret; 666 | int nread; 667 | char *bufp = (char *)buf; 668 | int nleft = maxline; 669 | while(1) { 670 | ret = recv_peek(sockfd, bufp, nleft); 671 | if(ret < 0)//不用再进行中断判断,因为recv_peek函数内部已经进行了 672 | return ret; 673 | else if(ret == 0)//对方关闭 674 | return ret; 675 | nread = ret; 676 | int i;//判断已接收到的缓冲区中是否有\n 677 | for(i = 0; i < nread; ++i) { 678 | if(bufp[i] == '\n') { 679 | ret = readn(sockfd, bufp, i + 1);//将数据从缓冲区移除 680 | if(ret != i + 1) 681 | exit(EXIT_FAILURE); 682 | return ret; 683 | } 684 | } 685 | 686 | if(nread > nleft) 687 | exit(EXIT_FAILURE); 688 | 689 | nleft -= nread; 690 | ret = readn(sockfd, bufp, nread);//还没遇到\n的数据也从缓冲区移除 691 | if(ret != nread) 692 | exit(EXIT_FAILURE); 693 | 694 | bufp += nread; 695 | } 696 | return -1; 697 | } 698 | 699 | void echo_srv(int conn) { 700 | //接收 701 | char recvbuf[1024]; 702 | while(1) { 703 | memset(recvbuf, 0, sizeof(recvbuf)); 704 | int ret = readline(conn, recvbuf, 1024); 705 | if(ret == -1) 706 | ERR_EXIT("readline"); 707 | if(ret == 0) { 708 | printf("client close\n"); 709 | break; 710 | } 711 | 712 | fputs(recvbuf, stdout); 713 | writen(conn, recvbuf, strlen(recvbuf)); 714 | } 715 | } 716 | 717 | int main() { 718 | //创建套接字 719 | int listenfd; 720 | if((listenfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) 721 | ERR_EXIT("socket"); 722 | 723 | //地址初始化 724 | struct sockaddr_in servaddr; //IPv4地址结构 725 | memset(&servaddr, 0, sizeof(servaddr)); 726 | servaddr.sin_family = AF_INET; 727 | servaddr.sin_port = htons(5188); 728 | servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//绑定本机的任意地址 729 | 730 | int on = 1; 731 | if(setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) 732 | ERR_EXIT("setsockopt"); 733 | 734 | //绑定 735 | if(bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) 736 | ERR_EXIT("bind"); 737 | 738 | //监听 739 | if(listen(listenfd, SOMAXCONN) < 0) 740 | ERR_EXIT("listen"); 741 | 742 | //连接 743 | struct sockaddr_in peeraddr; 744 | socklen_t peerlen = sizeof(peeraddr); 745 | int conn; 746 | 747 | pid_t pid; 748 | while(1) { 749 | if((conn = accept(listenfd, (struct sockaddr*)&peeraddr, &peerlen)) < 0) 750 | ERR_EXIT("accept"); 751 | printf("ip=%s port=%d\n", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port)); 752 | 753 | pid = fork(); 754 | if(pid == -1) 755 | ERR_EXIT("fork"); 756 | if(pid == 0) { 757 | //子进程不需要处理监听 758 | close(listenfd); 759 | echo_srv(conn); 760 | exit(EXIT_SUCCESS); 761 | } 762 | else 763 | //父进程不需要处理连接 764 | close(conn); 765 | } 766 | 767 | return 0; 768 | } 769 | ``` 770 | 771 | **echocli2.cpp** 772 | 773 | ```c++ 774 | #include 775 | #include 776 | #include 777 | #include 778 | #include 779 | #include 780 | #include 781 | #include 782 | #include 783 | 784 | #define ERR_EXIT(m) \ 785 | do { \ 786 | perror(m); \ 787 | exit(EXIT_FAILURE); \ 788 | } while(0) 789 | 790 | ssize_t readn(int fd, void *buf, size_t count) {//ssize_t 有符号整数 size_t 无符号整数 791 | size_t nleft = count;//剩余的字节数 792 | ssize_t nread;//已接收的字节数 793 | char *bufp = (char*)buf; 794 | 795 | while(nleft > 0) { 796 | if((nread = read(fd, bufp, nleft)) < 0) { 797 | if(errno == EINTR)//被信号中断 798 | continue; 799 | return -1; 800 | } 801 | else if(nread == 0)//对等方关闭了 802 | return count - nleft; 803 | bufp += nread; 804 | nleft -= nread; 805 | } 806 | return count; 807 | } 808 | 809 | ssize_t writen(int fd, const void *buf, size_t count) { 810 | size_t nleft = count;//剩余要发送的字节数 811 | ssize_t nwritten;//已发送的字节数 812 | char *bufp = (char*)buf; 813 | 814 | while(nleft > 0) { 815 | if((nwritten = write(fd, bufp, nleft)) < 0) { 816 | if(errno == EINTR)//被信号中断 817 | continue; 818 | return -1; 819 | } 820 | else if(nwritten == 0) 821 | continue; 822 | bufp += nwritten; 823 | nleft -= nwritten; 824 | } 825 | return count; 826 | } 827 | 828 | ssize_t recv_peek(int sockfd, void *buf, size_t len) {//接收数据后不将数据从缓冲区移除 829 | while(1) { 830 | int ret = recv(sockfd, buf, len, MSG_PEEK); 831 | if(ret == -1 && errno == EINTR)//被信号中断 832 | continue; 833 | return ret; 834 | } 835 | } 836 | 837 | ssize_t readline(int sockfd, void *buf, size_t maxline) {//只能用于套接口 838 | int ret; 839 | int nread; 840 | char *bufp = (char *)buf; 841 | int nleft = maxline; 842 | while(1) { 843 | ret = recv_peek(sockfd, bufp, nleft); 844 | if(ret < 0)//不用再进行中断判断,因为recv_peek函数内部已经进行了 845 | return ret; 846 | else if(ret == 0)//对方关闭 847 | return ret; 848 | nread = ret; 849 | int i;//判断已接收到的缓冲区中是否有\n 850 | for(i = 0; i < nread; ++i) { 851 | if(bufp[i] == '\n') { 852 | ret = readn(sockfd, bufp, i + 1);//将数据从缓冲区移除 853 | if(ret != i + 1) 854 | exit(EXIT_FAILURE); 855 | return ret; 856 | } 857 | } 858 | 859 | if(nread > nleft) 860 | exit(EXIT_FAILURE); 861 | 862 | nleft -= nread; 863 | ret = readn(sockfd, bufp, nread);//还没遇到\n的数据也从缓冲区移除 864 | if(ret != nread) 865 | exit(EXIT_FAILURE); 866 | 867 | bufp += nread; 868 | } 869 | return -1; 870 | } 871 | 872 | void echo_cli(int sock) { 873 | char sendbuf[1024] = {0}; 874 | char recvbuf[1024] = {0}; 875 | while(fgets(sendbuf, sizeof(sendbuf), stdin) != NULL) { 876 | writen(sock, sendbuf, strlen(sendbuf)); 877 | 878 | int ret = readline(sock, recvbuf, sizeof(recvbuf)); 879 | if(ret == -1) 880 | ERR_EXIT("readline"); 881 | else if(ret == 0) { 882 | printf("client close\n"); 883 | break; 884 | } 885 | 886 | fputs(recvbuf, stdout); 887 | memset(sendbuf, 0, sizeof(sendbuf)); 888 | memset(recvbuf, 0, sizeof(recvbuf)); 889 | } 890 | 891 | close(sock); 892 | } 893 | 894 | int main() { 895 | //创建套接字 896 | int sock; 897 | if((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) 898 | ERR_EXIT("socket"); 899 | 900 | //地址初始化 901 | struct sockaddr_in servaddr; //IPv4地址结构 902 | memset(&servaddr, 0, sizeof(servaddr)); 903 | servaddr.sin_family = AF_INET; 904 | servaddr.sin_port = htons(5188); 905 | servaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); 906 | 907 | if(connect(sock, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) 908 | ERR_EXIT("connect"); 909 | 910 | struct sockaddr_in localaddr; 911 | socklen_t addrlen = sizeof(localaddr); 912 | if((getsockname(sock, (struct sockaddr*)&localaddr, &addrlen)) < 0) 913 | ERR_EXIT("getsockname"); 914 | printf("ip = %s port = %d\n", inet_ntoa(localaddr.sin_addr), ntohs(localaddr.sin_port)); 915 | 916 | echo_cli(sock); 917 | 918 | return 0; 919 | } 920 | ``` 921 | 922 | ## 获取本机ip地址 923 | 924 | **gethost.cpp** 925 | 926 | ```c++ 927 | #include 928 | #include 929 | #include 930 | #include 931 | #include 932 | #include 933 | #include 934 | #include 935 | #include 936 | #include 937 | 938 | #define ERR_EXIT(m) \ 939 | do { \ 940 | perror(m); \ 941 | exit(EXIT_FAILURE); \ 942 | } while(0) 943 | 944 | /* 945 | struct hostent { 946 | char *h_name; 947 | char **h_aliases; 948 | int h_addrtype; 949 | int h_length; 950 | char **h_addr_list; 951 | */ 952 | 953 | int getlocalip(char *ip) { 954 | char host[100] = {0}; 955 | //获取本机主机名 956 | if(gethostname(host, sizeof(host)) < 0) 957 | return -1; 958 | 959 | struct hostent *hp; 960 | if((hp = gethostbyname(host)) == NULL) 961 | return -1; 962 | strcpy(ip, inet_ntoa(*(struct in_addr*)hp->h_addr)); 963 | return 0; 964 | } 965 | 966 | int main() { 967 | char host[100] = {0}; 968 | //获取本机主机名 969 | if(gethostname(host, sizeof(host)) < 0) 970 | ERR_EXIT("gethostname"); 971 | 972 | //获取主机中所有ip 973 | struct hostent *hp; 974 | if((hp = gethostbyname(host)) == NULL) 975 | ERR_EXIT("gethostbyname"); 976 | 977 | int i = 0; 978 | while(hp->h_addr_list[i] != NULL) { 979 | printf("%s\n", inet_ntoa(*(struct in_addr*)hp->h_addr_list[i])); 980 | ++i; 981 | } 982 | 983 | char ip[100]; 984 | if(getlocalip(ip) < 0) 985 | ERR_EXIT("getlocalip"); 986 | printf("localip = %s\n", ip); 987 | return 0; 988 | } 989 | ``` 990 | 991 | ## 多个文件描述符(I/O)的管理 992 | 993 | ### select函数实现单进程下多个客户端并发 994 | 995 | **echosrv.cpp** 996 | 997 | ```c++ 998 | #include 999 | #include 1000 | #include 1001 | #include 1002 | #include 1003 | #include 1004 | #include 1005 | #include 1006 | #include 1007 | #include 1008 | #include 1009 | #include 1010 | 1011 | #define ERR_EXIT(m) \ 1012 | do { \ 1013 | perror(m); \ 1014 | exit(EXIT_FAILURE); \ 1015 | } while(0) 1016 | 1017 | ssize_t readn(int fd, void *buf, size_t count) {//ssize_t 有符号整数 size_t 无符号整数 1018 | size_t nleft = count;//剩余的字节数 1019 | ssize_t nread;//已接收的字节数 1020 | char *bufp = (char*)buf; 1021 | 1022 | while(nleft > 0) { 1023 | if((nread = read(fd, bufp, nleft)) < 0) { 1024 | if(errno == EINTR)//被信号中断 1025 | continue; 1026 | return -1; 1027 | } 1028 | else if(nread == 0)//对等方关闭了 1029 | return count - nleft; 1030 | bufp += nread; 1031 | nleft -= nread; 1032 | } 1033 | return count; 1034 | } 1035 | 1036 | ssize_t writen(int fd, const void *buf, size_t count) { 1037 | size_t nleft = count;//剩余要发送的字节数 1038 | ssize_t nwritten;//已发送的字节数 1039 | char *bufp = (char*)buf; 1040 | 1041 | while(nleft > 0) { 1042 | if((nwritten = write(fd, bufp, nleft)) < 0) { 1043 | if(errno == EINTR)//被信号中断 1044 | continue; 1045 | return -1; 1046 | } 1047 | else if(nwritten == 0) 1048 | continue; 1049 | bufp += nwritten; 1050 | nleft -= nwritten; 1051 | } 1052 | return count; 1053 | } 1054 | 1055 | ssize_t recv_peek(int sockfd, void *buf, size_t len) {//接收数据后不将数据从缓冲区移除 1056 | while(1) { 1057 | int ret = recv(sockfd, buf, len, MSG_PEEK); 1058 | if(ret == -1 && errno == EINTR)//被信号中断 1059 | continue; 1060 | return ret; 1061 | } 1062 | } 1063 | 1064 | ssize_t readline(int sockfd, void *buf, size_t maxline) {//只能用于套接口 1065 | int ret; 1066 | int nread; 1067 | char *bufp = (char *)buf; 1068 | int nleft = maxline; 1069 | while(1) { 1070 | ret = recv_peek(sockfd, bufp, nleft); 1071 | if(ret < 0)//不用再进行中断判断,因为recv_peek函数内部已经进行了 1072 | return ret; 1073 | else if(ret == 0)//对方关闭 1074 | return ret; 1075 | nread = ret; 1076 | int i;//判断已接收到的缓冲区中是否有\n 1077 | for(i = 0; i < nread; ++i) { 1078 | if(bufp[i] == '\n') { 1079 | ret = readn(sockfd, bufp, i + 1);//将数据从缓冲区移除 1080 | if(ret != i + 1) 1081 | exit(EXIT_FAILURE); 1082 | return ret; 1083 | } 1084 | } 1085 | 1086 | if(nread > nleft) 1087 | exit(EXIT_FAILURE); 1088 | 1089 | nleft -= nread; 1090 | ret = readn(sockfd, bufp, nread);//还没遇到\n的数据也从缓冲区移除 1091 | if(ret != nread) 1092 | exit(EXIT_FAILURE); 1093 | 1094 | bufp += nread; 1095 | } 1096 | return -1; 1097 | } 1098 | 1099 | void echo_srv(int conn) { 1100 | //接收 1101 | char recvbuf[1024]; 1102 | while(1) { 1103 | memset(recvbuf, 0, sizeof(recvbuf)); 1104 | int ret = readline(conn, recvbuf, 1024); 1105 | if(ret == -1) 1106 | ERR_EXIT("readline"); 1107 | if(ret == 0) { 1108 | printf("client close\n"); 1109 | break; 1110 | } 1111 | 1112 | fputs(recvbuf, stdout); 1113 | writen(conn, recvbuf, strlen(recvbuf)); 1114 | } 1115 | } 1116 | 1117 | void handle_sigchld(int sig) { 1118 | //捕获子进程的初始状态 1119 | //wait(NULL); 1120 | while(waitpid(-1, NULL, WNOHANG) > 0);//可以等待所有子进程,大于0表示等待到了一个子进程 1121 | } 1122 | 1123 | int main() { 1124 | //signal(SIGCHLD, SIG_IGN); 1125 | signal(SIGCHLD, handle_sigchld); 1126 | //创建套接字 1127 | int listenfd; 1128 | if((listenfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) 1129 | ERR_EXIT("socket"); 1130 | 1131 | //地址初始化 1132 | struct sockaddr_in servaddr; //IPv4地址结构 1133 | memset(&servaddr, 0, sizeof(servaddr)); 1134 | servaddr.sin_family = AF_INET; 1135 | servaddr.sin_port = htons(5188); 1136 | servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//绑定本机的任意地址 1137 | 1138 | int on = 1; 1139 | if(setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) 1140 | ERR_EXIT("setsockopt"); 1141 | 1142 | //绑定 1143 | if(bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) 1144 | ERR_EXIT("bind"); 1145 | 1146 | //监听 1147 | if(listen(listenfd, SOMAXCONN) < 0) 1148 | ERR_EXIT("listen"); 1149 | 1150 | //连接 1151 | struct sockaddr_in peeraddr; 1152 | socklen_t peerlen; 1153 | int conn; 1154 | 1155 | int i; 1156 | int client[FD_SETSIZE];//保存多个客户端连接信息 1157 | int maxi = 0;//最大的不空闲的位置 1158 | 1159 | for(i = 0; i < FD_SETSIZE; ++i) 1160 | client[i] = -1;//-1表示空闲的 1161 | 1162 | int nready;//检测到的事件个数 1163 | int maxfd = listenfd; 1164 | fd_set rset; 1165 | fd_set allset; 1166 | FD_ZERO(&rset); 1167 | FD_ZERO(&allset); 1168 | FD_SET(listenfd, &allset); 1169 | 1170 | while(1) { 1171 | rset = allset; 1172 | nready = select(maxfd + 1, &rset, NULL, NULL, NULL); 1173 | if(nready == -1) { 1174 | if(errno == EINTR)//被信号中断 1175 | continue; 1176 | ERR_EXIT("select"); 1177 | } 1178 | if(nready == 0)//超时,现在timeout设置为NULL,故不可能发生 1179 | continue; 1180 | if(FD_ISSET(listenfd, &rset)) { 1181 | peerlen = sizeof(peeraddr);//必须设置一个初始值 1182 | conn = accept(listenfd, (struct sockaddr*)&peeraddr, &peerlen); 1183 | if(conn == -1) 1184 | ERR_EXIT("accept"); 1185 | //保存到某个空闲位置 1186 | for(i = 0; i < FD_SETSIZE; ++i) { 1187 | if(client[i] < 0) { 1188 | client[i] = conn; 1189 | if(i > maxi) 1190 | maxi = i; 1191 | break; 1192 | } 1193 | } 1194 | if(i == FD_SETSIZE) { 1195 | fprintf(stderr, "too mang clients\n"); 1196 | exit(EXIT_FAILURE); 1197 | } 1198 | printf("ip=%s port=%d\n", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port)); 1199 | 1200 | FD_SET(conn, &allset); 1201 | if(conn > maxfd) 1202 | maxfd = conn; 1203 | 1204 | if(--nready <= 0)//检测到的事件已经处理完了,应继续监听,没必要处理下面的代码 1205 | continue; 1206 | } 1207 | 1208 | //已连接套接口产生了数据 1209 | for(i = 0; i <= maxi; ++i) { 1210 | conn = client[i]; 1211 | if(conn == -1) 1212 | continue; 1213 | if(FD_ISSET(conn, &rset)) { 1214 | char recvbuf[1024] = {0}; 1215 | int ret = readline(conn, recvbuf, 1024); 1216 | if(ret == -1) 1217 | ERR_EXIT("readline"); 1218 | if(ret == 0) { 1219 | printf("client close\n"); 1220 | FD_CLR(conn, &allset); 1221 | client[i] = -1; 1222 | close(conn); 1223 | } 1224 | 1225 | fputs(recvbuf, stdout); 1226 | writen(conn, recvbuf, strlen(recvbuf)); 1227 | 1228 | if(--nready <= 0) 1229 | break; 1230 | } 1231 | } 1232 | } 1233 | return 0; 1234 | } 1235 | ``` 1236 | 1237 | **echocli.cpp** 1238 | 1239 | ```c++ 1240 | #include 1241 | #include 1242 | #include 1243 | #include 1244 | #include 1245 | #include 1246 | #include 1247 | #include 1248 | #include 1249 | #include 1250 | 1251 | #define ERR_EXIT(m) \ 1252 | do { \ 1253 | perror(m); \ 1254 | exit(EXIT_FAILURE); \ 1255 | } while(0) 1256 | 1257 | ssize_t readn(int fd, void *buf, size_t count) {//ssize_t 有符号整数 size_t 无符号整数 1258 | size_t nleft = count;//剩余的字节数 1259 | ssize_t nread;//已接收的字节数 1260 | char *bufp = (char*)buf; 1261 | 1262 | while(nleft > 0) { 1263 | if((nread = read(fd, bufp, nleft)) < 0) { 1264 | if(errno == EINTR)//被信号中断 1265 | continue; 1266 | return -1; 1267 | } 1268 | else if(nread == 0)//对等方关闭了 1269 | return count - nleft; 1270 | bufp += nread; 1271 | nleft -= nread; 1272 | } 1273 | return count; 1274 | } 1275 | 1276 | ssize_t writen(int fd, const void *buf, size_t count) { 1277 | size_t nleft = count;//剩余要发送的字节数 1278 | ssize_t nwritten;//已发送的字节数 1279 | char *bufp = (char*)buf; 1280 | 1281 | while(nleft > 0) { 1282 | if((nwritten = write(fd, bufp, nleft)) < 0) { 1283 | if(errno == EINTR)//被信号中断 1284 | continue; 1285 | return -1; 1286 | } 1287 | else if(nwritten == 0) 1288 | continue; 1289 | bufp += nwritten; 1290 | nleft -= nwritten; 1291 | } 1292 | return count; 1293 | } 1294 | 1295 | ssize_t recv_peek(int sockfd, void *buf, size_t len) {//接收数据后不将数据从缓冲区移除 1296 | while(1) { 1297 | int ret = recv(sockfd, buf, len, MSG_PEEK); 1298 | if(ret == -1 && errno == EINTR)//被信号中断 1299 | continue; 1300 | return ret; 1301 | } 1302 | } 1303 | 1304 | ssize_t readline(int sockfd, void *buf, size_t maxline) {//只能用于套接口 1305 | int ret; 1306 | int nread; 1307 | char *bufp = (char *)buf; 1308 | int nleft = maxline; 1309 | while(1) { 1310 | ret = recv_peek(sockfd, bufp, nleft); 1311 | if(ret < 0)//不用再进行中断判断,因为recv_peek函数内部已经进行了 1312 | return ret; 1313 | else if(ret == 0)//对方关闭 1314 | return ret; 1315 | nread = ret; 1316 | int i;//判断已接收到的缓冲区中是否有\n 1317 | for(i = 0; i < nread; ++i) { 1318 | if(bufp[i] == '\n') { 1319 | ret = readn(sockfd, bufp, i + 1);//将数据从缓冲区移除 1320 | if(ret != i + 1) 1321 | exit(EXIT_FAILURE); 1322 | return ret; 1323 | } 1324 | } 1325 | 1326 | if(nread > nleft) 1327 | exit(EXIT_FAILURE); 1328 | 1329 | nleft -= nread; 1330 | ret = readn(sockfd, bufp, nread);//还没遇到\n的数据也从缓冲区移除 1331 | if(ret != nread) 1332 | exit(EXIT_FAILURE); 1333 | 1334 | bufp += nread; 1335 | } 1336 | return -1; 1337 | } 1338 | 1339 | void echo_cli(int sock) { 1340 | fd_set rset; 1341 | FD_ZERO(&rset); 1342 | 1343 | //循环检测是否产生了可读事件 1344 | int nready; 1345 | int maxfd; 1346 | int fd_stdin = fileno(stdin);//标准输入的文件描述符 1347 | if(fd_stdin > sock) 1348 | maxfd = fd_stdin; 1349 | else 1350 | maxfd = sock; 1351 | 1352 | char sendbuf[1024] = {0}; 1353 | char recvbuf[1024] = {0}; 1354 | 1355 | int stdineof = 0; 1356 | 1357 | while(1) { 1358 | if(stdineof == 0) 1359 | FD_SET(fd_stdin, &rset); 1360 | FD_SET(sock, &rset); 1361 | nready = select(maxfd + 1, &rset, NULL, NULL, NULL); 1362 | if(nready == -1) 1363 | ERR_EXIT("select"); 1364 | if(nready == 0) 1365 | continue; 1366 | if(FD_ISSET(sock, &rset)) { 1367 | int ret = readline(sock, recvbuf, sizeof(recvbuf)); 1368 | if(ret == -1) 1369 | ERR_EXIT("readline"); 1370 | else if(ret == 0) { 1371 | printf("server close\n"); 1372 | break; 1373 | } 1374 | 1375 | fputs(recvbuf, stdout); 1376 | memset(recvbuf, 0, sizeof(recvbuf)); 1377 | } 1378 | if(FD_ISSET(fd_stdin, &rset)) { 1379 | if(fgets(sendbuf, sizeof(sendbuf), stdin) == NULL) { 1380 | stdineof = 1; 1381 | break; 1382 | } 1383 | writen(sock, sendbuf, strlen(sendbuf)); 1384 | memset(sendbuf, 0, sizeof(sendbuf)); 1385 | } 1386 | } 1387 | close(sock); 1388 | } 1389 | 1390 | int main() { 1391 | //创建套接字 1392 | int sock; 1393 | int i; 1394 | if((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) 1395 | ERR_EXIT("socket"); 1396 | 1397 | //地址初始化 1398 | struct sockaddr_in servaddr; //IPv4地址结构 1399 | memset(&servaddr, 0, sizeof(servaddr)); 1400 | servaddr.sin_family = AF_INET; 1401 | servaddr.sin_port = htons(5188); 1402 | servaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); 1403 | 1404 | if(connect(sock, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) 1405 | ERR_EXIT("connect"); 1406 | 1407 | struct sockaddr_in localaddr; 1408 | socklen_t addrlen = sizeof(localaddr); 1409 | if((getsockname(sock, (struct sockaddr*)&localaddr, &addrlen)) < 0) 1410 | ERR_EXIT("getsockname"); 1411 | printf("ip = %s port = %d\n", inet_ntoa(localaddr.sin_addr), ntohs(localaddr.sin_port)); 1412 | 1413 | echo_cli(sock); 1414 | 1415 | return 0; 1416 | } 1417 | ``` 1418 | 1419 | ### poll函数实现单进程下多个客户端并发 1420 | 1421 | **pollsrv.cpp** 1422 | 1423 | ```c++ 1424 | #include 1425 | #include 1426 | #include 1427 | #include 1428 | #include 1429 | #include 1430 | #include 1431 | #include 1432 | #include 1433 | #include 1434 | #include 1435 | #include 1436 | #include 1437 | 1438 | #define ERR_EXIT(m) \ 1439 | do { \ 1440 | perror(m); \ 1441 | exit(EXIT_FAILURE); \ 1442 | } while(0) 1443 | 1444 | ssize_t readn(int fd, void *buf, size_t count) {//ssize_t 有符号整数 size_t 无符号整数 1445 | size_t nleft = count;//剩余的字节数 1446 | ssize_t nread;//已接收的字节数 1447 | char *bufp = (char*)buf; 1448 | 1449 | while(nleft > 0) { 1450 | if((nread = read(fd, bufp, nleft)) < 0) { 1451 | if(errno == EINTR)//被信号中断 1452 | continue; 1453 | return -1; 1454 | } 1455 | else if(nread == 0)//对等方关闭了 1456 | return count - nleft; 1457 | bufp += nread; 1458 | nleft -= nread; 1459 | } 1460 | return count; 1461 | } 1462 | 1463 | ssize_t writen(int fd, const void *buf, size_t count) { 1464 | size_t nleft = count;//剩余要发送的字节数 1465 | ssize_t nwritten;//已发送的字节数 1466 | char *bufp = (char*)buf; 1467 | 1468 | while(nleft > 0) { 1469 | if((nwritten = write(fd, bufp, nleft)) < 0) { 1470 | if(errno == EINTR)//被信号中断 1471 | continue; 1472 | return -1; 1473 | } 1474 | else if(nwritten == 0) 1475 | continue; 1476 | bufp += nwritten; 1477 | nleft -= nwritten; 1478 | } 1479 | return count; 1480 | } 1481 | 1482 | ssize_t recv_peek(int sockfd, void *buf, size_t len) {//接收数据后不将数据从缓冲区移除 1483 | while(1) { 1484 | int ret = recv(sockfd, buf, len, MSG_PEEK); 1485 | if(ret == -1 && errno == EINTR)//被信号中断 1486 | continue; 1487 | return ret; 1488 | } 1489 | } 1490 | 1491 | ssize_t readline(int sockfd, void *buf, size_t maxline) {//只能用于套接口 1492 | int ret; 1493 | int nread; 1494 | char *bufp = (char *)buf; 1495 | int nleft = maxline; 1496 | while(1) { 1497 | ret = recv_peek(sockfd, bufp, nleft); 1498 | if(ret < 0)//不用再进行中断判断,因为recv_peek函数内部已经进行了 1499 | return ret; 1500 | else if(ret == 0)//对方关闭 1501 | return ret; 1502 | nread = ret; 1503 | int i;//判断已接收到的缓冲区中是否有\n 1504 | for(i = 0; i < nread; ++i) { 1505 | if(bufp[i] == '\n') { 1506 | ret = readn(sockfd, bufp, i + 1);//将数据从缓冲区移除 1507 | if(ret != i + 1) 1508 | exit(EXIT_FAILURE); 1509 | return ret; 1510 | } 1511 | } 1512 | 1513 | if(nread > nleft) 1514 | exit(EXIT_FAILURE); 1515 | 1516 | nleft -= nread; 1517 | ret = readn(sockfd, bufp, nread);//还没遇到\n的数据也从缓冲区移除 1518 | if(ret != nread) 1519 | exit(EXIT_FAILURE); 1520 | 1521 | bufp += nread; 1522 | } 1523 | return -1; 1524 | } 1525 | 1526 | void echo_srv(int conn) { 1527 | //接收 1528 | char recvbuf[1024]; 1529 | while(1) { 1530 | memset(recvbuf, 0, sizeof(recvbuf)); 1531 | int ret = readline(conn, recvbuf, 1024); 1532 | if(ret == -1) 1533 | ERR_EXIT("readline"); 1534 | if(ret == 0) { 1535 | printf("client close\n"); 1536 | break; 1537 | } 1538 | 1539 | fputs(recvbuf, stdout); 1540 | writen(conn, recvbuf, strlen(recvbuf)); 1541 | } 1542 | } 1543 | 1544 | void handle_sigchld(int sig) { 1545 | //捕获子进程的初始状态 1546 | //wait(NULL); 1547 | while(waitpid(-1, NULL, WNOHANG) > 0);//可以等待所有子进程,大于0表示等待到了一个子进程 1548 | } 1549 | 1550 | void handle_sigpipe(int sig) { 1551 | printf("recv a sig = %d\n", sig); 1552 | } 1553 | 1554 | int main() { 1555 | //signal(SIGPIPE, SIG_IGN); 1556 | signal(SIGPIPE, handle_sigpipe); 1557 | //signal(SIGCHLD, SIG_IGN); 1558 | signal(SIGCHLD, handle_sigchld); 1559 | //创建套接字 1560 | int listenfd; 1561 | if((listenfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) 1562 | ERR_EXIT("socket"); 1563 | 1564 | //地址初始化 1565 | struct sockaddr_in servaddr; //IPv4地址结构 1566 | memset(&servaddr, 0, sizeof(servaddr)); 1567 | servaddr.sin_family = AF_INET; 1568 | servaddr.sin_port = htons(5188); 1569 | servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//绑定本机的任意地址 1570 | 1571 | int on = 1; 1572 | if(setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) 1573 | ERR_EXIT("setsockopt"); 1574 | 1575 | //绑定 1576 | if(bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) 1577 | ERR_EXIT("bind"); 1578 | 1579 | //监听 1580 | if(listen(listenfd, SOMAXCONN) < 0) 1581 | ERR_EXIT("listen"); 1582 | 1583 | //连接 1584 | struct sockaddr_in peeraddr; 1585 | socklen_t peerlen; 1586 | int conn; 1587 | 1588 | /* 1589 | struct pollfd { 1590 | int fd;//文件描述符 1591 | short events;//请求事件/感兴趣事件 1592 | short revents;//返回事件 1593 | }; 1594 | */ 1595 | 1596 | int i; 1597 | struct pollfd client[2048];//保存多个客户端连接信息 1598 | int maxi = 0;//最大的不空闲的位置 1599 | 1600 | for(i = 0; i < 2048; ++i) 1601 | client[i].fd = -1;//-1表示空闲的 1602 | 1603 | int nready;//检测到的事件个数 1604 | client[0].fd = listenfd; 1605 | client[0].events = POLLIN;//对可读事件感兴趣 1606 | 1607 | while(1) { 1608 | nready = poll(client, maxi + 1, -1); 1609 | if(nready == -1) { 1610 | if(errno == EINTR)//被信号中断 1611 | continue; 1612 | ERR_EXIT("poll"); 1613 | } 1614 | if(nready == 0)//超时,现在timeout设置为NULL,故不可能发生 1615 | continue; 1616 | 1617 | if(client[0].revents & POLLIN) { 1618 | peerlen = sizeof(peeraddr);//必须设置一个初始值 1619 | conn = accept(listenfd, (struct sockaddr*)&peeraddr, &peerlen); 1620 | if(conn == -1) 1621 | ERR_EXIT("accept"); 1622 | //保存到某个空闲位置 1623 | for(i = 0; i < 2048; ++i) { 1624 | if(client[i].fd < 0) { 1625 | client[i].fd = conn; 1626 | if(i > maxi) 1627 | maxi = i; 1628 | break; 1629 | } 1630 | } 1631 | if(i == 2048) { 1632 | fprintf(stderr, "too mang clients\n"); 1633 | exit(EXIT_FAILURE); 1634 | } 1635 | printf("ip=%s port=%d\n", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port)); 1636 | 1637 | client[i].events = POLLIN; 1638 | 1639 | if(--nready <= 0)//检测到的事件已经处理完了,应继续监听,没必要处理下面的代码 1640 | continue; 1641 | } 1642 | 1643 | //已连接套接口产生了数据 1644 | for(i = 1; i <= maxi; ++i) { 1645 | conn = client[i].fd; 1646 | if(conn == -1) 1647 | continue; 1648 | if(client[i].events & POLLIN) { 1649 | char recvbuf[1024] = {0}; 1650 | int ret = readline(conn, recvbuf, 1024); 1651 | if(ret == -1) 1652 | ERR_EXIT("readline"); 1653 | if(ret == 0) { 1654 | printf("client close\n"); 1655 | client[i].fd = -1; 1656 | close(conn); 1657 | } 1658 | 1659 | fputs(recvbuf, stdout); 1660 | writen(conn, recvbuf, strlen(recvbuf)); 1661 | 1662 | if(--nready <= 0) 1663 | break; 1664 | } 1665 | } 1666 | } 1667 | return 0; 1668 | } 1669 | ``` 1670 | 1671 | ### epoll函数实现单进程下多个客户端并发 1672 | 1673 | **epollsrv.cpp** 1674 | 1675 | ```c++ 1676 | #include 1677 | #include 1678 | #include 1679 | #include 1680 | #include 1681 | #include 1682 | #include 1683 | #include 1684 | #include 1685 | #include 1686 | #include 1687 | #include 1688 | #include 1689 | #include 1690 | #include 1691 | 1692 | typedef std::vector EventList; 1693 | 1694 | #define ERR_EXIT(m) \ 1695 | do { \ 1696 | perror(m); \ 1697 | exit(EXIT_FAILURE); \ 1698 | } while(0) 1699 | 1700 | void activate_nonblock(int fd) { 1701 | int ret; 1702 | int flags = fcntl(fd, F_GETFL); 1703 | if(flags == -1) 1704 | ERR_EXIT("fcntl"); 1705 | 1706 | flags |= O_NONBLOCK; 1707 | ret = fcntl(fd, F_SETFL, flags); 1708 | if(ret == -1) 1709 | ERR_EXIT("fcntl"); 1710 | } 1711 | 1712 | ssize_t readn(int fd, void *buf, size_t count) {//ssize_t 有符号整数 size_t 无符号整数 1713 | size_t nleft = count;//剩余的字节数 1714 | ssize_t nread;//已接收的字节数 1715 | char *bufp = (char*)buf; 1716 | 1717 | while(nleft > 0) { 1718 | if((nread = read(fd, bufp, nleft)) < 0) { 1719 | if(errno == EINTR)//被信号中断 1720 | continue; 1721 | return -1; 1722 | } 1723 | else if(nread == 0)//对等方关闭了 1724 | return count - nleft; 1725 | bufp += nread; 1726 | nleft -= nread; 1727 | } 1728 | return count; 1729 | } 1730 | 1731 | ssize_t writen(int fd, const void *buf, size_t count) { 1732 | size_t nleft = count;//剩余要发送的字节数 1733 | ssize_t nwritten;//已发送的字节数 1734 | char *bufp = (char*)buf; 1735 | 1736 | while(nleft > 0) { 1737 | if((nwritten = write(fd, bufp, nleft)) < 0) { 1738 | if(errno == EINTR)//被信号中断 1739 | continue; 1740 | return -1; 1741 | } 1742 | else if(nwritten == 0) 1743 | continue; 1744 | bufp += nwritten; 1745 | nleft -= nwritten; 1746 | } 1747 | return count; 1748 | } 1749 | 1750 | ssize_t recv_peek(int sockfd, void *buf, size_t len) {//接收数据后不将数据从缓冲区移除 1751 | while(1) { 1752 | int ret = recv(sockfd, buf, len, MSG_PEEK); 1753 | if(ret == -1 && errno == EINTR)//被信号中断 1754 | continue; 1755 | return ret; 1756 | } 1757 | } 1758 | 1759 | ssize_t readline(int sockfd, void *buf, size_t maxline) {//只能用于套接口 1760 | int ret; 1761 | int nread; 1762 | char *bufp = (char *)buf; 1763 | int nleft = maxline; 1764 | while(1) { 1765 | ret = recv_peek(sockfd, bufp, nleft); 1766 | if(ret < 0)//不用再进行中断判断,因为recv_peek函数内部已经进行了 1767 | return ret; 1768 | else if(ret == 0)//对方关闭 1769 | return ret; 1770 | nread = ret; 1771 | int i;//判断已接收到的缓冲区中是否有\n 1772 | for(i = 0; i < nread; ++i) { 1773 | if(bufp[i] == '\n') { 1774 | ret = readn(sockfd, bufp, i + 1);//将数据从缓冲区移除 1775 | if(ret != i + 1) 1776 | exit(EXIT_FAILURE); 1777 | return ret; 1778 | } 1779 | } 1780 | 1781 | if(nread > nleft) 1782 | exit(EXIT_FAILURE); 1783 | 1784 | nleft -= nread; 1785 | ret = readn(sockfd, bufp, nread);//还没遇到\n的数据也从缓冲区移除 1786 | if(ret != nread) 1787 | exit(EXIT_FAILURE); 1788 | 1789 | bufp += nread; 1790 | } 1791 | return -1; 1792 | } 1793 | 1794 | void echo_srv(int conn) { 1795 | //接收 1796 | char recvbuf[1024]; 1797 | while(1) { 1798 | memset(recvbuf, 0, sizeof(recvbuf)); 1799 | int ret = readline(conn, recvbuf, 1024); 1800 | if(ret == -1) 1801 | ERR_EXIT("readline"); 1802 | if(ret == 0) { 1803 | printf("client close\n"); 1804 | break; 1805 | } 1806 | 1807 | fputs(recvbuf, stdout); 1808 | writen(conn, recvbuf, strlen(recvbuf)); 1809 | } 1810 | } 1811 | 1812 | void handle_sigchld(int sig) { 1813 | //捕获子进程的初始状态 1814 | //wait(NULL); 1815 | while(waitpid(-1, NULL, WNOHANG) > 0);//可以等待所有子进程,大于0表示等待到了一个子进程 1816 | } 1817 | 1818 | void handle_sigpipe(int sig) { 1819 | printf("recv a sig = %d\n", sig); 1820 | } 1821 | 1822 | int main() { 1823 | int count = 0; 1824 | signal(SIGPIPE, handle_sigpipe); 1825 | signal(SIGCHLD, handle_sigchld); 1826 | //创建套接字 1827 | int listenfd; 1828 | if((listenfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) 1829 | ERR_EXIT("socket"); 1830 | 1831 | //地址初始化 1832 | struct sockaddr_in servaddr; //IPv4地址结构 1833 | memset(&servaddr, 0, sizeof(servaddr)); 1834 | servaddr.sin_family = AF_INET; 1835 | servaddr.sin_port = htons(5188); 1836 | servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//绑定本机的任意地址 1837 | 1838 | int on = 1; 1839 | if(setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) 1840 | ERR_EXIT("setsockopt"); 1841 | 1842 | //绑定 1843 | if(bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) 1844 | ERR_EXIT("bind"); 1845 | 1846 | //监听 1847 | if(listen(listenfd, SOMAXCONN) < 0) 1848 | ERR_EXIT("listen"); 1849 | 1850 | std::vector clients; 1851 | int epollfd; 1852 | //创建一个epoll实例,EPOLL_CLOEXEC表示当进程被替换时文件描述符会关闭 1853 | epollfd = epoll_create1(EPOLL_CLOEXEC); 1854 | 1855 | /* 1856 | typedef union epoll_data { 1857 | void *ptr; 1858 | int fd; 1859 | __uint32_t u32; 1860 | __uint64_t u64; 1861 | } epoll_data_t;//大小为8个字节 1862 | 1863 | struct epoll_event { 1864 | __uint32_t events;//Epoll事件 1865 | epoll_data_t data;//用户数据变量 ,数据类型为共用体 1866 | }; 1867 | */ 1868 | 1869 | struct epoll_event event; 1870 | event.data.fd = listenfd; 1871 | event.events = EPOLLIN | EPOLLET;//EPOLLET表示边沿方式触发 1872 | //将感兴趣的文件描述符加入epoll进行管理 1873 | epoll_ctl(epollfd, EPOLL_CTL_ADD, listenfd, &event); 1874 | 1875 | //连接 1876 | EventList events(16); 1877 | struct sockaddr_in peeraddr; 1878 | socklen_t peerlen; 1879 | int conn; 1880 | int i; 1881 | 1882 | int nready;//检测到的事件个数 1883 | while(1) { 1884 | nready = epoll_wait(epollfd, &*events.begin(), static_cast(events.size()), -1); 1885 | if(nready == -1) { 1886 | if(errno == EINTR)//被信号中断 1887 | continue; 1888 | ERR_EXIT("poll_wait"); 1889 | } 1890 | if(nready == 0)//超时,现在timeout设置为NULL,故不可能发生 1891 | continue; 1892 | 1893 | if((size_t)nready == events.size()) 1894 | events.resize(events.size() * 2); 1895 | 1896 | //遍历返回的事件 1897 | for(i = 0; i < nready; ++i) { 1898 | if(events[i].data.fd == listenfd) { 1899 | peerlen = sizeof(peeraddr);//必须设置一个初始值 1900 | conn = accept(listenfd, (struct sockaddr*)&peeraddr, &peerlen); 1901 | if(conn == -1) 1902 | ERR_EXIT("accept"); 1903 | 1904 | printf("ip=%s port=%d\n", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port)); 1905 | printf("count = %d\n", ++count); 1906 | clients.push_back(conn); 1907 | 1908 | activate_nonblock(conn); 1909 | 1910 | event.data.fd = conn; 1911 | event.events = EPOLLIN | EPOLLET; 1912 | epoll_ctl(epollfd, EPOLL_CTL_ADD, conn, &event); 1913 | } 1914 | else if(events[i].events & EPOLLIN) { 1915 | conn = events[i].data.fd; 1916 | if(conn < 0) 1917 | continue; 1918 | 1919 | char recvbuf[1024] = {0}; 1920 | int ret = readline(conn, recvbuf, 1024); 1921 | if(ret == -1) 1922 | ERR_EXIT("readline"); 1923 | if(ret == 0) { 1924 | printf("client close\n"); 1925 | close(conn); 1926 | event = events[i]; 1927 | epoll_ctl(epollfd, EPOLL_CTL_DEL, conn, &event); 1928 | clients.erase(std::remove(clients.begin(), clients.end(), conn), clients.end()); 1929 | } 1930 | 1931 | fputs(recvbuf, stdout); 1932 | writen(conn, recvbuf, strlen(recvbuf)); 1933 | } 1934 | } 1935 | } 1936 | return 0; 1937 | } 1938 | ``` 1939 | 1940 | ## UDP客户/服务器模型 1941 | 1942 | **echosrv.cpp** 1943 | 1944 | ```c++ 1945 | #include 1946 | #include 1947 | #include 1948 | #include //sockaddr_in 1949 | #include 1950 | #include 1951 | #include 1952 | #include 1953 | 1954 | #define ERR_EXIT(m) \ 1955 | do { \ 1956 | perror(m); \ 1957 | exit(EXIT_FAILURE); \ 1958 | } while(0) 1959 | 1960 | void echo_srv(int sock) { 1961 | char recvbuf[1024] = {0}; 1962 | struct sockaddr_in peeraddr; 1963 | socklen_t peerlen; 1964 | int n;//接收到的字节数 1965 | while(1) { 1966 | peerlen = sizeof(peeraddr); 1967 | memset(recvbuf, 0, sizeof(recvbuf)); 1968 | n = recvfrom(sock, recvbuf, sizeof(recvbuf), 0, (struct sockaddr*)&peeraddr, &peerlen); 1969 | if(n == -1) { 1970 | if(errno == EINTR) 1971 | continue; 1972 | ERR_EXIT("recvfrom"); 1973 | } 1974 | else if(n > 0) {//回射回去 1975 | fputs(recvbuf, stdout); 1976 | sendto(sock, recvbuf, n, 0, (struct sockaddr*)&peeraddr, peerlen); 1977 | } 1978 | } 1979 | close(sock); 1980 | } 1981 | 1982 | int main() { 1983 | //创建套接字 1984 | int sock; 1985 | if((sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0)//SOCK_DGRAM代表UDP 1986 | ERR_EXIT("socket"); 1987 | //初始化地址 1988 | struct sockaddr_in servaddr; 1989 | memset(&servaddr, 0, sizeof(servaddr)); 1990 | servaddr.sin_family = AF_INET; 1991 | servaddr.sin_port = htons(5188); 1992 | servaddr.sin_addr.s_addr = htonl(INADDR_ANY); 1993 | 1994 | //绑定 1995 | if(bind(sock, (struct sockaddr*)& servaddr, sizeof(servaddr)) < 0) 1996 | ERR_EXIT("bind"); 1997 | 1998 | echo_srv(sock); 1999 | 2000 | return 0; 2001 | } 2002 | ``` 2003 | 2004 | **echocli.cpp** 2005 | 2006 | ```c++ 2007 | #include 2008 | #include 2009 | #include 2010 | #include //sockaddr_in 2011 | #include 2012 | #include 2013 | #include 2014 | #include 2015 | #include 2016 | 2017 | #define ERR_EXIT(m) \ 2018 | do { \ 2019 | perror(m); \ 2020 | exit(EXIT_FAILURE); \ 2021 | } while(0) 2022 | 2023 | void echo_cli(int sock) { 2024 | struct sockaddr_in servaddr; 2025 | memset(&servaddr, 0, sizeof(servaddr)); 2026 | servaddr.sin_family = AF_INET; 2027 | servaddr.sin_port = htons(5188); 2028 | servaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); 2029 | 2030 | char sendbuf[1024] = {0}; 2031 | char recvbuf[1024] = {0}; 2032 | while(fgets(sendbuf, sizeof(sendbuf), stdin) != NULL) { 2033 | sendto(sock, sendbuf, strlen(sendbuf), 0, (struct sockaddr*)&servaddr, sizeof(servaddr)); 2034 | recvfrom(sock, recvbuf, sizeof(recvbuf), 0, NULL, NULL); 2035 | fputs(recvbuf, stdout); 2036 | memset(sendbuf, 0, sizeof(sendbuf)); 2037 | memset(recvbuf, 0, sizeof(recvbuf)); 2038 | } 2039 | 2040 | close(sock); 2041 | } 2042 | 2043 | int main() { 2044 | //创建套接字 2045 | int sock; 2046 | if((sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0)//SOCK_DGRAM代表UDP 2047 | ERR_EXIT("socket"); 2048 | 2049 | echo_cli(sock); 2050 | 2051 | return 0; 2052 | } 2053 | ``` 2054 | 2055 | ### UDP聊天室实现 2056 | 2057 | **chatsrv.cpp** 2058 | 2059 | ```c++ 2060 | #include 2061 | #include 2062 | #include 2063 | #include 2064 | #include 2065 | #include 2066 | #include 2067 | #include 2068 | #include 2069 | #include 2070 | 2071 | #include "pub.h" 2072 | 2073 | #define ERR_EXIT(m) \ 2074 | do { \ 2075 | perror(m); \ 2076 | exit(EXIT_FAILURE); \ 2077 | } while(0) 2078 | 2079 | USER_LIST client_list;//聊天室成员列表 2080 | 2081 | void do_login(MESSAGE& msg, int sock, struct sockaddr_in *cliaddr); 2082 | void do_logout(MESSAGE& msg, int sock, struct sockaddr_in *cliaddr); 2083 | void do_sendlist(int sock, struct sockaddr_in *cliaddr); 2084 | 2085 | void chat_srv(int sock) { 2086 | struct sockaddr_in cliaddr; 2087 | socklen_t clilen; 2088 | int n; 2089 | MESSAGE msg; 2090 | while(1) { 2091 | memset(&msg, 0, sizeof(msg)); 2092 | clilen = sizeof(cliaddr); 2093 | n = recvfrom(sock, &msg, sizeof(msg), 0, (struct sockaddr*)&cliaddr,&clilen); 2094 | if(n < 0) { 2095 | if(errno == EINTR) 2096 | continue; 2097 | ERR_EXIT("recvfrom"); 2098 | } 2099 | 2100 | int cmd = ntohl(msg.cmd); 2101 | switch(cmd) { 2102 | case C2S_LOGIN: 2103 | do_login(msg, sock, &cliaddr); 2104 | break; 2105 | case C2S_LOGOUT: 2106 | do_logout(msg, sock, &cliaddr); 2107 | break; 2108 | case C2S_ONLINE_USER: 2109 | do_sendlist(sock, &cliaddr); 2110 | break; 2111 | default: 2112 | break; 2113 | } 2114 | } 2115 | } 2116 | 2117 | void do_login(MESSAGE& msg, int sock, struct sockaddr_in *cliaddr) { 2118 | USER_INFO user; 2119 | strcpy(user.username, msg.body); 2120 | user.ip = cliaddr->sin_addr.s_addr; 2121 | user.port = cliaddr->sin_port; 2122 | 2123 | //查找用户 2124 | USER_LIST::iterator it; 2125 | for(it = client_list.begin(); it != client_list.end(); ++it) 2126 | if(strcmp(it->username, msg.body) == 0) 2127 | break; 2128 | 2129 | if(it == client_list.end()) {//没找到用户 2130 | printf("has a user login : %s <-> %s:%d\n", msg.body, inet_ntoa(cliaddr->sin_addr), cliaddr->sin_port); 2131 | client_list.push_back(user); 2132 | 2133 | //登录成功应答 2134 | MESSAGE reply_msg; 2135 | memset(&reply_msg, 0, sizeof(reply_msg)); 2136 | reply_msg.cmd = htonl(S2C_LOGIN_OK); 2137 | sendto(sock, &reply_msg, sizeof(msg), 0, (struct sockaddr*)cliaddr, sizeof(*cliaddr)); 2138 | 2139 | int count = htonl((int)client_list.size()); 2140 | //发送在线人数 2141 | sendto(sock, &count, sizeof(int), 0, (struct sockaddr*)cliaddr, sizeof(*cliaddr)); 2142 | 2143 | //发送在线列表 2144 | printf("sending user list informatio to:%s <-> %s:%d\n",msg.body, inet_ntoa(cliaddr->sin_addr), cliaddr->sin_port); 2145 | for(it = client_list.begin(); it != client_list.end(); ++it) 2146 | sendto(sock, &*it, sizeof(USER_INFO), 0, (struct sockaddr*)cliaddr,sizeof(*cliaddr)); 2147 | 2148 | //向其他用户通知有新用户登录 2149 | for(it = client_list.begin(); it != client_list.end(); ++it) { 2150 | if(strcmp(it->username, msg.body) == 0) 2151 | continue; 2152 | 2153 | struct sockaddr_in peeraddr; 2154 | memset(&peeraddr, 0, sizeof(peeraddr)); 2155 | peeraddr.sin_family = AF_INET; 2156 | peeraddr.sin_port = it->port; 2157 | peeraddr.sin_addr.s_addr = it->ip; 2158 | 2159 | msg.cmd = htonl(S2C_SOMEONE_LOGIN); 2160 | memcpy(msg.body, &user, sizeof(user)); 2161 | 2162 | if(sendto(sock, &msg, sizeof(msg), 0, (struct sockaddr*)&peeraddr, sizeof(peeraddr)) < 0) 2163 | ERR_EXIT("sendto"); 2164 | } 2165 | } 2166 | else {//找到用户 2167 | printf("user %s has already logined\n", msg.body); 2168 | 2169 | MESSAGE reply_msg; 2170 | memset(&reply_msg, 0, sizeof(reply_msg)); 2171 | reply_msg.cmd = htonl(S2C_ALREADY_LOGINED); 2172 | sendto(sock, &reply_msg, sizeof(reply_msg), 0, (struct sockaddr*)cliaddr, sizeof(*cliaddr)); 2173 | } 2174 | } 2175 | 2176 | void do_logout(MESSAGE& msg, int sock, struct sockaddr_in *cliaddr) { 2177 | printf("has a user logout : %s <-> %s:%d\n", msg.body, inet_ntoa(cliaddr->sin_addr), ntohs(cliaddr->sin_port)); 2178 | 2179 | USER_LIST::iterator it; 2180 | for(it = client_list.begin(); it != client_list.end(); ++it) { 2181 | if(strcmp(it->username, msg.body) == 0) { 2182 | it = client_list.erase(it); 2183 | continue; 2184 | } 2185 | 2186 | struct sockaddr_in peeraddr; 2187 | memset(&peeraddr, 0, sizeof(peeraddr)); 2188 | peeraddr.sin_family = AF_INET; 2189 | peeraddr.sin_port = it->port; 2190 | peeraddr.sin_addr.s_addr = it->ip; 2191 | 2192 | msg.cmd = htonl(S2C_SOMEONE_LOGOUT); 2193 | 2194 | if(sendto(sock, &msg, sizeof(msg), 0, (struct sockaddr*)&peeraddr, sizeof(peeraddr)) < 0) 2195 | ERR_EXIT("sendto"); 2196 | } 2197 | } 2198 | 2199 | void do_sendlist(int sock, struct sockaddr_in *cliaddr) { 2200 | MESSAGE msg; 2201 | msg.cmd = htonl(S2C_ONLINE_USER); 2202 | sendto(sock, (const char*)&msg, sizeof(msg), 0, (struct sockaddr*)cliaddr, sizeof(*cliaddr)); 2203 | 2204 | int count = htonl((int)client_list.size()); 2205 | //发送在线用户数 2206 | sendto(sock, (const char*)&count, sizeof(int), 0, (struct sockaddr*)cliaddr, sizeof(*cliaddr)); 2207 | //发送在线用户列表 2208 | for(USER_LIST::iterator it = client_list.begin(); it != client_list.end(); ++it) { 2209 | sendto(sock,&*it, sizeof(USER_INFO), 0, (struct sockaddr*)cliaddr, sizeof(*cliaddr)); 2210 | } 2211 | } 2212 | 2213 | int main() { 2214 | int sock; 2215 | if((sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0)//SOCK_DGRAM代表UDP 2216 | ERR_EXIT("socket"); 2217 | 2218 | struct sockaddr_in servaddr; 2219 | memset(&servaddr, 0, sizeof(servaddr)); 2220 | servaddr.sin_family = AF_INET; 2221 | servaddr.sin_port = htons(5188); 2222 | servaddr.sin_addr.s_addr = htonl(INADDR_ANY); 2223 | 2224 | if(bind(sock, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) 2225 | ERR_EXIT("bind"); 2226 | 2227 | chat_srv(sock); 2228 | 2229 | return 0; 2230 | } 2231 | ``` 2232 | 2233 | **chatcli.cpp** 2234 | 2235 | ```c++ 2236 | #include 2237 | #include 2238 | #include 2239 | #include 2240 | #include 2241 | #include 2242 | #include 2243 | #include 2244 | #include 2245 | #include 2246 | 2247 | #include "pub.h" 2248 | 2249 | #define ERR_EXIT(m) \ 2250 | do { \ 2251 | perror(m); \ 2252 | exit(EXIT_FAILURE); \ 2253 | } while(0) 2254 | 2255 | char username[16]; 2256 | 2257 | USER_LIST client_list;//聊天室成员列表 2258 | 2259 | void do_someone_login(MESSAGE& msg); 2260 | void do_someone_logout(MESSAGE& msg); 2261 | void do_getlist(); 2262 | void do_chat(); 2263 | 2264 | void parse_cmd(char* cmdline, int sock, struct sockaddr_in *servaddr); 2265 | bool sendmsgto(int sock, char* username, char* msg); 2266 | 2267 | void parse_cmd(char* cmdline, int sock, struct sockaddr_in *servaddr) { 2268 | char cmd[10] = {0}; 2269 | char *p; 2270 | p = strchr(cmdline, ' '); 2271 | if(p != NULL) 2272 | *p = '\0'; 2273 | 2274 | strcpy(cmd, cmdline); 2275 | 2276 | if(strcmp(cmd, "exit") == 0) { 2277 | MESSAGE msg; 2278 | memset(&msg, 0, sizeof(msg)); 2279 | msg.cmd = htonl(C2S_LOGOUT); 2280 | strcpy(msg.body, username); 2281 | 2282 | if(sendto(sock, &msg, sizeof(msg), 0, (struct sockaddr*)servaddr, sizeof(*servaddr)) < 0) 2283 | ERR_EXIT("sendto"); 2284 | 2285 | printf("user %s has logout server\n", username); 2286 | exit(EXIT_SUCCESS); 2287 | } 2288 | else if(strcmp(cmd, "send") == 0) { 2289 | char peername[16] = {0}; 2290 | char msg[MSG_LEN] = {0}; 2291 | 2292 | /* send user msg */ 2293 | /* p p2 */ 2294 | while(*p++ == ' ') ; 2295 | char *p2; 2296 | p2 = strchr(p, ' '); 2297 | if(p2 == NULL) { 2298 | printf("bad command\n"); 2299 | printf("\nCommands are:\n"); 2300 | printf("send username msg\n"); 2301 | printf("list\n"); 2302 | printf("exit\n"); 2303 | printf("\n"); 2304 | return; 2305 | } 2306 | *p2 = '\0'; 2307 | strcpy(peername, p); 2308 | 2309 | while(*p2++ == ' ') ; 2310 | strcpy(msg, p2); 2311 | sendmsgto(sock, peername, msg); 2312 | } 2313 | else if(strcmp(cmd, "list") == 0) { 2314 | MESSAGE msg; 2315 | memset(&msg, 0, sizeof(msg)); 2316 | msg.cmd = htonl(C2S_ONLINE_USER); 2317 | 2318 | if(sendto(sock, &msg, sizeof(msg), 0, (struct sockaddr*)servaddr, sizeof(*servaddr)) < 0) 2319 | ERR_EXIT("sendto"); 2320 | } 2321 | else { 2322 | printf("bad command\n"); 2323 | printf("\nCommands are:\n"); 2324 | printf("send username msg\n"); 2325 | printf("list\n"); 2326 | printf("exit\n"); 2327 | printf("\n"); 2328 | } 2329 | } 2330 | 2331 | bool sendmsgto(int sock, char* name, char* msg) { 2332 | if(strcmp(name, username) == 0) { 2333 | printf("can't send message to self\n"); 2334 | return false; 2335 | } 2336 | 2337 | USER_LIST::iterator it; 2338 | for(it = client_list.begin(); it != client_list.end(); ++it) { 2339 | if(strcmp(it->username, name) == 0) 2340 | break; 2341 | } 2342 | 2343 | if(it == client_list.end()) { 2344 | printf("user %s has not logined server\n", name); 2345 | return false; 2346 | } 2347 | 2348 | MESSAGE m; 2349 | memset(&m, 0, sizeof(m)); 2350 | m.cmd = htonl(C2C_CHAT); 2351 | 2352 | CHAT_MSG cm; 2353 | strcpy(cm.username, username); 2354 | strcpy(cm.msg, msg); 2355 | 2356 | memcpy(m.body, &cm, sizeof(cm)); 2357 | 2358 | struct sockaddr_in peeraddr; 2359 | memset(&peeraddr, 0, sizeof(peeraddr)); 2360 | peeraddr.sin_family = AF_INET; 2361 | peeraddr.sin_addr.s_addr = it->ip; 2362 | peeraddr.sin_port = it->port; 2363 | 2364 | in_addr tmp; 2365 | tmp.s_addr = it->ip; 2366 | 2367 | printf("sending message [%s] to user [%s] <-> %s:%d\n", msg, name, inet_ntoa(tmp), it->port); 2368 | 2369 | sendto(sock, (const char*)&m, sizeof(m), 0, (struct sockaddr*)&peeraddr, sizeof(peeraddr)); 2370 | return true; 2371 | } 2372 | 2373 | void do_getlist(int sock) { 2374 | int count; 2375 | recvfrom(sock, &count, sizeof(int), 0, NULL, NULL); 2376 | printf("has %d users logined server\n", ntohl(count)); 2377 | client_list.clear(); 2378 | 2379 | int n = ntohl(count); 2380 | for(int i = 0; i < n; ++i) { 2381 | USER_INFO user; 2382 | recvfrom(sock, &user, sizeof(USER_INFO), 0, NULL, NULL); 2383 | in_addr tmp; 2384 | tmp.s_addr = user.ip; 2385 | 2386 | printf("%s <-> %s:%d\n", user.username, inet_ntoa(tmp), ntohs(user.port)); 2387 | } 2388 | } 2389 | 2390 | void do_someone_login(MESSAGE& msg) { 2391 | USER_INFO *user = (USER_INFO*)msg.body; 2392 | in_addr tmp; 2393 | tmp.s_addr = user->ip; 2394 | printf("%s <-> %s:%d has logined server\n", user->username, inet_ntoa(tmp), user->port); 2395 | client_list.push_back(*user); 2396 | } 2397 | 2398 | void do_someone_logout(MESSAGE& msg) { 2399 | USER_LIST::iterator it; 2400 | for(it = client_list.begin(); it != client_list.end(); ++it) { 2401 | if(strcmp(it->username, msg.body) == 0) 2402 | break; 2403 | } 2404 | 2405 | if(it != client_list.end()) 2406 | client_list.erase(it); 2407 | 2408 | printf("user %s has logout server\n", msg.body); 2409 | } 2410 | 2411 | void do_chat(const MESSAGE& msg) { 2412 | CHAT_MSG *cm = (CHAT_MSG*)msg.body; 2413 | printf("recv a msg [%s] from [%s]\n", cm->msg, cm->username); 2414 | } 2415 | 2416 | void chat_cli(int sock) { 2417 | struct sockaddr_in servaddr; 2418 | memset(&servaddr, 0, sizeof(servaddr)); 2419 | servaddr.sin_family = AF_INET; 2420 | servaddr.sin_port = htons(5188); 2421 | servaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); 2422 | 2423 | struct sockaddr_in peeraddr; 2424 | socklen_t peerlen; 2425 | 2426 | MESSAGE msg; 2427 | while(1) { 2428 | memset(username, 0, sizeof(username)); 2429 | printf("please input your name:"); 2430 | fflush(stdout); 2431 | scanf("%s", username); 2432 | 2433 | memset(&msg, 0, sizeof(msg)); 2434 | msg.cmd = htonl(C2S_LOGIN); 2435 | strcpy(msg.body, username); 2436 | 2437 | sendto(sock, &msg, sizeof(msg), 0, (struct sockaddr*)&servaddr, sizeof(servaddr)); 2438 | 2439 | memset(&msg, 0, sizeof(msg)); 2440 | recvfrom(sock, &msg, sizeof(msg), 0, NULL, NULL); 2441 | int cmd = ntohl(msg.cmd); 2442 | if(cmd == S2C_ALREADY_LOGINED) 2443 | printf("user %s already logined server, please use another username\n", username); 2444 | else if(cmd == S2C_LOGIN_OK) { 2445 | printf("user %s already server\n", username); 2446 | break; 2447 | } 2448 | } 2449 | int count; 2450 | recvfrom(sock, &count, sizeof(int), 0, NULL, NULL); 2451 | 2452 | int n = ntohl(count); 2453 | printf("has %d user logined server\n", n); 2454 | 2455 | for(int i = 0; i < n; ++i) { 2456 | USER_INFO user; 2457 | recvfrom(sock, &user, sizeof(USER_INFO), 0, NULL, NULL); 2458 | client_list.push_back(user); 2459 | in_addr tmp; 2460 | tmp.s_addr = user.ip; 2461 | 2462 | printf("%d %s <-> %s:%d\n", i, user.username, inet_ntoa(tmp), ntohs(user.port)); 2463 | } 2464 | 2465 | printf("\nCommands are:\n"); 2466 | printf("send username msg\n"); 2467 | printf("list\n"); 2468 | printf("exit\n"); 2469 | printf("\n"); 2470 | 2471 | fd_set rset; 2472 | FD_ZERO(&rset); 2473 | int nready; 2474 | while(1) { 2475 | FD_SET(STDIN_FILENO, &rset); 2476 | FD_SET(sock, &rset); 2477 | nready = select(sock + 1, &rset, NULL, NULL, NULL); 2478 | if(nready == -1) 2479 | ERR_EXIT("select"); 2480 | if(nready == 0) 2481 | continue; 2482 | if(FD_ISSET(sock, &rset)) { 2483 | peerlen = sizeof(peeraddr); 2484 | memset(&msg, 0, sizeof(msg)); 2485 | recvfrom(sock, &msg, sizeof(msg), 0, (struct sockaddr*)&peeraddr, &peerlen); 2486 | int cmd = ntohl(msg.cmd); 2487 | switch(cmd) { 2488 | case S2C_SOMEONE_LOGIN: 2489 | do_someone_login(msg); 2490 | break; 2491 | case S2C_SOMEONE_LOGOUT: 2492 | do_someone_logout(msg); 2493 | break; 2494 | case S2C_ONLINE_USER: 2495 | do_getlist(sock); 2496 | break; 2497 | case C2C_CHAT: 2498 | do_chat(msg); 2499 | break; 2500 | defalut: 2501 | break; 2502 | } 2503 | } 2504 | if(FD_ISSET(STDIN_FILENO, &rset)) { 2505 | char cmdline[100] = {0}; 2506 | if(fgets(cmdline, sizeof(cmdline), stdin) == NULL) 2507 | break; 2508 | 2509 | if(cmdline[0] == '\n') 2510 | continue; 2511 | cmdline[strlen(cmdline) - 1] = '\0'; 2512 | parse_cmd(cmdline, sock, &servaddr); 2513 | } 2514 | } 2515 | 2516 | memset(&msg, 0, sizeof(msg)); 2517 | msg.cmd = htonl(C2S_LOGOUT); 2518 | strcpy(msg.body, username); 2519 | 2520 | sendto(sock, (const char*)&msg, sizeof(msg), 0, (struct sockaddr*)&servaddr, sizeof(servaddr)); 2521 | close(sock); 2522 | } 2523 | 2524 | int main() { 2525 | int sock; 2526 | if((sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0) 2527 | ERR_EXIT("socket"); 2528 | 2529 | chat_cli(sock); 2530 | 2531 | return 0; 2532 | } 2533 | ``` 2534 | 2535 | ## 多线程 2536 | 2537 | **thread.cpp** 2538 | 2539 | **编译语句为:g++ thread.cpp -o thread -lpthread** 2540 | 2541 | ```c++ 2542 | #include 2543 | #include 2544 | #include 2545 | #include 2546 | #include 2547 | #include 2548 | #include 2549 | 2550 | #define ERR_EXIT(m) \ 2551 | do { \ 2552 | perror(m); \ 2553 | exit(EXIT_FAILURE); \ 2554 | } while(0) 2555 | 2556 | void* thread_routine(void *arg) { 2557 | for(int i = 0; i < 20; ++i) { 2558 | printf("B"); 2559 | fflush(stdout); 2560 | usleep(20); 2561 | if(i == 3) 2562 | pthread_exit(const_cast("ABC")); 2563 | } 2564 | 2565 | return 0; 2566 | } 2567 | 2568 | int main() { 2569 | pthread_t tid; 2570 | int ret; 2571 | if((ret = pthread_create(&tid, NULL, thread_routine, NULL)) != 0) { 2572 | fprintf(stderr, "pthread_create:%s\n", strerror(ret)); 2573 | exit(EXIT_FAILURE); 2574 | } 2575 | 2576 | for(int i = 0; i < 20; ++i) { 2577 | printf("A"); 2578 | fflush(stdout); 2579 | usleep(20); 2580 | } 2581 | 2582 | void *value; 2583 | if((ret = pthread_join(tid, &value) != 0)) {//等待其他线程完成 2584 | fprintf(stderr, "pthread_join:%s\n", strerror(ret)); 2585 | exit(EXIT_FAILURE); 2586 | } 2587 | 2588 | printf("\nreturn msg = %s\n", (char*)value); 2589 | 2590 | return 0; 2591 | } 2592 | ``` 2593 | 2594 | ### 多线程下的回射客户/服务器模型 2595 | 2596 | **echosrv.cpp** 2597 | 2598 | ```c++ 2599 | #include 2600 | #include 2601 | #include 2602 | #include 2603 | #include 2604 | #include 2605 | #include 2606 | #include 2607 | #include 2608 | #include 2609 | #include 2610 | 2611 | #define ERR_EXIT(m) \ 2612 | do { \ 2613 | perror(m); \ 2614 | exit(EXIT_FAILURE); \ 2615 | } while(0) 2616 | 2617 | void do_service(int conn) { 2618 | //接收 2619 | char recvbuf[1024]; 2620 | while(1) { 2621 | memset(recvbuf, 0, sizeof(recvbuf)); 2622 | int ret = read(conn, recvbuf, sizeof(recvbuf)); 2623 | if(ret == 0) { 2624 | printf("client close\n"); 2625 | break; 2626 | } 2627 | else if(ret == -1) 2628 | ERR_EXIT("read"); 2629 | fputs(recvbuf, stdout); 2630 | write(conn, recvbuf, ret); 2631 | } 2632 | } 2633 | 2634 | void* thread_routine(void* arg) { 2635 | pthread_detach(pthread_self()); 2636 | int conn = (int)arg; 2637 | do_service(conn); 2638 | printf("exiting thread……\n"); 2639 | return NULL; 2640 | } 2641 | 2642 | int main() { 2643 | //创建套接字 2644 | int listenfd; 2645 | if((listenfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) 2646 | ERR_EXIT("socket"); 2647 | 2648 | //地址初始化 2649 | struct sockaddr_in servaddr; //IPv4地址结构 2650 | memset(&servaddr, 0, sizeof(servaddr)); 2651 | servaddr.sin_family = AF_INET; 2652 | servaddr.sin_port = htons(5188); 2653 | servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//绑定本机的任意地址 2654 | 2655 | int on = 1; 2656 | if(setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) 2657 | ERR_EXIT("setsockopt"); 2658 | 2659 | //绑定 2660 | if(bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) 2661 | ERR_EXIT("bind"); 2662 | 2663 | //监听 2664 | if(listen(listenfd, SOMAXCONN) < 0) 2665 | ERR_EXIT("listen"); 2666 | 2667 | //连接 2668 | struct sockaddr_in peeraddr; 2669 | socklen_t peerlen = sizeof(peeraddr); 2670 | int conn; 2671 | 2672 | while(1) { 2673 | if((conn = accept(listenfd, (struct sockaddr*)&peeraddr, &peerlen)) < 0) 2674 | ERR_EXIT("accept"); 2675 | printf("ip=%s port=%d\n", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port)); 2676 | 2677 | pthread_t tid; 2678 | int ret; 2679 | int *p = malloc(sizeof(int)); 2680 | *p = conn; 2681 | if((ret = pthread_create(&tid, NULL, thread_routine, p)) != 0) { 2682 | fprintf(stderr, "pthread_create:%s\n", strerror(ret)); 2683 | exit(EXIT_FAILURE); 2684 | } 2685 | } 2686 | 2687 | return 0; 2688 | } 2689 | ``` 2690 | 2691 | **echocli.cpp** 2692 | 2693 | ```c++ 2694 | #include 2695 | #include 2696 | #include 2697 | #include 2698 | #include 2699 | #include 2700 | #include 2701 | #include 2702 | #include 2703 | 2704 | #define ERR_EXIT(m) \ 2705 | do { \ 2706 | perror(m); \ 2707 | exit(EXIT_FAILURE); \ 2708 | } while(0) 2709 | 2710 | int main() { 2711 | //创建套接字 2712 | int sock; 2713 | if((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) 2714 | ERR_EXIT("socket"); 2715 | 2716 | //地址初始化 2717 | struct sockaddr_in servaddr; //IPv4地址结构 2718 | memset(&servaddr, 0, sizeof(servaddr)); 2719 | servaddr.sin_family = AF_INET; 2720 | servaddr.sin_port = htons(5188); 2721 | servaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); 2722 | 2723 | if(connect(sock, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) 2724 | ERR_EXIT("connect"); 2725 | 2726 | char sendbuf[1024] = {0}; 2727 | char recvbuf[1024] = {0}; 2728 | while(fgets(sendbuf, sizeof(sendbuf), stdin) != NULL) { 2729 | write(sock, sendbuf, strlen(sendbuf)); 2730 | read(sock, recvbuf, sizeof(recvbuf)); 2731 | fputs(recvbuf, stdout); 2732 | memset(sendbuf, 0, sizeof(sendbuf)); 2733 | memset(recvbuf, 0, sizeof(recvbuf)); 2734 | } 2735 | 2736 | close(sock); 2737 | 2738 | return 0; 2739 | } 2740 | ``` 2741 | 2742 | ### 信号量+互斥锁实现生产者-消费者模型 2743 | 2744 | **pctest.cpp** 2745 | 2746 | ```c++ 2747 | #include 2748 | #include 2749 | #include 2750 | #include 2751 | #include 2752 | #include 2753 | #include 2754 | #include 2755 | 2756 | #define ERR_EXIT(m) \ 2757 | do { \ 2758 | perror(m); \ 2759 | exit(EXIT_FAILURE); \ 2760 | } while(0) 2761 | 2762 | #define CONSUMERS_COUNT 1 2763 | #define PRODUCERS_COUNT 5 2764 | #define BUFFSIZE 10 2765 | 2766 | int g_buffer[BUFFSIZE]; 2767 | 2768 | unsigned short in = 0;//从0号位置开始存放 2769 | unsigned short out = 0;//从0号位置开始取走 2770 | unsigned short produce_id = 0; 2771 | unsigned short consumer_id = 0; 2772 | 2773 | sem_t g_sem_full; 2774 | sem_t g_sem_empty; 2775 | pthread_mutex_t g_mutex; 2776 | 2777 | pthread_t g_thread[CONSUMERS_COUNT + PRODUCERS_COUNT]; 2778 | 2779 | void* consume(void *arg) { 2780 | int num = (long)arg; 2781 | int i; 2782 | while(1) { 2783 | printf("%d wait buffer not empty\n", num); 2784 | sem_wait(&g_sem_empty); 2785 | pthread_mutex_lock(&g_mutex); 2786 | for(i = 0; i < BUFFSIZE; ++i) { 2787 | printf("%02d ", i); 2788 | if(g_buffer[i] == -1) 2789 | printf("%s", "null"); 2790 | else 2791 | printf("%d", g_buffer[i]); 2792 | if(i == out) 2793 | printf("\t<--consume"); 2794 | printf("\n"); 2795 | } 2796 | int consume_id = g_buffer[out]; 2797 | printf("%d begin consume product %d\n", num, consume_id); 2798 | g_buffer[out] = -1; 2799 | out = (out + 1) % BUFFSIZE; 2800 | printf("%d end consume product %d\n", num, consume_id); 2801 | pthread_mutex_unlock(&g_mutex); 2802 | sem_post(&g_sem_full); 2803 | sleep(5); 2804 | } 2805 | 2806 | return NULL; 2807 | } 2808 | 2809 | void* produce(void *arg) { 2810 | int num = (long)arg; 2811 | int i; 2812 | while(1) { 2813 | printf("%d wait buffer not full\n", num); 2814 | sem_wait(&g_sem_full); 2815 | pthread_mutex_lock(&g_mutex); 2816 | for(i = 0; i < BUFFSIZE; ++i) { 2817 | printf("%02d ", i); 2818 | if(g_buffer[i] == -1) 2819 | printf("%s", "null"); 2820 | else 2821 | printf("%d", g_buffer[i]); 2822 | if(i == in) 2823 | printf("\t<--produce"); 2824 | printf("\n"); 2825 | } 2826 | 2827 | printf("%d begin produce product %d\n", num, produce_id); 2828 | g_buffer[in] = produce_id; 2829 | in = (in + 1) % BUFFSIZE; 2830 | printf("%d end produce product %d\n", num, produce_id++); 2831 | pthread_mutex_unlock(&g_mutex); 2832 | sem_post(&g_sem_empty); 2833 | sleep(1); 2834 | } 2835 | 2836 | return NULL; 2837 | } 2838 | 2839 | int main() { 2840 | int i; 2841 | for(i = 0; i < BUFFSIZE; ++i) 2842 | g_buffer[i] = -1; 2843 | sem_init(&g_sem_full, 0, BUFFSIZE); 2844 | sem_init(&g_sem_empty, 0, 0); 2845 | 2846 | pthread_mutex_init(&g_mutex, NULL); 2847 | 2848 | for(i = 0; i < CONSUMERS_COUNT; ++i) 2849 | pthread_create(&g_thread[i], NULL, consume, (void*)(long)i); 2850 | 2851 | for(i = 0; i < PRODUCERS_COUNT; ++i) 2852 | pthread_create(&g_thread[CONSUMERS_COUNT + i], NULL, produce, (void*)(long)i); 2853 | 2854 | for(i = 0; i < CONSUMERS_COUNT + PRODUCERS_COUNT; ++i) 2855 | pthread_join(g_thread[i], NULL); 2856 | 2857 | sem_destroy(&g_sem_full); 2858 | sem_destroy(&g_sem_empty); 2859 | pthread_mutex_destroy(&g_mutex); 2860 | 2861 | return 0; 2862 | } 2863 | ``` 2864 | 2865 | ### 条件变量+互斥锁实现生产者-消费者模型 2866 | 2867 | **pctest.cpp** 2868 | 2869 | ```c++ 2870 | #include 2871 | #include 2872 | #include 2873 | #include 2874 | #include 2875 | #include 2876 | #include 2877 | #include 2878 | 2879 | 2880 | #define ERR_EXIT(m) \ 2881 | do { \ 2882 | perror(m); \ 2883 | exit(EXIT_FAILURE); \ 2884 | } while(0) 2885 | 2886 | #define CONSUMERS_COUNT 2 2887 | #define PRODUCERS_COUNT 1 2888 | 2889 | pthread_mutex_t g_mutex; 2890 | pthread_cond_t g_cond; 2891 | 2892 | pthread_t g_thread[CONSUMERS_COUNT + PRODUCERS_COUNT]; 2893 | 2894 | int nready = 0; 2895 | 2896 | void* consume(void *arg) { 2897 | int num = (long)arg; 2898 | while(1) { 2899 | pthread_mutex_lock(&g_mutex); 2900 | while(nready == 0) {//条件不满足则等待 2901 | //解锁,等待其他线程改变nready的值 2902 | printf("consumer %d begin wait a condition...\n", num); 2903 | pthread_cond_wait(&g_cond, &g_mutex); 2904 | } 2905 | printf("consumer %d end wait a condition...\n", num); 2906 | printf("consumer %d begin consume product...\n", num); 2907 | --nready; 2908 | pthread_mutex_unlock(&g_mutex); 2909 | sleep(1); 2910 | } 2911 | 2912 | return NULL; 2913 | } 2914 | 2915 | void* produce(void *arg) { 2916 | int num = (long)arg; 2917 | 2918 | while(1) { 2919 | pthread_mutex_lock(&g_mutex); 2920 | printf("producer %d begin produce product...\n", num); 2921 | ++nready; 2922 | printf("producer %d end produce product...\n", num); 2923 | printf("producer %d signal...\n", num); 2924 | pthread_cond_signal(&g_cond); 2925 | pthread_mutex_unlock(&g_mutex); 2926 | sleep(5); 2927 | } 2928 | 2929 | return NULL; 2930 | } 2931 | 2932 | int main() { 2933 | int i; 2934 | 2935 | pthread_mutex_init(&g_mutex, NULL); 2936 | pthread_cond_init(&g_cond, NULL); 2937 | 2938 | 2939 | for(i = 0; i < CONSUMERS_COUNT; ++i) 2940 | pthread_create(&g_thread[i], NULL, consume, (void*)(long)i); 2941 | 2942 | sleep(1); 2943 | 2944 | for(i = 0; i < PRODUCERS_COUNT; ++i) 2945 | pthread_create(&g_thread[CONSUMERS_COUNT + i], NULL, produce, (void*)(long)i); 2946 | 2947 | for(i = 0; i < CONSUMERS_COUNT + PRODUCERS_COUNT; ++i) 2948 | pthread_join(g_thread[i], NULL); 2949 | 2950 | pthread_mutex_destroy(&g_mutex); 2951 | pthread_cond_destroy(&g_cond); 2952 | 2953 | return 0; 2954 | } 2955 | ``` 2956 | 2957 | ## 简单的线程池实现 2958 | 2959 | **condition.h** 2960 | 2961 | ```c++ 2962 | #ifndef _CONDITION_H_ 2963 | #define _CONDITION_H_ 2964 | 2965 | #include 2966 | 2967 | typedef struct condition { 2968 | pthread_mutex_t pmutex; 2969 | pthread_cond_t pcond; 2970 | } condition_t; 2971 | 2972 | int condition_init(condition_t *cond); 2973 | int condition_lock(condition_t *cond); 2974 | int condition_unlock(condition_t *cond); 2975 | int condition_wait(condition_t *cond); 2976 | int condition_timedwait(condition_t *cond, const struct timespec *abstime); 2977 | int condition_signal(condition_t *cond); 2978 | int condition_broadcast(condition_t *cond); 2979 | int condition_destroy(condition_t *cond); 2980 | 2981 | #endif /* _CONDITION_H_ */ 2982 | ``` 2983 | 2984 | **condition.cpp** 2985 | 2986 | ```c++ 2987 | #include "condition.h" 2988 | 2989 | int condition_init(condition_t *cond) { 2990 | int status; 2991 | if((status = pthread_mutex_init(&cond->pmutex, NULL))) 2992 | return status; 2993 | 2994 | if((status = pthread_cond_init(&cond->pcond, NULL))) 2995 | return status; 2996 | 2997 | return 0; 2998 | } 2999 | 3000 | int condition_lock(condition_t *cond) { 3001 | return pthread_mutex_lock(&cond->pmutex); 3002 | } 3003 | 3004 | int condition_unlock(condition_t *cond) { 3005 | return pthread_mutex_unlock(&cond->pmutex); 3006 | } 3007 | 3008 | int condition_wait(condition_t *cond) { 3009 | return pthread_cond_wait(&cond->pcond, &cond->pmutex); 3010 | } 3011 | 3012 | int condition_timedwait(condition_t *cond, const struct timespec *abstime) { 3013 | return pthread_cond_timedwait(&cond->pcond, &cond->pmutex, abstime); 3014 | } 3015 | 3016 | int condition_signal(condition_t *cond) { 3017 | return pthread_cond_signal(&cond->pcond); 3018 | } 3019 | 3020 | int condition_broadcast(condition_t *cond) { 3021 | return pthread_cond_broadcast(&cond->pcond); 3022 | } 3023 | int condition_destroy(condition_t *cond) { 3024 | int status; 3025 | if((status = pthread_mutex_destroy(&cond->pmutex))) 3026 | return status; 3027 | 3028 | if((status = pthread_cond_destroy(&cond->pcond))) 3029 | return status; 3030 | 3031 | return 0; 3032 | } 3033 | ``` 3034 | 3035 | **threadpool.h** 3036 | 3037 | ```c++ 3038 | #ifndef _THREAD_POOL_H_ 3039 | #define _THREAD_POOL_H_ 3040 | 3041 | #include "condition.h" 3042 | 3043 | typedef struct task { 3044 | void *(*run)(void *arg); //任务回调函数 3045 | void *arg; //回调函数参数 3046 | struct task *next; 3047 | } task_t; 3048 | 3049 | typedef struct threadpool { 3050 | condition_t ready; //任务准备就绪或者线程池销毁通知 3051 | task_t *first; //任务队列头指针 3052 | task_t *last; //任务队列尾指针 3053 | int counter; //线程池中当前线程数 3054 | int idle; //线程池中当前正在等待任务的线程数 3055 | int max_treads; //线程池中最大允许的线程数 3056 | int quit; //销毁线程池的时候置1 3057 | } threadpool_t; 3058 | 3059 | //初始化线程池 3060 | void threadpool_init(threadpool_t *pool, int threads); 3061 | //往线程池中添加任务 3062 | void threadpool_add_task(threadpool_t *pool, void *(*run)(void *arg), void *arg); 3063 | //销毁线程池 3064 | void threadpool_destroy(threadpool_t *pool); 3065 | 3066 | #endif /* _THREAD_POOL_H_ */ 3067 | ``` 3068 | 3069 | **threadpool.cpp** 3070 | 3071 | ```c++ 3072 | #include "threadpool.h" 3073 | #include 3074 | #include 3075 | #include 3076 | #include 3077 | #include 3078 | 3079 | void *thread_routine(void *arg) { 3080 | struct timespec abstime; 3081 | int timeout; 3082 | printf("thread 0x%x is starting\n", (int)pthread_self()); 3083 | threadpool_t *pool = (threadpool_t *)arg; 3084 | while(1) { 3085 | timeout = 0; 3086 | condition_lock(&pool->ready); 3087 | pool->idle++; 3088 | //等待队列有任务到来或者线程池销毁通知 3089 | while(pool->first == NULL && !pool->quit) { 3090 | printf("thread 0x%x is waiting\n", (int)pthread_self()); 3091 | clock_gettime(CLOCK_REALTIME, &abstime); 3092 | abstime.tv_sec += 2; 3093 | int status = condition_timedwait(&pool->ready, &abstime); 3094 | if(status == ETIMEDOUT) { 3095 | printf("thread 0x%x is wait timed out\n", (int)pthread_self()); 3096 | timeout = 1; 3097 | break; 3098 | } 3099 | } 3100 | 3101 | //等待到条件,处于工作状态 3102 | pool->idle--; 3103 | 3104 | //等待到任务 3105 | if(pool->first != NULL) { 3106 | //从队头取出任务 3107 | task_t *t = pool->first; 3108 | pool->first = t->next; 3109 | //执行任务需要一定时间,所以要先解锁,以便生产者进程 3110 | //能够往队列中添加新任务,其他消费者进程能够进入等待任务 3111 | condition_unlock(&pool->ready); 3112 | t->run(t->arg); 3113 | free(t); 3114 | condition_lock(&pool->ready); 3115 | } 3116 | //如果等待到线程池销毁通知,且任务都执行完毕 3117 | if(pool->quit && pool->first == NULL) { 3118 | pool->counter--; 3119 | if(pool->counter == 0) 3120 | condition_signal(&pool->ready); 3121 | //跳出循环之前要基带解锁 3122 | condition_unlock(&pool->ready); 3123 | break; 3124 | } 3125 | 3126 | if(timeout && pool->first == NULL) { 3127 | pool->counter--; 3128 | //跳出循环之前要基带解锁 3129 | condition_unlock(&pool->ready); 3130 | break; 3131 | } 3132 | condition_unlock(&pool->ready); 3133 | } 3134 | 3135 | printf("thread 0x%x is exiting\n", (int)pthread_self()); 3136 | return NULL; 3137 | } 3138 | 3139 | //初始化线程池 3140 | void threadpool_init(threadpool_t *pool, int threads) { 3141 | //对线程池中的各个字段初始化 3142 | condition_init(&pool->ready); 3143 | pool->first = NULL; 3144 | pool->last = NULL; 3145 | pool->counter = 0; 3146 | pool->idle = 0; 3147 | pool->max_treads = threads; 3148 | pool->quit = 0; 3149 | } 3150 | 3151 | //往线程池中添加任务 3152 | void threadpool_add_task(threadpool_t *pool, void *(*run)(void *arg), void *arg) { 3153 | //生成新任务 3154 | task_t *newtask = (task_t *)malloc(sizeof(task_t)); 3155 | newtask->run = run; 3156 | newtask->arg = arg; 3157 | newtask->next = NULL; 3158 | 3159 | condition_lock(&pool->ready); 3160 | //将任务添加到队列 3161 | if(pool->first == NULL) 3162 | pool->first = newtask; 3163 | else 3164 | pool->last->next = newtask; 3165 | pool->last = newtask; 3166 | 3167 | //如果有等待线程,则唤醒其中一个 3168 | if(pool->idle > 0) { 3169 | condition_signal(&pool->ready); 3170 | } 3171 | else if(pool->counter < pool->max_treads) { 3172 | //没有等待线程,并且当前线程数不超过最大线程数,则创建一个新线程 3173 | pthread_t tid; 3174 | pthread_create(&tid, NULL, thread_routine, pool); 3175 | pool->counter++; 3176 | } 3177 | 3178 | condition_unlock(&pool->ready); 3179 | } 3180 | 3181 | //销毁线程池 3182 | void threadpool_destroy(threadpool_t *pool) { 3183 | if(pool->quit) 3184 | return; 3185 | 3186 | condition_lock(&pool->ready); 3187 | pool->quit = 1; 3188 | if(pool->counter > 0) { 3189 | if(pool->idle > 0) 3190 | condition_broadcast(&pool->ready); 3191 | 3192 | //处于执行任务状态中的线程,不会收到广播 3193 | //线程池需要等待执行任务状态中的线程全部退出 3194 | while(pool->counter > 0) 3195 | condition_wait(&pool->ready); 3196 | } 3197 | condition_unlock(&pool->ready); 3198 | condition_destroy(&pool->ready); 3199 | } 3200 | ``` 3201 | 3202 | **main.cpp** 3203 | 3204 | ```c++ 3205 | #include "threadpool.h" 3206 | #include 3207 | #include 3208 | #include 3209 | 3210 | void* mytask(void *arg) { 3211 | printf("thread 0x%x is working on task %d\n", (int)pthread_self(), *(int*)arg); 3212 | sleep(1); 3213 | free(arg); 3214 | return NULL; 3215 | } 3216 | 3217 | int main() { 3218 | threadpool_t pool; 3219 | threadpool_init(&pool, 3); 3220 | 3221 | int i; 3222 | for(i = 0; i < 10; ++i) { 3223 | int *arg = (int*)malloc(sizeof(int)); 3224 | *arg = i; 3225 | threadpool_add_task(&pool, mytask, arg); 3226 | } 3227 | 3228 | threadpool_destroy(&pool); 3229 | return 0; 3230 | } 3231 | ``` 3232 | 3233 | **Makefile** 3234 | 3235 | ```c++ 3236 | OBJS = main.o threadpool.o condition.o 3237 | main: $(OBJS) 3238 | g++ $(OBJS) -o main -lpthread -lrt 3239 | main.o: main.cpp threadpool.h condition.h 3240 | g++ -c main.cpp -o main.o 3241 | threadpool.o: threadpool.cpp threadpool.h condition.h 3242 | g++ -c threadpool.cpp -o threadpool.o 3243 | condition.o: condition.cpp condition.h 3244 | g++ -c condition.cpp -o condition.o 3245 | clean: 3246 | rm -rf *.o main 3247 | ``` 3248 | 3249 | 3250 | 3251 | --------------------------------------------------------------------------------