unix网络编程代码(2)

继续贴《unix网络编程》上的示例代码。这次是一个反射程序,反射是客户端讲用户输入的文本发送到服务器端,服务器端读取客户端发过来的文本消息,然后原封不动的把文本消息返回给客户端。使用tcp协议连接客户端和服务端,我已经在我的阿里云服务器上测试过了,能够完美运行。

 

首先是头文件wrap.h,在该头文件中,声明了封装部分网络编程套接字api的包裹函数,以及某些宏定义。

 1 #ifndef WRAP_H_
 2 #define WRAP_H_
 3 
 4 #include <stdio.h>
 5 #include <stdlib.h>
 6 #include <unistd.h>
 7 #include <arpa/inet.h> 
 8 #include <sys/socket.h>
 9 #include <netinet/in.h>
10 #include <sys/types.h>
11 #include <errno.h>
12 #include <string.h>
13 #include <strings.h>
14 #include <signal.h>
15 
16 #define SOCKET_ERROR  1
17 #define BIND_ERROR    2
18 #define LISTEN_ERROR  3
19 #define CONNECT_ERROR 4
20 #define ACCEPT_ERROR  5
21 #define READ_ERR0R    6
22 #define SIGNAL_ERROR  7
23 #define PROCESS_ERROR 8
24 
25 #define MAXLINE       4096
26 #define SER_PORT      9375
27 
28 /*
29  *All the function is possible in unix network programing.
30  *On success,0 is returned;
31  *or on error,number above 0 is returned;
32  */
33 int Socket (int domain,int type,int protocol);
34 int Bind (int sockfd,const struct sockaddr *addr,socklen_t addrlen);
35 int Listen (int sockfd,int backlog);
36 int Connect (int sockfd,const struct sockaddr *addr,socklen_t addrlen);
37 
38 /*read n bytes from a file descriptor*/
39 ssize_t readn (int fd,void *vptr,size_t n);
40 /*write n bytes into a file descriptor*/
41 ssize_t writen (int fd,void *vptr,size_t n);
42 /*read a line*/
43 static int read_cnt;
44 static char *read_ptr;
45 static char read_buf[MAXLINE];
46 static ssize_t my_read (int fd,char *ptr);
47 ssize_t readline (int fd,void *vptr,size_t maxlen);
48 ssize_t readlinebuf (void **vptrptr);
49 
50 /*for linux signal*/
51 typedef void Sigfunc (int);
52 Sigfunc *Signal (int signo,Sigfunc *func);
53 
54 /*handle signal SIGCHLD*/
55 void sig_chld (int signo);
56 
57 /*create child process*/
58 pid_t Fork ();
59 
60 #endif 

 

