echo_stdserv.c
//套接字的标准IO:标准IO函数有自己的缓冲,可以提高传输性能 //而系统函数read和write未提供缓冲,传输相对较慢 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #include <sys/socket.h> #define BUF_SIZE 1024 void error_handling(char *message); int main(int argc, char *argv[]) { int serv_sock; int clnt_sock; struct sockaddr_in serv_addr; struct sockaddr_in clnt_addr; socklen_t clnt_addr_size; int str_len; char message[BUF_SIZE]; FILE *readfp; FILE *writefp; if(argc!=2) { exit(1); } //TCP socket serv_sock=socket(PF_INET, SOCK_STREAM, 0); if(serv_sock == -1) error_handling("socket error!"); memset(&serv_addr, 0, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; //IPV4协议族 serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); //主机字节序(host)转换成网络字节序(net)(大端序) serv_addr.sin_port = htons(atoi(argv[1])); //端口号 if(bind(serv_sock, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) == -1) error_handling("bind error"); if(listen(serv_sock, 5) == -1) error_handling("listen error"); clnt_addr_size = sizeof(clnt_addr); int i; for(i=0;i<5;i++) { clnt_sock=accept(serv_sock, (struct sockaddr*) &clnt_addr, &clnt_addr_size); if(clnt_sock == -1) error_handling("accept error"); else printf("connect client: %d \n", i+1); //下面使用标准IO函数传输数据 readfp=fdopen(clnt_sock, "r"); //将套接字描述符转换为FILE指针 writefp=fdopen(clnt_sock, "w"); while(!feof(readfp)) { fgets(message, BUF_SIZE, readfp); fputs(message, writefp); //标准IO内部有额外缓冲,若不调用fflush则无法立即将数据传输到客户端 fflush(writefp); } fclose(readfp); fclose(writefp); } close(serv_sock); return 0; } void error_handling(char *message) { fputs(message, stderr); fputc('\n', stderr); exit(1); }
IO流分离(半关闭的实现)
sep_serv.c
//分离IO流,文件描述符的复制和半关闭 //文件描述符复制:dup(),dup2() //shutdown函数使服务器端进入半关闭状态 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #include <sys/socket.h> #define BUF_SIZE 1024 void error_handling(char *message); int main(int argc, char *argv[]) { int serv_sock; int clnt_sock; struct sockaddr_in serv_addr; struct sockaddr_in clnt_addr; socklen_t clnt_addr_size; int str_len; char buf[BUF_SIZE]; FILE *readfp; FILE *writefp; if(argc!=2) { exit(1); } //TCP socket serv_sock=socket(PF_INET, SOCK_STREAM, 0); if(serv_sock == -1) error_handling("socket error!"); memset(&serv_addr, 0, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; //IPV4协议族 serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); //主机字节序(host)转换成网络字节序(net)(大端序) serv_addr.sin_port = htons(atoi(argv[1])); //端口号 if(bind(serv_sock, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) == -1) error_handling("bind error"); if(listen(serv_sock, 5) == -1) error_handling("listen error"); clnt_addr_size = sizeof(clnt_addr); readfp=fdopen(clnt_sock, "r"); writefp=fdopen(dup(clnt_sock), "w"); //使用dup复制套接字描述符 fputs("hello world!", writefp); fflush(writefp); //调用shutdown关闭输出流,fileno作用是将FILE指针转换为描述符 shutdown(fileno(writefp), SHUT_WR); fclose(writefp); //关闭writefp不会关闭整个套接字,因为上面复制了套接字描述符 fgets(buf, sizeof(buf), readfp); fputs(buf, stdout); fclose(readfp); //至此关闭了套接字 return 0; } void error_handling(char *message) { fputs(message, stderr); fputc('\n', stderr); exit(1); }