}
void DieWithSystemMessage(const char *msg) { perror(msg); exit(1);
}
/****************************************************************************** 文件名:TCPServerUtility.c
******************************************************************************/ #include
static const int MAXPENDING = 5; //常量不是宏定义
int SetupTCPServerSocket(const char *service) { struct addrinfo addrCriteria; // Criteria for address match memset(&addrCriteria, 0, sizeof(addrCriteria)); // Zero out structure
addrCriteria.ai_family = AF_UNSPEC; // Any address family
addrCriteria.ai_flags = AI_PASSIVE; // Accept on any address/port addrCriteria.ai_socktype = SOCK_STREAM; // Only stream sockets addrCriteria.ai_protocol = IPPROTO_TCP; // Only TCP protocol struct addrinfo *servAddr; // List of server addresses
int rtnVal = getaddrinfo(NULL, service, &addrCriteria, &servAddr); if (rtnVal != 0) DieWithUserMessage(\ int servSock = -1; struct addrinfo *addr;
for (addr = servAddr; addr != NULL; addr = addr->ai_next) { servSock = socket(addr->ai_family, addr->ai_socktype,addr->ai_protocol); if (servSock < 0) continue; if ((bind(servSock, addr->ai_addr, addr->ai_addrlen) == 0) &&(listen(servSock, MAXPENDING) == 0)) {
struct sockaddr_storage localAddr; socklen_t addrSize = sizeof(localAddr); if (getsockname(servSock, (struct sockaddr *) &localAddr, &addrSize) < 0) DieWithSystemMessage(\ break; } close(servSock); servSock = -1; } freeaddrinfo(servAddr); return servSock; }
int AcceptTCPConnection(int servSock) {
int clntSock; /* 客户连接返回的套接字 */ struct sockaddr_in echoClntAddr; /* 客户地址*/ unsigned int clntLen;
clntLen = sizeof(echoClntAddr);
/* 等待客户端连接 */
if ((clntSock = accept(servSock, (struct sockaddr *) &echoClntAddr,&clntLen)) < 0) { printf(\ exit(1); }
printf(\ return clntSock; }
void HandleTCPClient(int clntSocket) { char buffer[BUFSIZE]; ssize_t numBytesRcvd = recv(clntSocket, buffer, BUFSIZE, 0); if (numBytesRcvd < 0) DieWithSystemMessage(\ while (numBytesRcvd > 0)
{ //将收到的信息返回给客户端 if(send(clntSocket, buffer, numBytesRcvd, 0) < 0) DieWithSystemMessage(\ numBytesRcvd = recv(clntSocket, buffer, BUFSIZE, 0); if (numBytesRcvd < 0) DieWithSystemMessage(\ }
close(clntSocket); }
/***************************************************************************** 文件名:AddressUtility.c
******************************************************************************/ #include
void PrintSocketAddress(const struct sockaddr *address, FILE *stream) { if (address == NULL || stream == NULL) return;
void *numericAddress; char addrBuffer[INET6_ADDRSTRLEN]; in_port_t port; switch (address->sa_family) {
case AF_INET: //IPV4 numericAddress = &((struct sockaddr_in *) address)->sin_addr; port = ntohs(((struct sockaddr_in *) address)->sin_port); break;
case AF_INET6: //IPV6 numericAddress = &((struct sockaddr_in6 *) address)->sin6_addr; port = ntohs(((struct sockaddr_in6 *) address)->sin6_port); break;
default: fputs(\
return; } if (inet_ntop(address->sa_family, numericAddress, addrBuffer, sizeof(addrBuffer)) == NULL) fputs(\ else { fprintf(stream, \往流中写字符串 if (port != 0) fprintf(stream, \ //u%表示无符号整数输出 } }
bool SockAddrsEqual(const struct sockaddr *addr1, const struct sockaddr *addr2) { if (addr1 == NULL || addr2 == NULL) return addr1 == addr2;
else if (addr1->sa_family != addr2->sa_family) return false;
else if (addr1->sa_family == AF_INET) { struct sockaddr_in *ipv4Addr1 = (struct sockaddr_in *) addr1; struct sockaddr_in *ipv4Addr2 = (struct sockaddr_in *) addr2; return ipv4Addr1->sin_addr.s_addr == ipv4Addr2->sin_addr.s_addr && ipv4Addr1->sin_port == ipv4Addr2->sin_port; }
else if (addr1->sa_family == AF_INET6) { struct sockaddr_in6 *ipv6Addr1 = (struct sockaddr_in6 *) addr1; struct sockaddr_in6 *ipv6Addr2 = (struct sockaddr_in6 *) addr2; return memcmp(&ipv6Addr1->sin6_addr, &ipv6Addr2->sin6_addr,sizeof(struct in6_addr)) == 0 && ipv6Addr1->sin6_port == ipv6Addr2->sin6_port; } else return false; }
/****************************************************************************** 文件名:TCPEchoServer-Thread.c
******************************************************************************/ #include
#include
void *ThreadMain(void *arg); //线程块
struct ThreadArgs { int clntSock; };
int main(int argc, char *argv[]) { if(argc != 2) DieWithSystemMessage(\运行命令和参数错误!\
char *service = argv[1];
unsigned int processLimit = atoi(argv[2]);
int servSock = SetupTCPServerSocket(service); if ( servSock<0)
DieWithSystemMessage(\创建套接字失败!\
for(;;) { int clntSock = AcceptTCPConnection(servSock); struct ThreadArgs *threadArgs = (struct ThreadArgs *) malloc(sizeof(struct ThreadArgs)); if(threadArgs == NULL) DieWithSystemMessage(\分配内存失败!\ threadArgs->clntSock = clntSock; pthread_t threadID; int returnValue = pthread_create(&threadID, NULL, ThreadMain, threadArgs); if(returnValue != 0) DieWithSystemMessage(\ printf(\ } }
void *ThreadMain(void *threadArgs) { pthread_detach( pthread_self() );
int clntSock = ((struct ThreadArgs *)threadArgs)->clntSock; free(threadArgs); HandleTCPClient(clntSock); return NULL; }
3)运行结果 首先编译链接完成后产生serv和 cli两个程序,编译链接服务器和客户端程序的指令请大家参照前面的案例给出。完成编译链接后先启动服务器程序serv,如图3所示,然后再运行客户端程序,如图4所示。服务器打印子进程ID,客户端收到服务器返回的信息。可以同时运行多个客户端程序。建议客户端和服务器运行于不同的物理机器上。
图3、服务器运行界面
图4、客户端运行界面
3、思考题
1)请使用多线程实现服务器和客户端之间不受发送接收顺序约束的“聊天”。 2)简要描述父线程与子线程之间的关系。
三、小结
本次实验使我们熟悉Linux线程的一些基本概念以及多进程程序与多线程程序的区别,熟悉了如何创建线程,熟悉了函数pthread_create的用法以及多线程的特点。特别要注意非标准库函数的编译链接方法。