unix網絡編程代碼(2)

繼續貼《unix網絡編程》上的示例代碼。此次是一個反射程序,反射是客戶端講用戶輸入的文本發送到服務器端,服務器端讀取客戶端發過來的文本消息,而後原封不動的把文本消息返回給客戶端。使用tcp協議鏈接客戶端和服務端,我已經在個人阿里雲服務器上測試過了,可以完美運行。linux

 

首先是頭文件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:api

  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)。dom

3.子進程結束以後,會產生SIGCHLD信號,在父進程中捕獲此信號,使用waitpid函數回收子進程的資源,以避免出現殭屍進程。socket

4.accept等屬於輸入慢調用,會被信號中斷,errno設置爲EINTR,所以若是accept被中斷,須要判斷errno的值。tcp

5.在readline函數實現中,使用了靜態變量,這是線程不安全的函數,多線程編程中可能會存在問題。

代碼中的問題:

在wrap.h中,我定義了SER_PORT的值,可是在client.c server.c 若是沒有SER_PORT的宏定義時,在編譯htons (SER_PORT)時,編譯器就會報錯,以下圖。我在server.c中包含了wrap.h文件,爲何不能使用SER_PORT這個宏呢?

代碼中有不完善的地方,請多指教。

相關文章
相關標籤/搜索