Linux select TCP并发服务器与客户端编程
介紹:運行在ubuntu linux系統,需要先打開一個終端運行服務端代碼,這時,可以打開多個終端同時運行多個客戶端代碼(注意客戶端數目要小于MAX_FD);在客戶端輸入數據后回車,可以看見服務器收到數據,并回復客戶端確認信息,客戶端輸入:exit,按回車,該客戶端關閉,在服務器端顯示退出信息;所有客戶端關閉后,服務器不會自動關閉,需要按ctrl+c強制關閉。
服務器端代碼:
#include <stdio.h>?
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/select.h>
#define SERV_PORT 8888
#define SERV_IP "127.0.0.1" ? ? ? ? //本地回環接口
#define LIST 20 ? ? ? ? ? ? ? ? ? ? ? ? ? ? //服務器最大接受連接
#define MAX_FD 10 ? ? ? ? ? ? ? ? ? ? ?//FD_SET支持描述符數量
int main(void)
{
? ? ? ? int sockfd;
? ? ? ? int err;
? ? ? ? int i;
? ? ? ? int connfd;
? ? ? ? int fd_all[MAX_FD]; //保存所有描述符,用于select調用后,判斷哪個可讀
//下面兩個備份原因是select調用后,會發生變化,再次調用select前,需要重新賦值
? ? ? ? fd_set fd_read; ? ? ? ? ? ? ? ? ? ? ?//FD_SET數據備份
? ? ? ? fd_set fd_select; ? ? ? ? ? ? ? ? ? ?//用于select
? ? ? ? struct timeval timeout; ? ? ? ? ? //超時時間備份
? ? ? ? struct timeval timeout_select; ?//用于select
? ? ? ? struct sockaddr_in serv_addr; ? //服務器地址
? ? ? ? struct sockaddr_in cli_addr; ? ? ?//客戶端地址
? ? ? ? socklen_t serv_len;
? ? ? ? socklen_t cli_len;
? ? ? ?//超時時間設置
? ? ? ? timeout.tv_sec = 10;
? ? ? ? timeout.tv_usec = 0;
? ? ? ? sockfd = socket(AF_INET, SOCK_STREAM, 0);
? ? ? ? if(sockfd < 0)
? ? ? ? {
? ? ? ? ? ? ? ? perror("fail to socket");
? ? ? ? ? ? ? ? exit(1);
}
? ? ? ? memset(&serv_addr, 0, sizeof(serv_addr));
? ? ? ? memset(&cli_addr, 0, sizeof(cli_addr));
? ? ? ? serv_addr.sin_family = AF_INET;
? ? ? ? serv_addr.sin_port = htons(SERV_PORT);
? ? ? ? serv_addr.sin_addr.s_addr = inet_addr(SERV_IP);
? ? ? ? serv_len = sizeof(serv_addr);
? ? ? ? err = bind(sockfd, (struct sockaddr *)&serv_addr, serv_len);
? ? ? ? if(err < 0)
? ? ? ? {
? ? ? ? ? ? ? ? perror("fail to bind");
? ? ? ? ? ? ? ? exit(1);
? ? ? ? }
? ? ? ? err = listen(sockfd, LIST);
? ? ? ? if(err < 0)
? ? ? ? {
? ? ? ? ? ? ? ? perror("fail to listen");
? ? ? ? ? ? ? ? exit(1);
? ? ? ? }
? ? ? ? //初始化fd_all數組
? ? ? ? memset(&fd_all, -1, sizeof(fd_all));
? ? ? ? fd_all[0] = sockfd; ? //第一個為監聽套接字
? ? ? ? FD_ZERO(&fd_read);
? ? ? ? FD_SET(sockfd, &fd_read); ?//將監聽套接字加入fd_set
? ? ? ? char buf[1024]; ?//讀寫緩沖區
? ? ? ? int num;
? ? ? ? int maxfd;
? ? ? ? maxfd = fd_all[0]; ?//監聽的最大套接字
? ? ? ??
? ? ? ? while(1)
? ? ? ? {
? ? ? ? ? ? ? ? //每次都需要重新賦值
? ? ? ? ? ? ? ? fd_select = fd_read;
? ? ? ? ? ? ? ? timeout_select = timeout;
// ? ? ? ? ? ? ?err = select(maxfd+1, &fd_select, NULL, NULL, NULL);
? ? ? ? ? ? ? ? err = select(maxfd+1, &fd_select, NULL, NULL, (struct timeval *)&timeout_select);
? ? ? ? ? ? ? ? if(err < 0)
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ? ? perror("fail to select");
? ? ? ? ? ? ? ? ? ? ? ? exit(1);
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? if(err == 0)
? ? ? ? ? ? ? ? ? ? ? ? printf("timeout\n");
? ? ? ? ? ? ? ?
//檢測監聽套接字是否可讀
? ? ? ? ? ? ? ? if(FD_ISSET(sockfd, &fd_select))
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ? ? //可讀,證明有新客戶端連接服務器 ? ? ?
? ? ? ? ? ? ? ? ? ? ? ? cli_len = sizeof(cli_addr);
? ? ? ? ? ? ? ? ? ? ? ? connfd = accept(sockfd, (struct sockaddr *)&cli_addr, &cli_len);
? ? ? ? ? ? ? ? ? ? ? ? if(connfd < 0)
? ? ? ? ? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? perror("fail to accept");
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? exit(1);
? ? ? ? ? ? ? ? ? ? ? ? }
//將新連接套接字加入fd_all及fd_read
? ? ? ? ? ? ? ? ? ? ? ? for(i=0; i<MAX_FD; i++)
? ? ? ? ? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? if(fd_all[i] != -1)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? continue;
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? else
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? fd_all[i] = connfd;
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? printf("client fd_all[%d] join\n", i);
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? break;
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? ? ? FD_SET(connfd, &fd_read);
? ? ? ? ? ? ? ? ? ? ? ? if(maxfd < connfd)
? ? ? ? ? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? maxfd = connfd; ?//更新maxfd
? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? //從1開始查看連接套接字是否可讀,因為上面已經處理過0(sockfd)
? ? ? ? ? ? ? ? for(i=1; i<maxfd; i++)
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ? ? if(FD_ISSET(fd_all[i], &fd_select))
? ? ? ? ? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? printf("fd_all[%d] is ok\n", i);
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? num = read(fd_all[i], buf, 1024);
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? if(num > 0)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? if(strncmp("exit", buf, 4) == 0)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? {
//客戶端退出,關閉套接字,并從監聽集合清除
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? printf("client:fd_all[%d] exit\n", i);
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? FD_CLR(fd_all[i], &fd_read);
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? close(fd_all[i]);
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? fd_all[i] = -1;
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? continue;
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? }
//收到 客戶端數據并打印
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? buf[num] = '\0';
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? printf("receive buf from client fd_all[%d] is: %s\n", i, buf);
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? }
//回復客戶端
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? num = write(fd_all[i], "ok", sizeof("ok"));
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? if(num < 0)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? perror("fail to write ");
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? exit(1);
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? else
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? printf("send reply\n");
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? ? ? else
? ? ? ? ? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //printf("no data\n"); ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? }
? ? ? ? }
? ? ? ? return 0;
}
客戶端代碼:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define SERV_PORT 8888
#define SERV_IP "127.0.0.1"
int main(void)
{
? ? ? ? int sockfd;
? ? ? ? int err;
? ? ? ? int connfd;
? ? ? ? struct sockaddr_in serv_addr;
? ? ? ? struct sockaddr_in cli_addr;
? ? ? ? socklen_t serv_len;
? ? ? ? socklen_t cli_len;
? ? ? ? sockfd = socket(AF_INET, SOCK_STREAM, 0);
? ? ? ? if(sockfd < 0)
? ? ? ? {
? ? ? ? ? ? ? ? perror("fail to socket");
? ? ? ? ? ? ? ? exit(1);
? ? ? ? }
? ? ? ? memset(&serv_addr, 0, sizeof(serv_addr));
? ? ? ? memset(&cli_addr, 0, sizeof(cli_addr));
? ? ? ? serv_addr.sin_family = AF_INET;
? ? ? ? serv_addr.sin_port = htons(SERV_PORT);
? ? ? ? serv_addr.sin_addr.s_addr = inet_addr(SERV_IP);
? ? ? ? serv_len = sizeof(serv_addr);
//客戶端不需要綁定,直接連接即可
? ? ? ? err = connect(sockfd, (struct sockaddr *)&serv_addr, serv_len);
? ? ? ? if(err < 0)
{
? ? ? ? ? ? ? ? perror("fail to bind");
? ? ? ? ? ? ? ? exit(1);
? ? ? ? }
? ? ? ? char buf[1024];
? ? ? ? int num;
? ? ? ? while(1)
? ? ? ? {
? ? ? ? ? ? ? ? sleep(2);
? ? ? ? ? ? ? ? num = read(STDIN_FILENO, buf, 1024);
? ? ? ? ? ? ? ? if(num > 0)
? ? ? ? ? ? ? ? {
//exit代表退出
? ? ? ? ? ? ? ? ? ? ? ? if(strncmp("exit", buf, 4) == 0)
? ? ? ? ? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? write(sockfd, buf, num);
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? break;
? ? ? ? ? ? ? ? ? ? ? ? }
? }
? ? ? ? ? ? ? ? ? ? ? ? write(sockfd, buf, num);
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? num = read(sockfd, buf, 1024);
? ? ? ? ? ? ? ? if(num > 0)
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ? ? buf[num] = '\0';
? ? ? ? ? ? ? ? ? ? ? ? printf("server reply: %s\n", buf);
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? else
? ? ? ? ? ? ? ? ? ? ? ? printf("error to read\n");
? ? ? ? }
? ? ? ? close(sockfd);
? ? ? ? return 0;
}
?
轉載于:https://www.cnblogs.com/suncoolcat/p/3292297.html
與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的Linux select TCP并发服务器与客户端编程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 微软BI 之SSAS 系列 - 在SQL
- 下一篇: HDU-1874