转自:http://blog.csdn.net/liujia2100/article/details/9006479
版权声明:本文为博主原创文章,未经博主允许不得转载。
各个主要函数的功能: 1、socket int socket(int domain, int type,int protocol) domain:说明我们网络程序所在的主机采用的通讯协族(AF_UNIX 和 AF_INET 等). AF_UNIX 只能够用于单一的 Unix 系统进程间通信,而 AF_INET 是针对 Internet 的,因而可以允 许在远程 主机之间通信(当我们 man socket 时发现 domain 可选项是 PF_*而不是 AF_*,因 为 glibc 是 posix 的实现 所以用 PF 代替了 AF,不过我们都可以使用的). type:我们网络程序所采用的通讯协议(SOCK_STREAM,SOCK_DGRAM 等) SOCK_STREAM 表明我们用的是 TCP 协议,这样会提供按顺序的,可靠,双向,面向连接的比特流. SOCK_DGRAM 表明我们用的是 UDP 协议,这样只会提供定长的,不可靠,无连接的通信. protocol:由于我们指定了 type,所以这个地方我们一般只要用 0 来代替就可以了 socket 为网络通讯做基本的准备.成功时返回文件描述符,失败时返回-1,看 errno 可知道出错 的详细情况. 2、bind int bind(int sockfd, struct sockaddr *my_addr, int addrlen) sockfd:是由 socket 调用返回的文件描述符. addrlen:是 sockaddr 结构的长度. my_addr:是一个指向 sockaddr 的指针. 在中有 sockaddr 的定义 struct sockaddr{ unisgned short as_family; char sa_data[14]; }; 不过由于系统的兼容性, 我们一般不用这个头文件, 而使用另外一个结构(struct sockaddr_in) 来代替.在中有 sockaddr_in 的定义 struct sockaddr_in{ unsigned short sin_family; unsigned short int sin_port; struct in_addr sin_addr; unsigned char sin_zero[8]; 我们主要使用 Internet 所以 sin_family 一般为 AF_INET,sin_addr 设置为 INADDR_ANY 表示可以和任何的主机通信,sin_port 是我们要监听的端口号.sin_zero[8]是用来填充的. bind 将本地的端口同 socket 返回的文件描述符捆绑在一起.成功是返回 0,失败的情况和 socket 一 样 3、listen int listen(int sockfd,int backlog) sockfd:是 bind 后的文件描述符. backlog:设置请求排队的最大长度.当有多个客户端程序和服务端相连时, 使用这个表示 可以介绍的排队长度. listen 函数将 bind 的文件描述符变为监听套接字.返回的情况和 bind 一 样. 4、 accept int accept(int sockfd, struct sockaddr *addr,int *addrlen) sockfd:是 listen 后的文件描述符. addr,addrlen 是用来给客户端的程序填写的,服务器端只要传递指针就可以了. bind,listen 和 accept 是服务器端用的函数,accept 调用时,服务器端的程序会一直阻塞到有一个客户程序 发出了连接. accept 成功时返回最后的服务器端的文件描述符,这个时候服务器端可以向该描 述符写信息了. 失败时返回-1 5、 connect int connect(int sockfd, struct sockaddr * serv_addr,int addrlen) sockfd:socket 返回的文件描述符. serv_addr:储存了服务器端的连接信息.其中 sin_add 是服务端的地址 addrlen:serv_addr 的长度 connect 函数是客户端用来同服务端连接的.成功时返回 0,sockfd 是同服务端通讯的文件 描述符 失败时返回-1. 6、 gethostbyname 和 gethostbyaddr 都涉及到一个 hostent 的结构体,如下所示: Struct hostent{ char *h_name;/*正式主机名*/ char **h_aliases;/*主机别名*/ int h_addrtype;/*地址类型*/ int h_length;/*地址长度*/ char **h_addr_list;/*指向 IPv4或 IPv6的地址指针数组*/ } 所需头文件 #include <netdb.h> 函数原型 Struct hostent *gethostbyname(const char *hostname) 函数传入值 Hostname:主机名 函数返回值 成功: hostent 类型指针 出错:−1 服务端源码: [cpp] view plain copy #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <sys/types.h> #include <sys/time.h> #include <unistd.h> #include <string.h> #include <netdb.h> #include <sys/types.h> #include <netinet/in.h> #include <sys/socket.h> int main(int argc, char *argv[]) { int sockfd,new_fd; int sockaddr_size; int ret; int len; struct sockaddr_in server_addr; //结构体sockaddr_in 和 aockaddr 是兼容的 struct sockaddr_in client_addr; int sin_size,portnumber; char buf[1024]; fd_set rfds; struct timeval tv; int maxfd; FD_ZERO(&rfds); FD_SET(STDIN_FILENO, &rfds); if((portnumber = atoi(argv[1]))<0) { printf("port number error, will exit\n"); exit(1); } /* 服务器端开始建立 socket 描述符 */ if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1) //AF_INET也可以用PF_INET代替,表明针对錓neternet,可以建立远程连接。SOCK_STREAM表明使用的是TCP协议 { //而 SOCK_DGRAM 表明我们用的是 UDP 协议 printf("socket, will exit\n"); close(sockfd); exit(1); } /* 服务器端填充 sockaddr 结构 */ bzero(&server_addr,sizeof(struct sockaddr_in)); server_addr.sin_family=AF_INET; server_addr.sin_addr.s_addr=htonl(INADDR_ANY); //标识可以和任何主机通信 server_addr.sin_port=htons(portnumber); /* 捆绑 sockfd 描述符 */ if(bind(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr))==-1) { printf("bind error, will exit\n"); close(sockfd); exit(1); } /* 监听 sockfd 描述符 */ if(listen(sockfd,5)==-1) //设置客户端请求排队的最大长度,允许最大5个 { printf("listen, will exit\n"); close(sockfd); exit(1); } while(1) { /* 服务器阻塞,直到客户程序建立连接 */ printf("Will go to talk, wait connecting...\n"); sockaddr_size = sizeof(struct sockaddr); //int accept(int sockfd, struct sockaddr *addr,int *addrlen) if((new_fd=accept(sockfd,(struct sockaddr *)(&client_addr),&sockaddr_size))==-1) { printf("accept error, will exit\n"); close(sockfd); exit(1); } else { printf("server: get connection from %s, port %d, socket %d\n", (char *)inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port), new_fd); } //进行会话 while(1) { FD_ZERO(&rfds); FD_SET(STDIN_FILENO, &rfds); FD_SET(new_fd, &rfds); //maxfd = new_fd > STDIN_FILENO ? newfd :STDIN_FILENO; //maxfd = maxfd + 1; maxfd = new_fd + 1; tv.tv_sec = 1; tv.tv_usec = 0;//设置等待时间 ret = select(maxfd, &rfds, NULL, NULL, &tv); if(ret<0) { printf("select error, process will eixt\n"); exit(0); } else if(FD_ISSET(STDIN_FILENO, &rfds))//测试是否有数据 { bzero(buf,1024); //printf("Server :"); fgets(buf, 1024, stdin); if (!strncasecmp(buf, "quit", 4)) { printf("current talk exit\n"); FD_CLR(new_fd, &rfds); close(new_fd); break; } //printf("Server :%s\n",buf); // int recv(int sockfd,void *buf,int len,int flags) // int send(int sockfd,void *buf,int len,int flags) //最后一个参数可以是0,也可以是 /* _______________________________________________________________ | MSG_DONTROUTE | 不查找路由表 | | MSG_OOB | 接受或者发送带外数据 | | MSG_PEEK | 查看数据,并不从系统缓冲区移走数据 | | MSG_WAITALL | 等待所有数据 | |--------------------------------------------------------------| */ len = send(new_fd,buf,strlen(buf)-1,0); if(len < 0) { printf("send fail\n"); } } else if(FD_ISSET(new_fd, &rfds)) { bzero(buf,1024); len = recv(new_fd,buf,strlen(buf)-1,0); if(len < 0) { printf("receive fail\n"); } if(len == 0)//当客户端终止运行的时候,会不断检测到sockfd的状态变化,但是收到的信息长度为0 { printf("Client has already exit, will exit\n"); FD_CLR(new_fd, &rfds); close(new_fd); break; } if (!strncasecmp(buf, "quit", 4)) { printf("current talk exit\n"); FD_CLR(new_fd, &rfds); close(new_fd); break; } buf[len+1]='\0'; printf(" Client :%s\n",buf); } } } } 客户端: [cpp] view plain copy #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <sys/types.h> #include <sys/time.h> #include <unistd.h> #include <string.h> #include <netdb.h> #include <sys/types.h> #include <netinet/in.h> #include <sys/socket.h> int main(int argc, char *argv[]) { int sockfd,new_fd; int sockaddr_size; int ret; int len; struct sockaddr_in server_addr; //结构体sockaddr_in 和 aockaddr 是兼容的 struct hostent *host; int sin_size,portnumber; char buf[1024]; fd_set rfds; struct timeval tv; int maxfd; FD_ZERO(&rfds); FD_SET(STDIN_FILENO, &rfds); if((host=gethostbyname(argv[1])) == 0) { printf("server ip %s error\n",argv[1]); exit(1); } if((portnumber = atoi(argv[2]))<0) { printf("port number error, will exit\n"); exit(1); } /* 客户程序开始建立 sockfd 描述符 */ if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1) //AF_INET也可以用PF_INET代替,表明针对錓neternet,可以建立远程连接。SOCK_STREAM表明使用的是TCP协议 { //而 SOCK_DGRAM 表明我们用的是 UDP 协议 printf("socket, will exit\n"); exit(1); } /* 客户程序填充服务端的资料 */ bzero(&server_addr,sizeof(struct sockaddr_in)); server_addr.sin_family=AF_INET; server_addr.sin_port=htons(portnumber); server_addr.sin_addr=*((struct in_addr *)host->h_addr); /* 客户程序发起连接请求 */ if(connect(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr))==-1) { printf("connect will exit\n"); exit(1); } printf("start talk...\n"); FD_SET(sockfd, &rfds); //进行会话 while(1) { FD_ZERO(&rfds); FD_SET(STDIN_FILENO, &rfds); FD_SET(sockfd, &rfds); //maxfd = new_fd > STDIN_FILENO ? newfd :STDIN_FILENO; //maxfd = maxfd + 1; maxfd = sockfd + 1; tv.tv_sec = 1; tv.tv_usec = 0;//设置等待时间 ret = select(maxfd, &rfds, NULL, NULL, &tv); if(ret<0) { printf("select error, process will eixt\n"); exit(0); } else if(FD_ISSET(STDIN_FILENO, &rfds))//测试是否有数据 { bzero(buf,1024); //printf("Client :"); fgets(buf, 1024, stdin); if (!strncasecmp(buf, "quit", 4)) { printf("current talk exit\n"); FD_CLR(sockfd, &rfds); close(sockfd); break; } //printf("Client :%s\n",buf); // int recv(int sockfd,void *buf,int len,int flags) // int send(int sockfd,void *buf,int len,int flags) //最后一个参数可以是0,也可以是 /* _______________________________________________________________ | MSG_DONTROUTE | 不查找路由表 | | MSG_OOB | 接受或者发送带外数据 | | MSG_PEEK | 查看数据,并不从系统缓冲区移走数据 | | MSG_WAITALL | 等待所有数据 | |--------------------------------------------------------------| */ len = send(sockfd,buf,strlen(buf)-1,0); if(len < 0) { printf("send fail\n"); } } else if(FD_ISSET(sockfd, &rfds)) { bzero(buf,1024); len = recv(sockfd,buf,strlen(buf)-1,0); if(len < 0) { printf("receive fail\n"); } if(len == 0)//当服务器终止运行的时候,会不断检测到sockfd的状态变化,但是收到的信息长度为0 { printf("Server has already exit, will exit\n"); FD_CLR(sockfd, &rfds); close(sockfd); break; } if (!strncasecmp(buf, "quit", 4)) { printf("current talk exit\n"); FD_CLR(sockfd, &rfds); close(sockfd); break; } buf[len+1]='\0'; printf(" Server: %s\n",buf); } } }
【作者】
张昺华
【新浪微博】 张昺华--sky
【twitter】 @sky2030_
【facebook】 张昺华 zhangbinghua
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.