继续贴《unix网络编程》上的示例代码。《unix网络编程》在第八章结束位置给出了一个使用select函数的TCP、UDP回射服务器程序,笔者把它实现了,现贴上代码:
服务端代码:
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <unistd.h> 4 #include <strings.h> 5 #include <sys/socket.h> 6 #include <sys/types.h> 7 #include <sys/wait.h> 8 #include <arpa/inet.h> 9 #include <signal.h> 10 #include <errno.h> 11 #include <netinet/in.h> 12 13 #define SER_PORT 9374 14 15 void handler (int signo); 16 int maxfd (int fd1,int fd2); 17 void str_echo (int connfd); 18 ssize_t writen (int fd,void *vptr,size_t n); 19 20 int main () 21 { 22 int tcp_fd,udp_fd,connfd; 23 int nready,maxfd1; 24 fd_set rset; 25 pid_t pid; 26 ssize_t n; 27 char recvline[4096]; 28 socklen_t clilen,serlen; 29 socklen_t uclilen; 30 struct sockaddr_in seraddr,tcp_cli,udp_cli; 31 struct sigaction act,old; 32 const int on = 1; 33 34 bzero (&seraddr,sizeof (seraddr)); 35 bzero (&tcp_cli,sizeof (tcp_cli)); 36 bzero (&udp_cli,sizeof (udp_cli)); 37 bzero (&act,sizeof (act)); 38 bzero (&old,sizeof (old)); 39 40 /*for signal*/ 41 act.sa_flags = 0; 42 act.sa_handler = handler; 43 sigemptyset (&act.sa_mask); 44 45 if ((sigaction (SIGCHLD,&act,&old)) < 0) { 46 perror ("sigaction"); 47 exit (1); 48 } 49 50 /*for tcp connection*/ 51 if ((tcp_fd = socket (AF_INET,SOCK_STREAM,0)) < 0) { 52 perror ("socket"); 53 exit (1); 54 } 55 if ((setsockopt (tcp_fd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof (on))) < 0) { 56 perror ("setsockopt"); 57 exit (1); 58 } 59 60 seraddr.sin_family = AF_INET; 61 seraddr.sin_port = htons (SER_PORT); 62 seraddr.sin_addr.s_addr = htonl (INADDR_ANY); 63 64 65 if ((bind (tcp_fd,(struct sockaddr *)&seraddr,sizeof (seraddr))) < 0) { 66 perror ("bind"); 67 exit (1); 68 } 69 70 if ((listen (tcp_fd,20)) < 0) { 71 perror ("listen"); 72 exit (1); 73 } 74 75 /*for udp connection*/ 76 if ((udp_fd = socket (AF_INET,SOCK_DGRAM,0)) < 0) { 77 perror ("socket"); 78 exit (1); 79 } 80 81 bzero (&seraddr,sizeof (seraddr)); 82 seraddr.sin_family = AF_INET; 83 seraddr.sin_addr.s_addr = htonl (INADDR_ANY); 84 seraddr.sin_port = htons (SER_PORT); 85 86 if ((bind (udp_fd,(struct sockaddr*) &seraddr,sizeof (seraddr))) < 0) { 87 perror ("bind"); 88 exit (1); 89 } 90 91 /*select*/ 92 maxfd1 = maxfd (tcp_fd,udp_fd) + 1; 93 FD_ZERO (&rset); 94 while (1) { 95 FD_SET (tcp_fd,&rset); 96 FD_SET (udp_fd,&rset); 97 if ((nready = select (maxfd1,&rset,NULL,NULL,NULL)) < 0) { 98 if (errno = EINTR) 99 continue; 100 else { 101 perror ("select"); 102 exit (1); 103 } 104 } 105 106 if (FD_ISSET (tcp_fd,&rset)) { 107 clilen = sizeof (tcp_cli); 108 connfd = accept (tcp_fd,(struct sockaddr*)&tcp_cli,&clilen); 109 pid = fork (); 110 111 if (pid < 0) { 112 perror ("fork"); 113 exit (1); 114 } 115 else if (pid == 0) 116 close (connfd); 117 else { 118 printf ("process %d created\n",getpid ()); 119 close (tcp_fd); 120 /*communication with client*/ 121 str_echo (connfd); 122 printf ("process %d exit\n",getpid ()); 123 exit (0); 124 } 125 } 126 127 if (FD_ISSET (udp_fd,&rset)) { 128 uclilen = sizeof (udp_cli); 129 n = recvfrom (udp_fd,recvline,4096,0,(struct sockaddr*) &udp_cli,&uclilen); 130 sendto (udp_fd,recvline,n,0,(struct sockaddr*) &udp_cli,uclilen); 131 } 132 } 133 134 return 0; 135 } 136 137 void handler (int signo) 138 { 139 pid_t chldpid; 140 while ((chldpid = waitpid (-1,NULL,WNOHANG)) > 0) { 141 printf ("child process %d terminated\n",chldpid); 142 } 143 } 144 145 int maxfd (int fd1,int fd2) 146 { 147 if (fd1 >= fd2) 148 return fd1; 149 else 150 return fd2; 151 } 152 153 void str_echo (int connfd) 154 { 155 ssize_t n; 156 char buf[4096]; 157 again: 158 while ((n = read (connfd,buf,4096)) > 0) 159 writen (connfd,buf,n); 160 161 if (n < 0 && errno == EINTR) 162 goto again; 163 else if (n < 0) { 164 perror ("write"); 165 exit (1); 166 } 167 168 printf ("str_echo end\n"); 169 } 170 171 ssize_t writen (int fd,void *vptr,size_t n) 172 { 173 size_t nleft; 174 ssize_t nwritten; 175 const char *ptr; 176 177 ptr = vptr; 178 nleft = n; 179 while (nleft > 0) { 180 if ((nwritten = write (fd,ptr,nleft)) <= 0) { 181 /*nothing written or an error occured*/ 182 if (nwritten < 0 && errno == EINTR) { 183 /* 184 *The call was interrupted by a signal before any data was read 185 *and call it again. 186 */ 187 nwritten = 0; 188 } else { 189 /*the call was interrupted by other signal,return -1*/ 190 return (-1); 191 } 192 } 193 194 nleft -= nwritten; 195 ptr += nwritten; 196 } 197 198 return (n); 199 }
使用UDP协议的客户端:
1 #include <stdlib.h> 2 #include <stdio.h> 3 #include <unistd.h> 4 #include <errno.h> 5 #include <arpa/inet.h> 6 #include <netinet/in.h> 7 #include <sys/socket.h> 8 #include <sys/types.h> 9 #include <string.h> 10 11 #define SER_PORT 9374 12 13 void dg_cli (FILE *fp,int sockfd, 14 const struct sockaddr *pser, 15 socklen_t serlen); 16 17 int main () 18 { 19 int sockfd; 20 struct sockaddr_in seraddr; 21 char *ip = "127.0.0.1"; 22 23 if ((sockfd = socket (AF_INET,SOCK_DGRAM,0)) == -1) { 24 perror ("socket"); 25 exit (1); 26 } 27 28 bzero (&seraddr,sizeof (seraddr)); 29 seraddr.sin_family = AF_INET; 30 seraddr.sin_port = htons (SER_PORT); 31 inet_pton (AF_INET,ip,&seraddr.sin_addr); 32 33 dg_cli (stdin,sockfd,(struct sockaddr*)&seraddr,sizeof (seraddr)); 34 35 return 0; 36 } 37 38 39 void dg_cli (FILE *fp,int sockfd, 40 const struct sockaddr *pser, 41 socklen_t serlen) { 42 int n; 43 char sendline[4096]; 44 char recvline[4096]; 45 if ((connect (sockfd,pser,serlen)) < 0) { 46 perror ("connect"); 47 exit (1); 48 } 49 50 while ((fgets (sendline,4096,fp)) != NULL) { 51 write (sockfd,sendline,strlen (sendline)); 52 n = read (sockfd,recvline,4096); 53 recvline[n] = ‘\0‘; 54 printf ("%s\n",recvline); 55 } 56 }
使用TCP协议的客户端:
1 #include "wrap.h" 2 3 #define SER_PORT 9374 4 5 void str_cli (FILE *fp,int connfd); 6 7 int main () 8 { 9 int sockfd; 10 char *ip = "127.0.0.1"; 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,&seraddr,sizeof (seraddr)); 20 str_cli (stdin,sockfd); 21 22 return 0; 23 } 24 25 void str_cli (FILE *fp,int connfd) 26 { 27 int maxfpd1,stdineof; 28 fd_set rset; 29 char buf[MAXLINE]; 30 int n; 31 32 stdineof = 0; 33 while (1) { 34 if (stdineof == 0) 35 FD_SET (fileno (fp),&rset); 36 FD_SET (connfd,&rset); 37 maxfpd1 = Max (fileno (fp),connfd) + 1; 38 select (maxfpd1,&rset,NULL,NULL,NULL); 39 40 if (FD_ISSET (connfd,&rset)) { 41 if ((n = read (connfd,buf,MAXLINE)) == 0) { 42 if (stdineof == 1) 43 return; 44 else { 45 printf ("str_cli:server terminated too early"); 46 exit (1); 47 } 48 } 49 write (fileno (stdout),buf,n); 50 } 51 52 if (FD_ISSET (fileno (fp),&rset)) { 53 if ((n = read (fileno (fp),buf,MAXLINE)) == 0) { 54 stdineof = 1; 55 shutdown (connfd,SHUT_WR); 56 FD_CLR (fileno (fp),&rset); 57 continue; 58 } 59 60 writen (connfd,buf,n); 61 } 62 } 63 }
其中,wrap.h以及一些包裹函数的实现,请见笔者博文《unix网络编程》(2)。
在服务端代码中:
1.建立了两个套接字,分别用于tcp、udp通信
2.两个套接字绑定于同一ip地址和端口(setsockopt实现)
3.捕捉SIGCHLD信号,回收子进程资源
4.使用select监听tcp的监听套接字,当tcp连接成功之后,建立子进程处理客户端信息;同时监听使用数据报协议的套接字
代码中有不完善的地方,还望多多指教。
原文:http://www.cnblogs.com/zxy-2016/p/5199970.html