1 進程模型
通過fork方式創建一個子進程,由子進程處理一個客戶端連接,此種方式在大併發下非常耗費系統資源,不建議使用
參考代碼 server.c
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define BUF_SIZE 100
int main()
{
//create socket
int iServer = socket(AF_INET, SOCK_STREAM, 0);
if (-1 == iServer)
{
return -1;
}
printf("create socket ok\r\n");
//bind
struct sockaddr_in stServer;
memset(&stServer, 0, sizeof(struct sockaddr_in));
stServer.sin_family = AF_INET;
stServer.sin_port = htons(8888);
//stServer.sin_addr.s_addr = inet_addr("0.0.0.0");
stServer.sin_addr.s_addr = INADDR_ANY;
int iRet = bind(iServer, (struct sockaddr *)&stServer, sizeof(struct sockaddr_in));
if (-1 == iRet)
{
return -1;
}
printf("bind ok\r\n");
//listen
iRet = listen(iServer, 5);
if (-1 == iRet)
{
return -1;
}
printf("listen ok\r\n");
//accept
struct sockaddr_in stClient;
socklen_t tLen = sizeof(struct sockaddr_in);
char buf[BUF_SIZE];
while(1)
{
memset(&stClient, 0, sizeof(struct sockaddr_in));
memset(buf, 0, BUF_SIZE);
int iClient = accept(iServer, (struct sockaddr *)&stClient, &tLen);
if (iClient < 0)
{
continue;
}
printf("accept ok %d %d %s\r\n", iClient, ntohs(stClient.sin_port), inet_ntoa(stClient.sin_addr));
//fork
pid_t pid = fork();
if(pid < 0 )
{
close(iClient);
continue;
}
//recv send
else if(0 == pid)
{
while(1)
{
iRet = recv(iClient, buf, BUF_SIZE, 0);
printf("recv %d\r\n", iRet);
if (iRet > 0)
{
printf("%s\r\n", buf);
send(iClient, buf, BUF_SIZE, 0);
}
else
{
close(iClient);
break;
}
}
}
}
//close
close(iServer);
return 0;
}
2 線程模型
通過創建線程方式,由線程處理一個客戶端連接,此種方式需要對創建線程個數的上線有一定限制,通常使用線程池方式處理併發預先創建好線程,有客戶端連接時,分配給一個客戶端,處理完成後將線程歸還給線程。
此種方式對線程的控制、分配、回收難度較大,建議使用開源代碼
線程併發模型參考 server.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define BUF_SIZE 100
void *ThreadFunc(void *arg)
{
if (NULL == arg)
{
return (void *)NULL;
}
int iClient = *((int *)arg);
free(arg);
if (iClient < 0)
{
return (void *)NULL;
}
char buf[BUF_SIZE];
int iRet = -1;
//recv send
while(1)
{
iRet = recv(iClient, buf, BUF_SIZE, 0);
printf("recv %d\r\n", iRet);
if (iRet > 0)
{
printf("%s\r\n", buf);
send(iClient, buf, BUF_SIZE, 0);
}
else
{
close(iClient);
break;
}
}
}
int main()
{
//create socket
int iServer = socket(AF_INET, SOCK_STREAM, 0);
if (-1 == iServer)
{
return -1;
}
printf("create socket ok\r\n");
//bind
struct sockaddr_in stServer;
memset(&stServer, 0, sizeof(struct sockaddr_in));
stServer.sin_family = AF_INET;
stServer.sin_port = htons(8888);
//stServer.sin_addr.s_addr = inet_addr("0.0.0.0");
stServer.sin_addr.s_addr = INADDR_ANY;
int iRet = bind(iServer, (struct sockaddr *)&stServer, sizeof(struct sockaddr_in));
if (-1 == iRet)
{
return -1;
}
printf("bind ok\r\n");
//listen
iRet = listen(iServer, 5);
if (-1 == iRet)
{
return -1;
}
printf("listen ok\r\n");
//accept
struct sockaddr_in stClient;
socklen_t tLen = sizeof(struct sockaddr_in);
char buf[BUF_SIZE];
while(1)
{
memset(&stClient, 0, sizeof(struct sockaddr_in));
memset(buf, 0, BUF_SIZE);
int iClient = accept(iServer, (struct sockaddr *)&stClient, &tLen);
if (iClient < 0)
{
continue;
}
printf("accept ok %d %d %s\r\n", iClient, ntohs(stClient.sin_port), inet_ntoa(stClient.sin_addr));
int *p = (int *) malloc (sizeof(int));
if (NULL == p)
{
close(iClient);
continue;
}
*p = iClient;
pthread_t tID = 0;
if (0 != pthread_create(&tID, NULL, ThreadFunc, (void *)p))
{
close(iClient);
continue;
}
}
//close
close(iServer);
return 0;
}
3 select模型
推薦使用,但要注意文件描述符是有上限限制的。
參考代碼 server.c
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/select.h>
#include <sys/time.h>
#define BUF_SIZE 100
int main()
{
//socket
int iServer = socket(AF_INET, SOCK_STREAM, 0);
if (-1 == iServer)
{
return -1;
}
printf("socket ok\r\n");
//bind
struct sockaddr_in stServer;
memset(&stServer, 0, sizeof(struct sockaddr_in));
stServer.sin_family = AF_INET;
stServer.sin_port = htons(8866);
stServer.sin_addr.s_addr = INADDR_ANY;
int iRet = bind(iServer, (struct sockaddr *)&stServer, sizeof(struct sockaddr_in));
if (-1 == iRet)
{
return -1;
}
printf("bind ok\r\n");
//listen
iRet = listen(iServer, 5);
if (-1 == iRet)
{
return -1;
}
printf("listen ok\r\n");
//select
fd_set stRSet;
FD_ZERO(&stRSet);
char buf[BUF_SIZE];
FD_SET(iServer, &stRSet);
int iMax = iServer;
while(1)
{
fd_set stTmp = stRSet;
iRet = select(iMax+1, &stTmp, NULL, NULL, NULL);
if (iRet <= 0)
{
continue;
}
int i = 0;
for (; i < (iMax+1); i++)
{
if (FD_ISSET(i, &stTmp))
{
if (i == iServer)
{
int iClient = accept(iServer, NULL, NULL);
if (iClient < 0)
{
continue;
}
FD_SET(iClient, &stRSet);
if (iClient > iMax)
{
iMax = iClient;
}
}
else
{
memset(buf, 0, BUF_SIZE);
iRet = recv(i, buf, BUF_SIZE, 0);
if (iRet > 0)
{
printf("server : %s\r\n", buf);
send(i, buf, BUF_SIZE, 0);
}
else
{
close(i);
FD_CLR(i, &stRSet);
}
}
}
}
}
return 0;
}
注意,在select之前需要使用臨時的fd_set和修改最大描述符最大值
fd_set 本質是一個1024bit位的一個buf,索引爲0-1023,select函數返回時
將對應bit位置1,對應的索引值的描述符可以進行操作,因此,select所支持的最大描述符個數爲1024。
select第一個參數加1的原因是索引從0開始,加一的目的是告訴select函數監聽的描述符集合範圍有多大
while循環中每次對描述符集合重新賦值的原因是select會修改fd_set中的bit位,所以需要保存一個完整的描述符集合,在循環中每次將完整的描述符集合重新賦值個臨時的描述符集合,由select去修改臨時描述符集合。