Chinaunix首页 | 论坛 | 博客
  • 博客访问: 535704
  • 博文数量: 142
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 1452
  • 用 户 组: 普通用户
  • 注册时间: 2013-09-12 16:28
文章分类

全部博文(142)

文章存档

2016年(10)

2015年(60)

2014年(72)

我的朋友

分类: C/C++

2014-09-26 15:17:43


一、正常启动
1.启动服务器
./tcpserver
gwwu@hz-dev2.wgw.com:~/test/socket>netstat -a -n -t
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address               Foreign Address             State           
tcp        0      0 127.0.0.1:10000             0.0.0.0:*                   LISTEN    
服务器启动后,经过socket,bind,listen,阻塞于accept,等待客户端的连接。
gwwu@hz-dev2.wgw.com:~/test/socket>netstat -a -n -t
Active Internet connections (servers and established)
Proto Recv-Q Send-Q     Local Address               Foreign Address             State           
tcp        0          0        127.0.0.1:10000             0.0.0.0:*                   LISTEN    

2.启动客户端
./tcpclient
客户端调用socket,connect,connect引发三次握手。当三次握手结束,客户端的connect和服务器的accept均返回,连接建立。
(1)客户端调用str_cli阻塞于fgets
(2)服务器父进程阻塞于accept,等待新的客户来连接
(3)服务器子进程调用str_echo阻塞于read。

gwwu@hz-dev2.wgw.com:~/test/socket>netstat -a -n -t
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address               Foreign Address             State       
tcp        0        0        127.0.0.1:10000             0.0.0.0:*                   LISTEN      
tcp        0        0        127.0.0.1:10000             127.0.0.1:49277        ESTABLISHED 
tcp        0        0        127.0.0.1:49277             127.0.0.1:10000        ESTABLISHED 

使用ps命令来查看进程的状态和关系
gwwu@hz-dev2.wgw.com:~/test/socket>ps -t pts/12 -o pid,ppid,tty,stat,args,wchan 
  PID  PPID TT       STAT COMMAND                     WCHAN
17561 23965 pts/12   S+   ./tcpclient                 n_tty_read
23965 23957 pts/12   Ss   -bash                       wait
gwwu@hz-dev2.wgw.com:~/test/socket>ps -t pts/9 -o pid,ppid,tty,stat,args,wchan  
  PID  PPID TT       STAT COMMAND                     WCHAN
15339 15338 pts/9    Ss   -bash                       wait
17546 15339 pts/9    S+   ./tcpserver                 inet_csk_accept-----------服务器父进程
17562 17546 pts/9    S+   ./tcpserver                 sk_wait_data-----------服务器子进程

二、正常终止
连接建立以后,客户端输入啥,立即回射该内容
当在客户端输入CTRL+D(终端的EOF字符)时:
(1)客户端str_cli函数中的fgets返回NULL,str_cli函数退出,最终客户端退出,客户端进程退出,会close打开的socket描述符,这会导致客户端发送FIN报文给服务器。
(2)服务器收到客户端的FIN报文,read函数返回0,导致str_echo函数退出,最终服务端的子进程退出,关闭对应的socket套接字,发送FIN报文给客户端,并发送SIGCHLD信号给父进程,如果父进程没有响应该信号,则该子进程编程僵死进程。
gwwu@hz-dev2.wgw.com:~/test/socket>ps -t pts/9 -o pid,ppid,tty,stat,args,wchan
  PID  PPID TT       STAT COMMAND                     WCHAN
15339 15338 pts/9    Ss   -bash                       wait
17546 15339 pts/9    S+   ./tcpserver                 inet_csk_accept
17562 17546 pts/9    Z+   [tcpserver]       exit --------------------------------此为僵死进程

(3)客户端响应FIN报文,发送ACK,进入TIME_WAIT状态

tcpserver.c