接下来是这些函数的实现wrap.c:

  1 #include "wrap.h"
  2 
  3 int Socket (int domain,int type,int protocol)
  4 {
  5     int result = 0;
  6     result = socket (domain,type,protocol);
  7     if (result == -1) {
  8         perror ("socket");
  9         exit (SOCKET_ERROR);
 10     }
 11 
 12     return result;
 13 }
 14 
 15 int Bind (int sockfd,const struct sockaddr *addr,socklen_t addrlen)
 16 {
 17     int result = 0;
 18     result = bind (sockfd,addr,addrlen);
 19     if (result == -1) {
 20         perror ("bind");
 21         exit (BIND_ERROR);
 22     }
 23 
 24     return 0;
 25 }
 26 
 27 int Listen (int sockfd,int backlog)
 28 {
 29     int result = 0;
 30     result = listen (sockfd,backlog);
 31     if (result == -1) {
 32         perror ("listen");
 33         exit (LISTEN_ERROR);
 34     }
 35 
 36     return 0;
 37 }
 38 
 39 int Connect (int sockfd,const struct sockaddr *addr,socklen_t addrlen)
 40 {
 41     int result = 0;
 42     result = connect (sockfd,addr,addrlen);
 43     if (result == -1) {
 44         perror ("connect");
 45         exit (CONNECT_ERROR);
 46     }
 47 
 48     return 0;
 49 }
 50 
 51 /*read n bytes from a file descriptor*/
 52 ssize_t readn (int fd,void *vptr,size_t n)
 53 {
 54     size_t nleft;
 55     ssize_t nread;
 56     char *ptr;
 57 
 58     ptr = vptr;
 59     nleft = n;
 60     /*read until n bytes*/
 61     while (nleft > 0) {
 62         /*read a string*/ 
 63         if ((nread = read (fd,ptr,nleft)) < 0) {
 64             if (errno == EINTR) {
 65             /*
 66              *The  call  was interrupted by a signal before any data was read
 67              *and call it again.
 68              */
 69                 nread = 0;
 70             }
 71             else {
 72             /*the call was interrupted by other signal,return -1*/
 73                 return (-1);    
 74             }
 75         } else if (nread == 0) {
 76             break; /*read a EOF from file descriptor and read all data*/
 77         }
 78 
 79         nleft = nleft - nread;
 80         ptr += nread;
 81     }
 82 
 83     return (n-nleft);
 84 }
 85 
 86 /*write n bytes into a file descriptor*/
 87 ssize_t writen (int fd,void *vptr,size_t n)
 88 {
 89     size_t nleft;
 90     ssize_t nwritten;
 91     const char *ptr;
 92 
 93     ptr = vptr;
 94     nleft = n;
 95     while (nleft > 0) {
 96         if ((nwritten = write (fd,ptr,nleft)) <= 0) {
 97             /*nothing written or an error occured*/
 98             if (nwritten < 0 && errno == EINTR) {
 99             /*
100              *The  call  was interrupted by a signal before any data was read
101              *and call it again.
102              */
103                 nwritten = 0;
104             } else {
105             /*the call was interrupted by other signal,return -1*/
106                 return (-1);
107             }
108         }
109 
110         nleft -= nwritten;
111         ptr += nwritten;
112     }
113 
114     return (n);
115 }
116 
117 static ssize_t my_read (int fd,char *ptr)
118  {
119     if (read_cnt <= 0) {
120         again:
121         if ((read_cnt = read (fd,read_buf,sizeof (read_buf))) < 0) {
122             if (errno == EINTR) {
123             /*
124              *The  call  was interrupted by a signal before any data was read
125              *and call it again.
126              */    
127                 goto again;
128             }
129             /*the call was interrupted by other signal,return -1*/
130             return (-1);
131         } else if (read_cnt == 0) {
132         /*nothing to read*/
133             return 0;
134         }
135         read_ptr = read_buf;
136     }
137 
138     read_cnt --;
139     *ptr = *read_ptr++;
140     return 1;
141 }
142 
143 ssize_t readline (int fd,void *vptr,size_t maxlen)
144 {
145     ssize_t n,rc;
146     char c,*ptr;
147 
148     ptr = vptr;
149     for (n = 1;n < maxlen;n ++) {
150         if ((rc = my_read (fd,&c)) == 1) {
151             *ptr++ = c;
152             if (c == '\n')
153                 break;    
154         } else if (rc == 0) {
155             *ptr = 0;
156             return (n - 1);
157         } else {
158             return (-1);
159         }
160     }
161 
162     *ptr = 0;
163     return n;
164 }
165 
166 ssize_t readlinebuf (void **vptrptr)
167 {
168     if (read_cnt)
169         *vptrptr = read_ptr;
170     return (read_cnt);
171 }
172 
173 /*handle SIGCHLD*/
174 void sig_chld (int signo)
175 {
176     pid_t  pid;
177     int   stat;
178 
179     while ((pid = waitpid (-1,&stat,WNOHANG)) > 0) {
180         printf ("child %d terminated\n",pid);
181     }
182 
183     return;
184 }
185 
186 /*signal handle*/
187 Sigfunc* Signal (int signo,Sigfunc *func)
188 {
189     struct sigaction act,oact;
190     act.sa_handler = func;
191     sigemptyset (&act.sa_mask);
192     act.sa_flags = 0;
193     
194     if (signo == SIGALRM)
195         act.sa_flags |= SA_RESTART;
196 
197     if (sigaction (signo,&act,&oact) < 0) {
198         exit (SIGNAL_ERROR);
199     }
200 
201     return (oact.sa_handler);
202 }
203 
204 pid_t Fork ()
205 {
206     pid_t pid;
207     pid = fork ();
208     if (pid < 0) {
209         perror ("fork");
210         exit (PROCESS_ERROR);
211     } else {
212         return pid;
213     }
214 }

 

