服務器代碼:
// SelectTest.cpp : 定義控制臺應用程式的入口點。
//
#include "stdafx.h"
//#include <windows.h>
//#include <WinSock2.h>
#include <WS2tcpip.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <io.h>
#include <process.h>
//#pragma comment(lib, "ws2_32.lib")
#define IPADDRESS "127.0.0.1"
#define PORT 8787
#define MAXLINE 1024
#define LISTENQ 5
#define STDIN_FILENO 0
#define STDOUT_FILENO 1
#define STDERR_FILENO 2
//函式宣告
//創建套接字并進行系結
static int socket_bind(const char* ip, int port);
//IO多用復用
static void do_select(int listenfd);
//處理多個連接
static void handle_connection(int (*connfds)[64], int & num, fd_set * prset, fd_set * pallset);
int _tmain(int argc, _TCHAR* argv[])
{
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
int listenfd, connfd, sockfd;
struct sockaddr_in cliaddr;
socklen_t cliaddrlen;
listenfd = socket_bind(IPADDRESS, PORT);
listen(listenfd, LISTENQ);
do_select(listenfd);
WSACleanup();
getchar();
return 0;
}
static int socket_bind(const char* ip, int port)
{
int listenfd;
struct sockaddr_in servaddr;
listenfd = socket(AF_INET, SOCK_STREAM, 0);
if(listenfd == -1)
{
perror("socket error:");
getchar();
exit(1);
}
memset(&servaddr,0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
inet_pton(AF_INET, ip, &servaddr.sin_addr); //點分十進制轉二進制127.0.0.1轉0x100007f
servaddr.sin_port = htons(port);
if(bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1)
{
int a = WSAGetLastError();
perror("bind error: ");
exit(1);
}
return listenfd;
}
static void do_select(int listenfd)
{
int connfd, sockfd;
struct sockaddr_in cliaddr;
socklen_t cliaddrlen;
fd_set rset, allset;
int maxfd = 0, maxi;
int i;
int clientfds[FD_SETSIZE]; //保存客戶端連接描述符
int nready;
//初始化客戶連接描述符
for(i = 0; i < FD_SETSIZE; ++i)
clientfds[i] = -1;
maxi = -1;
FD_ZERO(&allset);
//添加監聽描述符
FD_SET(listenfd, &allset);
maxfd = listenfd;
//回圈處理
for(;;)
{
rset = allset;
struct timeval tv;
tv.tv_sec = tv.tv_usec = 0;
//獲取可用描述符的個數
nready = select(maxfd+1, &rset, NULL, NULL, &tv);
if (nready == -1)
{
perror("select error;");
exit(1);
}
//測驗監聽描述符是否準備好
if(FD_ISSET(listenfd, &rset))
{
cliaddrlen = sizeof(cliaddr);
//接受新的連接
if((connfd = accept(listenfd, (struct sockaddr*)&cliaddr, &cliaddrlen)) == -1)
{
if(errno == EINTR)
{
continue;
}
else
{
perror("accept error:");
exit(1);
}
}
fprintf(stdout, "accept a new client: %s:%d\n", inet_ntoa(cliaddr.sin_addr), cliaddr.sin_port);
//將新的連接描述符添加到陣列中
for (i = 0; i < FD_SETSIZE; ++i)
{
if (clientfds[i] < 0)
{
clientfds[i] = connfd;
break;
}
}
if(i == FD_SETSIZE)
{
fprintf(stderr, "too many clients.\n");
exit(1);
}
//將新的描述符添加到讀描述符集合中
FD_SET(connfd, &allset);
//描述符個數
maxfd = (connfd > maxfd ? connfd : maxfd);
//記錄客戶連接套接字的個數
maxi = (i > maxi ? i : maxi)+1;
if(--nready <= 0)
continue;
}
//處理客戶連接
handle_connection(&clientfds, maxi, &rset, &allset);
}
}
static void handle_connection(int (*connfds)[64], int & num, fd_set *prset, fd_set * pallset)
{
int i, n;
char buf[MAXLINE];
memset(buf, 0, MAXLINE);
for(i = 0; i < num; ++i)
{
if (*connfds[i] < 0)
continue;
//測驗客戶描述符是否準備好
if(FD_ISSET(*connfds[i], prset))
{
//接收客戶端發送的訊息
n = recv(*connfds[i], buf, MAXLINE, 0);
if (n == 0)
{
_close(*connfds[i]);
FD_CLR(*connfds[i], pallset);
*connfds[i] = -1;
num = num - 1;
continue;
}
else if (n == -1)
{
int a = WSAGetLastError();
FD_CLR(*connfds[i], pallset);
*connfds[i] = -1;
num = num -1;
continue;
}
printf("read msg is: ");
_write(STDOUT_FILENO, buf, n);
//向客戶端發送buf
send(*connfds[i], buf, n, 0);
}
}
}
客戶端代碼:
// SelectTestClient.cpp : 定義控制臺應用程式的入口點。
//
#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
//#include <Windows.h>
#include <io.h>
#include <WS2tcpip.h>
#define MAXLINE 1024
#define IPADDRESS "127.0.0.1"
#define SERV_PORT 8787
#define STDIN_FILENO 0
#define STDOUT_FILENO 1
#define STDERR_FILENO 2
//#define max(a, b) (a > b) ? a : b
static void handle_connection(int sockfd);
int _tmain(int argc, _TCHAR* argv[])
{
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
int sockfd;
struct sockaddr_in servaddr;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
int timeout = 1000;
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout));
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(SERV_PORT);
inet_pton(AF_INET, IPADDRESS, &servaddr.sin_addr);
connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr));
//
handle_connection(sockfd);
WSACleanup();
return 0;
}
static void handle_connection(int sockfd)
{
char sendline[MAXLINE], recvline[MAXLINE];
int maxfdp, stdineof;
fd_set rset;
int n;
FD_ZERO(&rset);
for(;;)
{
//添加標準輸入描述符
FD_SET(STDIN_FILENO, &rset);
//添加連接描述符
FD_SET(sockfd, &rset);
maxfdp = max(STDIN_FILENO, sockfd);
//進行輪詢
select(maxfdp+1, &rset, NULL, NULL, NULL);
//測驗連接套接字是否準備好
if(FD_ISSET(sockfd, &rset))
{
n = recv(sockfd, recvline, MAXLINE, 0);
auto a = WSAGetLastError();
if(n == 0)
{
fprintf(stderr, "client: server is closed.\n");
_close(sockfd);
FD_CLR(sockfd, &rset);
}
else if (n == -1)
{
fprintf(stderr, "client: server is closed2.\n");
}
else
_write(STDOUT_FILENO, recvline, n);
}
//測驗標準輸入是否準備好
if(FD_ISSET(STDIN_FILENO, &rset))
{
n = _read(STDIN_FILENO, sendline, MAXLINE);
if(n == 0)
{
FD_CLR(STDIN_FILENO, &rset);
continue;
}
send(sockfd, sendline, n, 0);
}
}
}
多個客戶端都可以連接到服務器,但服務器select之后,呼叫FD_ISSET只能處理第一個客戶端的發送來的資料,其他的都沒反應,小弟實在不知道是何原因,請各位大神指教。
各位大神,小弟有完美主義傾向,只會把分給能解決問題的一個或多人,如果沒有滿意答案,絕對會選擇無滿意結貼,希望各位大神不要浪費自己的時間說些沒用的話。~~
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/153592.html
標籤:網絡通信