点击(此处)折叠或打开

  1. #include <unistd.h>
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <signal.h>
  5. #include <sys/socket.h>
  6. #include <sys/types.h>
  7. #include <sys/wait.h>
  8. #include <errno.h>
  9. #include <netinet/in.h>
  10. #include <arpa/inet.h>
  11. #include <sys/wait.h>
  12. #include <strings.h>
  13. #include "readn.h"

  14. /*read函数从套接字读入数据,writen函数把其中的内容回射给客户。如果客户关闭连接,那么接收到客户的FIN将导致服务器子进程的read函数返回0,这又导致str_echo函数的返回,从而终止了子进程*/
  15. void str_echo(int fd)
  16. {
  17.     int n;
  18.     char recvline[MAXLINE];
  19. again:
  20.     while((n = read(fd,recvline,MAXLINE))>0) {
  21.         writen(fd,recvline,n);
  22.     }
  23.     if(n < 0 && errno == EINTR) {
  24.         goto again;
  25.     }else if (n < 0) {
  26.         printf("read error\n");
  27.     }
  28. }

  29. int main(int argc,char *argv[])
  30. {
  31.     int ret = 0;
  32.     int listen_fd;
  33.     int conn_fd;
  34.     struct sockaddr_in server_addr;
  35.     struct sockaddr_in client_addr;
  36.     pid_t pid;
  37.     pid_t child_pid;
  38.     int status;
  39.     socklen_t len;

  40.     listen_fd = socket(AF_INET,SOCK_STREAM,0);

  41.     if(listen_fd < 0) {
  42.         printf("socket error\n");
  43.         return -1;
  44.     }

  45.     bzero(&server_addr,sizeof(struct sockaddr_in));

  46.     server_addr.sin_family = AF_INET;
  47.     server_addr.sin_port = htons(10000);
  48.     //server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
  49.     ret = inet_pton(AF_INET,"127.0.0.1",&server_addr.sin_addr.s_addr);
  50.     if(ret < 0) {
  51.         printf("inet_pton error\n");
  52.         close(listen_fd);
  53.         return ret;
  54.     }else if(ret == 0) {
  55.         printf("the second parameter of inet_addr is null\n");
  56.     }

  57.     if(bind(listen_fd,(struct sockaddr*)&server_addr,sizeof(struct sockaddr)) == -1) {
  58.             printf("bind error\n");
  59.             close(listen_fd);
  60.             return -1;
  61.     }
  62.     

  63.     if(listen(listen_fd,1024) == -1) {
  64.         printf("listen error\n");
  65.         close(listen_fd);
  66.         return -1;
  67.     }

  68.     for(;;) {
  69.         len = sizeof(client_addr);
  70.         conn_fd = accept(listen_fd,(struct sockaddr*)&client_addr,&len);
  71.         if(conn_fd < 0) {
  72.             printf("accept error,errno=%d\n",errno);
  73.             if(errno == EINTR)
  74.                 continue;
  75.             return -1;
  76.         }

  77.         pid = fork();

  78.         if(pid < 0) {
  79.             printf("fork error\n");
  80.             return -1;
  81.         }else if(pid == 0) {
  82.             close(listen_fd);
  83.             /*TODO::handle on conn_fd*/
  84.             str_echo(conn_fd);
  85.             exit(0);
  86.         }
  87.         close(conn_fd);
  88.         child_pid = waitpid(pid,&status,WNOHANG);
  89.         if(child_pid < 0)
  90.             printf("wait error\n");
  91.     }

  92.     return 0;
  93. }


tcpclient.c

点击(此处)折叠或打开

  1. #include <unistd.h>
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <sys/socket.h>
  5. #include <string.h>
  6. #include <sys/types.h>
  7. #include <arpa/inet.h>
  8. #include "readn.h"

  9. void str_cli(FILE *fp,int fd)
  10. {
  11.     char sendline[1024], recvline[1024];

  12.     while(fgets(sendline,1024,fp)) {
  13.         writen(fd,sendline,strlen(sendline));
  14.         memset(recvline,0,sizeof(recvline));
  15.         readline(fd,recvline,sizeof(recvline));
  16.         fputs(recvline,stdout);
  17.     }
  18. }

  19. int main(int argc,char* argv[])
  20. {
  21.     int fd;
  22.     struct sockaddr_in server_addr;
  23.     int ret = 0;


  24.     fd = socket(AF_INET,SOCK_STREAM,0);

  25.     if(fd < 0) {
  26.         printf("socket error\n");
  27.         return -1;
  28.     }

  29.     bzero(&server_addr,sizeof(struct sockaddr_in));

  30.     server_addr.sin_family = AF_INET;
  31.     ret = inet_pton(AF_INET,"127.0.0.1",&server_addr.sin_addr);

  32.     if(ret < 0) {
  33.         printf("inet_pton error\n");
  34.         return -1;
  35.     }else if(ret == 0) {
  36.         printf("the second parameter of inet_pton is NULL\n");
  37.     }
  38.     server_addr.sin_port = htons(10000);

  39.     ret = connect(fd,(struct sockaddr*)&server_addr,sizeof(struct sockaddr));
  40.     if(ret < 0) {
  41.         printf("connect error\n");
  42.         return -1;
  43.     }

  44.     str_cli(stdin,fd);

  45.     return 0;
  46. }

