第一版:实现简单功能
客户端从标准输入读入字符串传给服务端,服务端把字符串中的小写字母转化为大写字母传回客户端
#include <stdio.h> #include <unistd.h> #include <sys/socket.h> #include <stdlib.h> #include <arpa/inet.h> #include <ctype.h> #include <string.h> #define SERV_IP "127.0.0.1" #define SERV_PORT 6666 int main() { int cfd; struct sockaddr_in serv_addr; socklen_t serv_addr_len; char buf[BUFSIZ]; int n; cfd = socket(AF_INET, SOCK_STREAM, 0); memset(&serv_addr, 0, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(SERV_PORT); //serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); inet_pton(AF_INET, SERV_IP, &serv_addr.sin_addr.s_addr); //客户端可以不用bind()绑定ip和端口号,操作系会默认分配 connect(cfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)); while(1) { fgets(buf, sizeof(buf), stdin); write(cfd, buf, strlen(buf)); n = read(cfd, buf, sizeof(buf)); write(STDOUT_FILENO, buf, n); } close(cfd); return 0; }
#include <stdio.h> #include <unistd.h> #include <sys/socket.h> #include <stdlib.h> #include <arpa/inet.h> #include <ctype.h> #define SERV_PORT 6666 int main() { int sfd, cfd; struct sockaddr_in serv_addr, clie_addr; socklen_t clie_addr_len; char buf[BUFSIZ]; int n, i; sfd = socket(AF_INET, SOCK_STREAM, 0); serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(SERV_PORT); serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); bind(sfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)); listen(sfd, 128); clie_addr_len = sizeof(clie_addr); cfd = accept(sfd, (struct sockaddr*)&clie_addr, &clie_addr_len); while(1) { n = read(cfd, buf, sizeof(buf)); for(i=0; i<n; i++) { buf[i] = toupper(buf[i]); } write(cfd, buf, n); } close(sfd); close(cfd); return 0; }
第二版:加入错误封装
上面没有对错误进行处理,我们知道,系统调用不能保证每次都成功,必须进行出错处理,这样一方面可以保证程序逻辑正常,另一方面可以迅速得到故障信息。为使错误处理的代码不影响主程序的可读性,我们把与socket相关的一些系统函数加上错误处理代码包装成新的函数(新函数和原函数调用的区别仅仅在于新函数名首字母大写了),做成一个模块wrap.c,这样在调用socket函数时,调用我们封装了错误处理的函数就可以了。
#include <stdlib.h> #include <errno.h> #include <sys/socket.h> void perr_exit(const char *s) { perror(s); exit(1); } int Accept(int fd, struct sockaddr *sa, socklen_t *salenptr) { int n; again: if ( (n = accept(fd, sa, salenptr)) < 0) { if ((errno == ECONNABORTED) || (errno == EINTR)) goto again; else perr_exit("accept error"); } return n; } int Bind(int fd, const struct sockaddr *sa, socklen_t salen) { int n; if ((n = bind(fd, sa, salen)) < 0) perr_exit("bind error"); return n; } int Connect(int fd, const struct sockaddr *sa, socklen_t salen) { int n; if ((n = connect(fd, sa, salen)) < 0) perr_exit("connect error"); return n; } int Listen(int fd, int backlog) { int n; if ((n = listen(fd, backlog)) < 0) perr_exit("listen error"); return n; } int Socket(int family, int type, int protocol) { int n; if ( (n = socket(family, type, protocol)) < 0) perr_exit("socket error"); return n; } ssize_t Read(int fd, void *ptr, size_t nbytes) { ssize_t n; again: if ( (n = read(fd, ptr, nbytes)) == -1) { if (errno == EINTR) goto again; else return -1; } return n; } ssize_t Write(int fd, const void *ptr, size_t nbytes) { ssize_t n; again: if ( (n = write(fd, ptr, nbytes)) == -1) { if (errno == EINTR) goto again; else return -1; } return n; } int Close(int fd) { int n; if ((n = close(fd)) == -1) perr_exit("close error"); return n; } ssize_t Readn(int fd, void *vptr, size_t n) { size_t nleft; ssize_t nread; char *ptr; ptr = vptr; nleft = n; while (nleft > 0) { if ( (nread = read(fd, ptr, nleft)) < 0) { if (errno == EINTR) nread = 0; else return -1; } else if (nread == 0) break; nleft -= nread; ptr += nread; } return n - nleft; } ssize_t Writen(int fd, const void *vptr, size_t n) { size_t nleft; ssize_t nwritten; const char *ptr; ptr = vptr; nleft = n; while (nleft > 0) { if ( (nwritten = write(fd, ptr, nleft)) <= 0) { if (nwritten < 0 && errno == EINTR) nwritten = 0; else return -1; } nleft -= nwritten; ptr += nwritten; } return n; } static ssize_t my_read(int fd, char *ptr) { static int read_cnt; static char *read_ptr; static char read_buf[100]; if (read_cnt <= 0) { again: if ((read_cnt = read(fd, read_buf, sizeof(read_buf))) < 0) { if (errno == EINTR) goto again; return -1; } else if (read_cnt == 0) return 0; read_ptr = read_buf; } read_cnt--; *ptr = *read_ptr++; return 1; } ssize_t Readline(int fd, void *vptr, size_t maxlen) { ssize_t n, rc; char c, *ptr; ptr = vptr; for (n = 1; n < maxlen; n++) { if ( (rc = my_read(fd, &c)) == 1) { *ptr++ = c; if (c == ' ') break; } else if (rc == 0) { *ptr = 0; return n - 1; } else return -1; } *ptr = 0; return n; }
#ifndef __WRAP_H_ #define __WRAP_H_ void perr_exit(const char *s); int Accept(int fd, struct sockaddr *sa, socklen_t *salenptr); int Bind(int fd, const struct sockaddr *sa, socklen_t salen); int Connect(int fd, const struct sockaddr *sa, socklen_t salen); int Listen(int fd, int backlog); int Socket(int family, int type, int protocol); ssize_t Read(int fd, void *ptr, size_t nbytes); ssize_t Write(int fd, const void *ptr, size_t nbytes); int Close(int fd); ssize_t Readn(int fd, void *vptr, size_t n); ssize_t Writen(int fd, const void *vptr, size_t n); ssize_t my_read(int fd, char *ptr); ssize_t Readline(int fd, void *vptr, size_t maxlen); #endif
第三版:使用多进程技术在服务端实现高并发
#include <stdio.h> #include <unistd.h> #include <sys/socket.h> #include <stdlib.h> #include <arpa/inet.h> #include <ctype.h> #include <strings.h> #include <sys/wait.h> #include "wrap.h" #define SERV_PORT 6666 void wait_child(int signo) { while(waitpid(0, NULL, WNOHANG) > 0); return ; } int main() { pid_t pid; int sfd, cfd; struct sockaddr_in serv_addr, clie_addr; socklen_t clie_addr_len; char buf[BUFSIZ], clie_IP[BUFSIZ]; int n, i; sfd = Socket(AF_INET, SOCK_STREAM, 0); bzero(&serv_addr, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(SERV_PORT); serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); Bind(sfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)); Listen(sfd, 128); while(1) { clie_addr_len = sizeof(clie_addr); cfd = Accept(sfd, (struct sockaddr*)&clie_addr, &clie_addr_len); printf("client IP: %s, port: %d ", inet_ntop(AF_INET, &clie_addr.sin_addr.s_addr, clie_IP, sizeof(clie_IP)), ntohs(clie_addr.sin_port)); pid = fork(); if(pid < 0) { perror("fork error"); exit(1); } else if(pid == 0) { close(sfd); break; } else { close(cfd); signal(SIGCHLD, wait_child); } } if(pid == 0) { while(1) { n = Read(cfd, buf, sizeof(buf)); if(n == 0) { close(cfd); return 0; } else if(n == -1) { perror("read error"); exit(1); } else { for(i=0; i<n; i++) { buf[i] = toupper(buf[i]); } write(cfd, buf, n); write(STDOUT_FILENO, buf, n); } } } return 0; }
第四版:使用多线程技术在服务端实现高并发
#include <stdio.h> #include <unistd.h> #include <sys/socket.h> #include <stdlib.h> #include <arpa/inet.h> #include <ctype.h> #include <strings.h> #include <sys/wait.h> #include <fcntl.h> #include <pthread.h> #include <string.h> #include "wrap.h" #define SERV_PORT 6666 #define MAXLINE 8192 struct s_info{ struct sockaddr_in cliaddr; int connfd; }; void* do_work(void *arg) { int n,i; struct s_info *ts = (struct s_info*)arg; char buf[MAXLINE]; char str[INET_ADDRSTRLEN]; while(1){ n = Read(ts->connfd, buf, MAXLINE); if(n == 0){ printf("the client %d closed... ", ts->connfd); break; } printf("received from %s at PORT %d ", inet_ntop(AF_INET, &(*ts).cliaddr.sin_addr, str, sizeof(str)), ntohs((*ts).cliaddr.sin_port)); for(i=0; i<n; i++){ buf[i] = toupper(buf[i]); } write(ts->connfd, buf, n); write(STDOUT_FILENO, buf, n); } Close(ts->connfd); return (void *)0; } int main() { int sfd, cfd; struct sockaddr_in serv_addr, clie_addr; socklen_t clie_addr_len; struct s_info ts[256]; pthread_t tid; int i = 0; sfd = Socket(AF_INET, SOCK_STREAM, 0); bzero(&serv_addr, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(SERV_PORT); serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); Bind(sfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)); Listen(sfd, 128); while(1) { clie_addr_len = sizeof(clie_addr); cfd = Accept(sfd, (struct sockaddr*)&clie_addr, &clie_addr_len); ts[i].cliaddr = clie_addr; ts[i].connfd = cfd; pthread_create(&tid, NULL, do_work, (void*)&ts[i]); pthread_detach(tid); i++; } return 0; }