- socket();
- bind();
- liste();
- while(1)
- {
- accept();
- while(1)
- {
- recv
- ret==0;
- break;
- }
- close(acceptfd);
- }
- close(sockfd);
同一时刻相应多个客户端(tcp)。多进程模型/多线程模型/IO多路复用(select、poll、epoll)
- socket();
- bind();
- listen()
- while(1)
- {
- accept();
- if(fork()==0)
- {
- while(1)
- {
- recv
- ret==0;
- break;
- }
- close(acceptfd);
- exit();
- }
- else
- {}
-
- }
注意:收到客户端消息后,打印下是来自哪个客户端的数据(来电显示)
使用SIGCHLD来处理子进程结束的信号,信号函数中回收进程资源。

- /*服务器创建代码 */
- #include <stdio.h>
- #include <sys/types.h> /* See NOTES */
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <netinet/ip.h> /* superset of previous */
- #include <arpa/inet.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <string.h>
- #include <signal.h>
- #include <sys/types.h>
- #include <sys/wait.h>
-
- void handler(int arg)
- {
- waitpid(-1, NULL, WNOHANG);
- }
- int main(int argc, char const *argv[])
- {
- if (argc < 2)
- {
- printf("plase input
\n" ); - return -1;
- }
- //1.创建套接字,用于链接
- int sockfd;
- sockfd = socket(AF_INET, SOCK_STREAM, 0);
- if (sockfd < 0)
- {
- perror("socket err");
- return -1;
- }
- printf("sockfd:%d\n", sockfd);
- //2.绑定 ip+port 填充结构体
- struct sockaddr_in saddr;
- saddr.sin_family = AF_INET; //协议族ipv4
- saddr.sin_port = htons(atoi(argv[1])); //端口号,htons将无符号短整数hostshort从主机字节顺序到网络字节顺序。
- saddr.sin_addr.s_addr = inet_addr("0.0.0.0"); //ip地址,转化为16进制表示
- socklen_t len = sizeof(saddr); //结构体大小
- //bind绑定ip和端口
- if (bind(sockfd, (struct sockaddr *)&saddr, len) < 0)
- {
- perror("bind err");
- return -1;
- }
- printf("bind success\n");
- //3.启动监听,把主动套接子变为被动套接字
- if (listen(sockfd, 6) < 0)
- {
- perror("listen err");
- return -1;
- }
- printf("listen success\n");
- //4.阻塞等待客户端的链接请求
- int acceptfd;
- char buf[64];
- int ret;
- while (1)
- {
- acceptfd = accept(sockfd, (struct sockaddr *)&saddr, &len);
- //获取客户端的ip和端口,(struct sockaddr *)&saddr:用来存放返回的ip,和端口
- if (acceptfd < 0)
- {
- perror("accept err");
- return -1;
- }
- printf("client ip:%s ,port:%d\n", inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
- printf("connect success\n");
- signal(SIGCHLD, handler);
- pid_t pid = fork(); //创建子进程
- if (pid < 0)
- {
- perror("fork err");
- return -1;
- }
- else if (pid == 0)
- {
- while (1)
- {
- ret = recv(acceptfd, buf, sizeof(buf), 0);
- if (strncmp(buf, "quit", 4) == 0) //接收到quit退出
- {
- break;
- }
- if (ret < 0)
- {
- perror("recv err.");
- return -1;
- }
- else if (ret == 0) //客户端退出
- {
- printf("client exit\n");
-
- break;
- }
- else
- {
- printf("buf:%s\n", buf);
- }
- }
- close(acceptfd);//关闭子进程的文件描述副
- close(sockfd);//关闭子进程的套接字文件描述符,不影响主进程套接字
- exit(0);
- }
- close(acceptfd);//关闭主进程打开的文件描述符,
- //为下次循环开辟的文件描述符空位置,否则只能连续开辟文件描述符到1024个
- }
- close(sockfd);
- return 0;
- }
-
每来一个客户端连接,开一个子线程来专门处理客户端的数据,实现简单,占用资源较少,属于使用比较广泛的模型:
- socket();
- bind();
- listen();
- while(1)
- {
- accept();
- pthread_creat();
- pthread_detach();
- }
-
- #include <stdio.h>
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <netinet/ip.h>
- #include <arpa/inet.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <pthread.h>
-
- void *mythread(void *arg)
- {
- int acceptfd= *((int *)arg);
- int ret;
- char buf[128];
- while (1)
- {
- ret=recv(acceptfd,buf,sizeof(buf),0);
- if (ret < 0)
- {
- perror("recv err.");
- return -1;
- }else if (ret ==0)
- {
- printf("%d client exit\n",acceptfd);
- close(acceptfd);
- break;
- }else
- {
- printf("buf:%s\n",buf);
- }
- }
- pthread_exit(NULL);
- }
-
-
- int main(int argc, char const *argv[])
- {
-
- if (argc != 2)
- {
- printf("please input %s
\n" , argv[0]); - return -1;
- }
- // 1.创建流式套接字
- int sockfd = socket(AF_INET, SOCK_STREAM, 0); // 链接
- if (sockfd < 0)
- {
- perror("socket err.");
- return -1;
- }
- printf("sockfd:%d\n", sockfd); // 3
- // 填充ipv4的通信结构体
-
- struct sockaddr_in saddr, caddr;
- saddr.sin_family = AF_INET;
- saddr.sin_port = htons(atoi(argv[1])); //"8888" int a= atoi("8888")//a=8888
- // saddr.sin_addr.s_addr = inet_addr(argv[1]);
-
- // 设置服务器自动获取自己主机的ip
- // saddr.sin_addr.s_addr = htonl(INADDR_ANY);//INADDR_ANY 0x00000000 "0.0.0.0"
- // saddr.sin_addr.s_addr = INADDR_ANY;
- saddr.sin_addr.s_addr = inet_addr("0.0.0.0");
-
- socklen_t len = sizeof(caddr);
-
- // 2.绑定套接字 ip和端口(自己)
- if (bind(sockfd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0)
- {
- perror("bind err.");
- return -1;
- }
- printf("bind ok.\n");
-
- // 3.监听
- if (listen(sockfd, 5) < 0)
- {
- perror("listen err.");
- return -1;
- }
- printf("listen ok.\n");
- while (1)
- {
- int acceptfd = accept(sockfd, (struct sockaddr *)&caddr, &len);
- if (acceptfd < 0) // 4
- {
- perror("accept err.");
- return -1;
- }
- printf("acceptfd=%d\n", acceptfd);
- printf("ip=%s port=%d\n", inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port));
- pthread_t tid;
- pthread_create(&tid,NULL,mythread,&acceptfd);
- pthread_detach(tid);//游离态
- }
- close(sockfd);
- return 0;
- }
-
借助select、poll、epoll机制,将新连接的客户端描述符增加到描述符表中,只需要一个线程即可处理所有的客户端连接,在嵌入式开发中应用广泛,不过代码写起了稍显繁琐。