在Linux下,使用socket编程,通过I/O端口的复用(select函数)实现了基于TCP协议的并发服务器,所谓并发,即可以同时处理数个客户端的连接请求或已建立连接的数据收发服务。
下文中对于select函数的用法已经非常详尽,笔者这里不再赘述。
链接: https://blog.csdn.net/lingfengtengfei/article/details/12392449.
Select函数可以实现I/O端口的复用,笼统来说,select函数使用的总体思路如下:首先我们需要将我们所关心的文件标识符(Linux下一切皆文件,每一个socket建立都会返回一个文件标识符,即一个整数)加入到select函数监测的队列(代码中指fdset)中,一旦这些文件描述符所代表的事件有读、写或异常请求,select函数会返回一个大于0的整数,此时配合使用FD_ISSET(int fd, fd_set *fd_set)函数来判断fd是否有新事件到来。需要说明的是,一旦某个文件描述符fd发生可读事件,FD_ISSET函数会返回1,此时我们可以通过if()条件判断语句进行相应的操作。
接收各个客户端的连接请求和数据服务 代码片
.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
int main()
{
int sockListen, sockConn, index;
int selectfd, client[5];
int i, maxfd, clientid;
fd_set fdset;
char sendbuf[200];
char recvbuf[200];
int DataNum;
struct sockaddr_in addrSrv;
struct sockaddr_in addrClient;
struct timeval timeout = { 0, 1 }; // 分别是秒和毫秒
//创建一个服务器端socket
if ((sockListen = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
perror("socket() failed ");
exit(EXIT_FAILURE);
}
printf("sockListen:%d\n", sockListen);
memset(&addrSrv, 0, sizeof(addrSrv));
addrSrv.sin_addr.s_addr = inet_addr("192.168.***.***");
addrSrv.sin_family = AF_INET;
addrSrv.sin_port = htons(****);
int opt = 1;
if (setsockopt(sockListen, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) != 0)//防止地址占用
{
perror("Server setsockopt failed");
exit(EXIT_FAILURE);
}
if (bind(sockListen, (struct sockaddr*)&addrSrv, sizeof(addrSrv)) == -1)
{
perror(" bind failed ");
exit(EXIT_FAILURE);
}
if (listen(sockListen, 5) == -1) //listen函数将连接的客户端放入队列,参数2为队列长度。
{
perror(" listen failed ");
exit(EXIT_FAILURE);
}
printf("*Start listening \n*Waiting for client's request \n");
maxfd = sockListen;
memset(client, -1, sizeof(client));
while (1)
{
FD_ZERO(&fdset);
FD_SET(sockListen, &fdset);
for (i = 0; i < 5; i++)
{
if (client[i] > 0)
{
FD_SET(client[i], &fdset);
}
}
selectfd = select(maxfd + 1, &fdset, NULL, NULL, NULL);//
if (selectfd == 0)
{
printf("timeout");
exit(EXIT_FAILURE);
}
if (selectfd < 0)
{
perror("select");
exit(EXIT_FAILURE);
}
if (selectfd > 0)
{
for (index = 3; index <= maxfd + 1; index++)//i++
{
if (!FD_ISSET(index, &fdset))
{
continue;
}
if (index == sockListen)
{
socklen_t len = sizeof(addrClient);
memset(&addrClient, 0, sizeof(addrClient));
if ((sockConn = accept(sockListen, (struct sockaddr*)&addrClient, &len)) < 0)//"maxfd+1"
{
perror("accept");
}
else
{
printf("new socket fd:%d\n", sockConn);
for (i = 0; i < 5; i++)
{
if (client[i] < 0)
{
client[i] = sockConn;
break;
}
}
if (sockConn > maxfd)
{
maxfd = sockConn;
}
printf("New connection accepted:Client%d\n", i + 1);
printf("the connected IP: %s,the port :%d\n", inet_ntoa(addrClient.sin_addr), htons(addrClient.sin_port));
break;
}
}
else
{
for (i = 0; i < 5; i++)
{
if (client[i] == index)
{
clientid = i;
break;
}
}
DataNum = recv(index, recvbuf, 200, 0);
if (DataNum == 0)
{
close(index);
FD_CLR(index, &fdset);
client[clientid] = -1;
printf("client%d closed!\n", clientid + 1);
continue;
}
if (strcmp(recvbuf, "quit") == 0)
{
close(index);
FD_CLR(index, &fdset);
client[clientid] = -1;
}
printf("RECV--Client%d:the nummber :%d,the content:%s\n", clientid + 1, DataNum, recvbuf);
memset(recvbuf, 0, strlen(recvbuf));//clear
break;
}
}
}
}
close(sockConn);
close(sockListen);
}
客户端代码较为简单,主要功能发起建立连接请求和数据的简易发送,最后进行功能验证时可以同时启用多个客户端同时进行连接或数据发送代码片
.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
int sockclt;
char sendbuf[200];
char recvbuf[200];
int DataNum;
struct sockaddr_in addrSrv;
memset(&addrSrv, 0, sizeof(addrSrv));//clear addrsrv
addrSrv.sin_addr.s_addr = inet_addr("192.168.***.***");
addrSrv.sin_family = AF_INET;
addrSrv.sin_port = htons(***);
if ((sockclt = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
perror("socket failed");
exit(EXIT_FAILURE);
}
if ((connect(sockclt, (struct sockaddr*)&addrSrv, sizeof(addrSrv))) == -1)
{
perror("connection failed");
}
printf("Connected!\n");
//send
while (1)
{
printf("input your message:");
scanf("%s", sendbuf);
//printf("the message is %s \n",sendbuf);
if (strcmp(sendbuf, "quit") == 0)
{
break;
}
DataNum = send(sockclt, sendbuf, strlen(sendbuf), 0);
printf("the nummber you sent is %d\n", DataNum);
if (DataNum < 0)
{
perror("send failed");
exit(EXIT_FAILURE);
}
}
printf("send stoped!");
close(sockclt);
}