Outline
- 1.数据结构
- 2.服务器端编程框架
- 3.客户端编程框架
- 4.服务器端select多路IO转接使用框架 ======================================================================================
1.数据结构
struct addrinfo;
该结构的主要用途是使用getaddrinfo函数,自动填充socket相关信息。
struct sockaddr;
struct sockaddr_storage;
这两个为通用的socket结构,sockaddr可以指向sockaddr_in和sockaddr_in6,sockaddr_storage的大小可以容纳下任何一种socket(IPv4, IPv6, etc)。
struct sockaddr_in;
struct sockaddr_in6;
分别对应IPv4和IPv6地址。
2. 服务器端编程框架
getaddrinfo()
socket()
bind()
freeaddrinfo()
listen()
new_fd = accept()
fork() (为避免僵尸进程,可以使用信号SIGCHLD或者fork两次的方法)
recv(new_fd, ...)/send(new_fd, ...)
3. 客户端编程框架
getaddrinfo()
socket()
connect() (可选,SOCK_STREAM/SOCK_DGRAM)
freeaddrinfo()
recv()/recvfrom()/send()/sendto()
4. 服务器端select多路IO转接使用框架
a broadcast-echo server
测试方法:
打开多个
telnet localhost 8888
在某个客户端中发送消息后,其他客户端会收到消息回显
- /*
- * =====================================================================================
- *
- * Filename: multichat_server.c
- *
- * Description:
- *
- * Version: 1.0
- * Created: 08/07/11 15:05:34
- * Revision: none
- * Compiler: gcc
- *
- * Author: YOUR NAME (),
- * Company:
- *
- * =====================================================================================
- */
- #include <stdio.h>
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <netdb.h>
- #include <sys/select.h>
- #include <string.h>
- #include <unistd.h>
- #include <arpa/inet.h>
- int main(int argc, char *argv[])
- {
- struct addrinfo hints, *addr;
- struct sockaddr_storage client_addr;
- int ret;
- int fd, new_fd;
- fd_set cons, read_fds;
- int max_fd;
- char buf[50];
- char addrbuf[INET_ADDRSTRLEN];
- int i, j;
- int addrlen;
- int yes = 1;
- int nbytes;
- memset(&hints, 0, sizeof(struct addrinfo));
- hints.ai_flags = AI_PASSIVE;
- hints.ai_family = AF_INET;
- hints.ai_socktype = SOCK_STREAM;
- ret = getaddrinfo(NULL, "8888", &hints, &addr);
- if (ret != 0) {
- printf("getaddrinfo error: %s\n", gai_strerror(ret));
- return -1;
- }
-
- while(addr != NULL){
- fd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
- if (fd < 0) {
- perror("open socket error");
- return -1;
- }
- setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
- ret = bind(fd, (struct sockaddr *)addr->ai_addr, addr->ai_addrlen);
- if (ret < 0) {
- perror("bind error");
- close(fd);
- addr = addr->ai_next;
- continue;
- }
-
- break;
- }
- ret = listen(fd, 10);
- if (ret < 0) {
- perror("listen error");
- close(fd);
- return -1;
- }
-
- FD_ZERO(&cons);
- FD_SET(fd, &cons);
- max_fd = fd;
-
- while(1){
- read_fds = cons;
- ret = select(max_fd + 1, &read_fds, NULL, NULL, NULL);
- if (ret < 0) {
- perror("select error");
- return -1;
- }
-
- for (i = 0; i <= max_fd; i++) {
- ret = FD_ISSET(i, &read_fds);
- if (ret == 1) {
- if (i == fd) {
- //we've got a new connection
- addrlen = sizeof(struct sockaddr_in);
- new_fd = accept(i, (struct sockaddr *)&client_addr, &addrlen);
- if (new_fd < 0) {
- perror("accept error");
- }
- printf("new connection from %s\n",
- inet_ntop(client_addr.ss_family, &((struct sockaddr_in *)&client_addr)->sin_addr, addrbuf, INET6_ADDRSTRLEN));
- FD_SET(new_fd, &cons);
- if (new_fd > max_fd)
- max_fd = new_fd;
- } else {
- //client socket is readable
- nbytes = recv(i, buf, sizeof(buf), 0);
-
- //the connection is closed
- if (nbytes == 0) {
- close(i);
- FD_CLR(i, &cons);
- }
- else if (nbytes > 0) { //data available on i
- for (j = 0; j <= max_fd; j++) {
- ret = FD_ISSET(j, &cons);
- if (ret == 0 || j == fd || j == i)
- continue;
- nbytes = send(j, buf, nbytes, 0);
- if (nbytes < 0)
- perror("server echo error");
- }
- }
- }
- }
- }
- }
- return 0;
- }
阅读(923) | 评论(0) | 转发(0) |