readn.h

点击(此处)折叠或打开

  1. #ifndef __READN_H__
  2. #define __READN_H__
  3. #define MAXLINE 1024
  4. int readn(int fd, void* buffer, size_t n);
  5. int writen(int fd, void* buffer, size_t n);
  6. size_t readline(int fd, void * buffer, size_t maxlen);
  7. #endif
readn.c

点击(此处)折叠或打开

  1. #include <unistd.h>
  2. #include <signal.h>
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. #include <errno.h>
  6. #include "readn.h"

  7. int readn(int fd, void* buffer, size_t n)
  8. {
  9.     int nread;
  10.     int nleft;
  11.     char *ptr;

  12.     ptr = buffer;
  13.     nleft = n;
  14.     while(nleft >0) {
  15.         if((nread=read(fd,ptr,nleft)) < 0){
  16.             if(errno == EINTR) {
  17.                 nread = 0;
  18.                 continue;
  19.             }else {
  20.                 return -1;
  21.             }
  22.         }else if (nread==0) {
  23.             break;
  24.         }
  25.         ptr += nread;
  26.         nleft -= nread;
  27.     }
  28.     return (n - nleft);
  29. }

  30. int writen(int fd, void* buffer, size_t n)
  31. {
  32.     int nwrite;
  33.     int nleft;
  34.     char* ptr;

  35.     ptr = buffer;
  36.     nleft = n;
  37.     while(nleft > 0) {
  38.         if((nwrite = write(fd,ptr,nleft)) < 0) {
  39.             if(errno == EINTR) {
  40.                 nwrite = 0;
  41.                 continue;
  42.             }else {
  43.                 return -1;
  44.             }
  45.         }else if(nwrite == 0){
  46.             break;
  47.         }
  48.         ptr += nwrite;
  49.         nleft -= nwrite;
  50.     }
  51.     return (n-nleft);
  52. }
  53. static char *ptr;
  54. static int len = 0;
  55. char read_buffer[MAXLINE];
  56. ssize_t my_read(int fd, char* buffer)
  57. {
  58.     int nread;

  59.     if(len <= 0) {
  60. again:
  61.         if((nread = read(fd,read_buffer,MAXLINE)) < 0) {
  62.             if(errno == EINTR)
  63.                 goto again;
  64.             return -1;
  65.         }else if(nread == 0) {
  66.             return 0;
  67.         }
  68.         ptr = read_buffer;
  69.         len = nread;
  70.     }
  71.     *buffer = *ptr++;
  72.     len--;
  73.     return 1;
  74. }

  75. size_t readline(int fd, void * buffer, size_t maxlen)
  76. {
  77.     int i;
  78.     int nread;
  79.     char c;
  80.     char *ptr;

  81.     ptr=buffer;
  82.     for(i = 0;i < maxlen;i++) {
  83. again:
  84.         if((nread = my_read(fd,&c)) == 1){
  85.             *ptr++ = c;
  86.             if(c == '\n')
  87.                 break;
  88.         }else if(nread == 0) {
  89.             *ptr = 0;
  90.             return i-1;
  91.         }else {
  92.             if(errno == EINTR) {
  93.                 goto again;
  94.             }else {
  95.                 return -1;
  96.             }
  97.         }
  98.     }
  99.     *ptr = 0;
  100.     return i;
  101. }

编译运行:
gcc -g readn.c tcpclient.c -o tcpclient -Wall
gcc -g readn.c tcpserver.c -o tcpserver -Wall

SERVER端:
./tcpserver
client端:
./tcpclient
abc
abc


阅读(855) | 评论(0) | 转发(0) |
0

上一篇:fgets & fputs

下一篇:linux SIGPIPE信号

给主人留下些什么吧!~~