服务端代码server.c:

 1 #include "wrap.h"
 2 
 3 #define SER_PORT      9375
 4 
 5 extern void sig_chld (int signo);
 6 void str_echo (int connfd);
 7 
 8 int main ()
 9 {
10     int                listenfd,connfd;
11     pid_t              child;
12     socklen_t          chilen;
13     struct sockaddr_in cliaddr,seraddr;
14     char ip[20];
15 
16     listenfd = Socket (AF_INET,SOCK_STREAM,0);
17     bzero (&cliaddr,sizeof (cliaddr));
18     bzero (&seraddr,sizeof (seraddr));
19 
20     seraddr.sin_family      = AF_INET;
21     seraddr.sin_port        = htons (SER_PORT);
22     seraddr.sin_addr.s_addr = htonl (INADDR_ANY);
23 
24     Bind (listenfd,(struct sockaddr*)&seraddr,sizeof (seraddr));
25     Listen (listenfd,20);
26     Signal (SIGCHLD,sig_chld);
27 
28     while (1) {
29         chilen = sizeof (cliaddr);
30         if ((connfd = accept (listenfd,
31                       (struct sockaddr*) &cliaddr,
32                       &chilen)) < 0) {
33             if (errno == EINTR) 
34                 continue;
35             else 
36                 perror ("accept");
37         } else {
38             printf ("a client connected,");
39             inet_ntop (AF_INET,&cliaddr.sin_addr,ip,chilen);
40             printf ("ip address:%s\n",ip);
41             ip[0] = '\0';
42         }
43 
44         if ((child = Fork ()) == 0) {
45             /*this is child process*/
46             close (listenfd);
47             str_echo (connfd);
48             exit (0);
49         }
50         close (connfd);
51     }
52 
53     return 0;
54 }
55 
56 void str_echo (int connfd)
57 {
58     ssize_t n;
59     char buf[MAXLINE];
60 again:
61     while ((n = read (connfd,buf,MAXLINE)) > 0)
62         writen (connfd,buf,n);
63 
64     if (n < 0 && errno == EINTR)
65         goto again;
66     else if (n < 0) {
67         perror ("write");
68         exit (1);
69     }
70 }

 

 

客户端代码client.c:

 1 #include "wrap.h"
 2 
 3 #define SER_PORT      9375
 4 
 5 void str_cli (int connfd);
 6 
 7 int main ()
 8 {
 9     int sockfd;
10     char *ip = "115.28.75.192";
11     struct sockaddr_in seraddr;
12 
13     sockfd = Socket (AF_INET,SOCK_STREAM,0);
14 
15     seraddr.sin_family = AF_INET;
16     seraddr.sin_port = htons (SER_PORT);
17     inet_pton (AF_INET,ip,&seraddr.sin_addr);
18 
19     Connect (sockfd,(struct sockaddr*)&seraddr,sizeof (seraddr));
20     str_cli (sockfd);
21 
22     return 0;
23 }
24 
25 void str_cli (int connfd)
26 {
27     char sendline[MAXLINE];
28     char recvline[MAXLINE];
29 
30     while (fgets (sendline,MAXLINE,stdin) != NULL) {
31         writen (connfd,sendline,strlen (sendline));
32 
33         if (readline (connfd,recvline,MAXLINE) == 0) {
34             printf ("server closed too early");
35             exit (0);
36         }
37 
38         printf ("%s\n",recvline);
39 
40     }
41 }

 

 

 

这段代码中,有几个需要注意的地方:

1.在服务端代码中,产生子进程之后,要关闭监听描述符(listenfd)。

2.在服务端代码中,父进程要关闭已连接描述符(connfd)。

3.子进程结束之后,会产生SIGCHLD信号,在父进程中捕获此信号,使用waitpid函数回收子进程的资源,以免出现僵尸进程。

4.accept等属于输入慢调用,会被信号中断,errno设置为EINTR,因此如果accept被中断,需要判断errno的值。

5.在readline函数实现中,使用了静态变量,这是线程不安全的函数,多线程编程中可能会存在问题。

代码中的问题:

在wrap.h中,我定义了SER_PORT的值,但是在client.c server.c 如果没有SER_PORT的宏定义时,在编译htons (SER_PORT)时,编译器就会报错,如下图。我在server.c中包含了wrap.h文件,为什么不能使用SER_PORT这个宏呢?

unix网络编程代码(2)_第1张图片

代码中有不完善的地方,请多指教。

 

你可能感兴趣的:(unix网络编程代码(2))