c-并发tcp客户端-服务器程序和迭代程序之间的区别是什么



我想学习如何用C编写并发TCP客户端服务器,但我无法理解迭代服务器客户端程序和并发服务器客户端程序之间的区别。在互联网上我找不到太多信息。我在www.geeksfoeks.org网站上查看了TCP客户端服务器的C实现,但我认为这是一个迭代的例子。我如何使它并发?

TCP服务器:

#include <netdb.h> 
#include <netinet/in.h> 
#include <stdlib.h> 
#include <string.h> 
#include <sys/socket.h> 
#include <sys/types.h> 
#define MAX 80 
#define PORT 8080 
#define SA struct sockaddr 
// Function designed for chat between client and server. 
void func(int sockfd) 
{ 
char buff[MAX]; 
int n; 
// infinite loop for chat 
for (;;) { 
bzero(buff, MAX); 
// read the message from client and copy it in buffer 
read(sockfd, buff, sizeof(buff)); 
// print buffer which contains the client contents 
printf("From client: %st To client : ", buff); 
bzero(buff, MAX); 
n = 0; 
// copy server message in the buffer 
while ((buff[n++] = getchar()) != 'n') 
; 
// and send that buffer to client 
write(sockfd, buff, sizeof(buff)); 
// if msg contains "Exit" then server exit and chat ended. 
if (strncmp("exit", buff, 4) == 0) { 
printf("Server Exit...n"); 
break; 
} 
} 
} 
// Driver function 
int main() 
{ 
int sockfd, connfd, len; 
struct sockaddr_in servaddr, cli; 
// socket create and verification 
sockfd = socket(AF_INET, SOCK_STREAM, 0); 
if (sockfd == -1) { 
printf("socket creation failed...n"); 
exit(0); 
} 
else
printf("Socket successfully created..n"); 
bzero(&servaddr, sizeof(servaddr)); 
// assign IP, PORT 
servaddr.sin_family = AF_INET; 
servaddr.sin_addr.s_addr = htonl(INADDR_ANY); 
servaddr.sin_port = htons(PORT); 
// Binding newly created socket to given IP and verification 
if ((bind(sockfd, (SA*)&servaddr, sizeof(servaddr))) != 0) { 
printf("socket bind failed...n"); 
exit(0); 
} 
else
printf("Socket successfully binded..n"); 
// Now server is ready to listen and verification 
if ((listen(sockfd, 5)) != 0) { 
printf("Listen failed...n"); 
exit(0); 
} 
else
printf("Server listening..n"); 
len = sizeof(cli); 
// Accept the data packet from client and verification 
connfd = accept(sockfd, (SA*)&cli, &len); 
if (connfd < 0) { 
printf("server acccept failed...n"); 
exit(0); 
} 
else
printf("server acccept the client...n"); 
// Function for chatting between client and server 
func(connfd); 
// After chatting close the socket 
close(sockfd); 
} 

TCP客户端:

#include <netdb.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <sys/socket.h> 
#define MAX 80 
#define PORT 8080 
#define SA struct sockaddr 
void func(int sockfd) 
{ 
char buff[MAX]; 
int n; 
for (;;) { 
bzero(buff, sizeof(buff)); 
printf("Enter the string : "); 
n = 0; 
while ((buff[n++] = getchar()) != 'n') 
; 
write(sockfd, buff, sizeof(buff)); 
bzero(buff, sizeof(buff)); 
read(sockfd, buff, sizeof(buff)); 
printf("From Server : %s", buff); 
if ((strncmp(buff, "exit", 4)) == 0) { 
printf("Client Exit...n"); 
break; 
} 
} 
} 
int main() 
{ 
int sockfd, connfd; 
struct sockaddr_in servaddr, cli; 
// socket create and varification 
sockfd = socket(AF_INET, SOCK_STREAM, 0); 
if (sockfd == -1) { 
printf("socket creation failed...n"); 
exit(0); 
} 
else
printf("Socket successfully created..n"); 
bzero(&servaddr, sizeof(servaddr)); 
// assign IP, PORT 
servaddr.sin_family = AF_INET; 
servaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); 
servaddr.sin_port = htons(PORT); 
// connect the client socket to server socket 
if (connect(sockfd, (SA*)&servaddr, sizeof(servaddr)) != 0) { 
printf("connection with the server failed...n"); 
exit(0); 
} 
else
printf("connected to the server..n"); 
// function for chat 
func(sockfd); 
// close the socket 
close(sockfd); 
} 

如注释所述,迭代服务器将按顺序处理每个连接。在您发布的示例中,如果您运行服务器(在忽略一系列警告之后),则只能连接一个客户端。第二个客户端将成功连接服务器,但服务器永远不会对其做出响应。只有第一个客户端会发送消息并获得响应。并发实现将并行处理两个客户端,并且能够从这两个客户端获取消息。我可以想出3种方法来做到这一点,但其中一种是不推荐的:

  • 如注释中所建议的,将accept调用放入循环中。它将被阻止,直到有人连接。当有人连接时,您生成一个线程,并为它提供与客户端通信所需的所有信息,文件描述符(sockfd)应该足以作为一个简单的示例。之后线程可以执行你的func,因为这个线程只知道一个客户端,但主循环会再次阻塞accept,等待另一个连接。我在谷歌上找到了这个例子
  • 使用POSIX系统调用select(或者它的替代pollepoll),可以监视套接字的活动列表。如果任何受监视的套接字都有活动,则它的readaccept不会阻塞,然后处理这些套接字。如果服务器文件描述符有活动,则意味着连接了新的客户端,您应该将客户端文件描述符存储在某个位置,并开始将其传递给select。如果客户端文件描述符有活动,则可以像以前一样处理它们的消息。这种方式不需要线程,所有事情都在主例程上处理,根据您的用例可能会有优势我在谷歌上找到了这两个例子:这里和这里
  • 不建议使用这种方式:使用fcntl使服务器套接字不阻塞。这样,accept就不会阻塞,而是会立即返回一个错误EAGAINEWOULDBLOCK。然后,您可以在主循环上重复调用accept,大多数时候它会返回一个错误,但不返回的错误会发出新客户端连接的信号。当客户端连接时,您也可以使其文件描述符不阻塞,并将其存储在某个地方。在您的主例程中,对于每个客户端套接字,您将在它们上尝试read,但如果没有新的信息可用,它也会返回一个错误。如果有可用的东西,你会像以前一样对待它。这具有总是要求100%CPU利用率的缺点。您可以在循环中插入延迟,但这会增加额外的不必要的延迟。你不应该这样做

最新更新