0%

C语言多个线程与客户端通信的服务器

C语言多个线程与客户端通信的服务器

  • 代码结构:将与客户端收发信息的部分单独取出放入新的线程中
  • 捕获服务器收到的Ctrl+c信号(也就是SIGINT),将此信号绑定一个自定义的信号处理函数(为了防止程序被中断但是没有关闭socket描述符)
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <net/if.h>
#include <sys/ioctl.h>

#include <pthread.h>
#include <signal.h>


#define SERVERPORT 8080

int sockfd;

/*linux上支持(Android上也支持), 此函数不仅能获取IP,还可以获取MAC地址、掩码和广播地址等*/
int get_local_ip_using_ifconf(char *str_ip)
{
int sock_fd, intrface;
struct ifreq buf[INET_ADDRSTRLEN];
struct ifconf ifc;
char *local_ip = NULL;
int status = -1;

if ((sock_fd = socket(AF_INET, SOCK_DGRAM, 0)) >= 0)
{
ifc.ifc_len = sizeof(buf);
ifc.ifc_buf = (caddr_t)buf;
if (!ioctl(sock_fd, SIOCGIFCONF, (char *)&ifc))
{
intrface = ifc.ifc_len/sizeof(struct ifreq);
while (intrface-- > 0)
{
if (!(ioctl(sock_fd, SIOCGIFADDR, (char *)&buf[intrface])))
{
local_ip = NULL;
local_ip = inet_ntoa(((struct sockaddr_in*)(&buf[intrface].ifr_addr))->sin_addr);
if(local_ip)
{
strcpy(str_ip, local_ip);
status = 0;
if(strcmp("127.0.0.1", str_ip))
{
break;
}
}

}
}
}
close(sock_fd);
}
return status;
}

void* clientRoutine(void * clientFd)
{
int* clientFdt = (int*)clientFd;
char recvbuf[512];
int ret;
for (;;)
{
// 接收缓冲区清零
memset(recvbuf, 0x0, sizeof(recvbuf));
// 读数据
ret = recv(*clientFdt, recvbuf, sizeof(recvbuf), 0);
if (0 >= ret)
{
puts("client disconnected");
close(*clientFdt);
pthread_exit(NULL);
}
// 将读取到的数据以字符串形式打印出来
printf("from client: %s\n", recvbuf);

if (0 == strncmp("exit", recvbuf, 4))
{
printf("server exit...\n");
close(*clientFdt);
pthread_exit(NULL);
}
}
}

void SIGINTHandler(int sig)
{
close(sockfd);
printf("\nServer stopping, already closed socket %d. \n", sockfd);
exit(EXIT_SUCCESS);
}

int main()
{
struct sockaddr_in server_addr = {0};
struct sockaddr_in client_addr = {0};
char ip_str[20] = {0};

int addrlen = sizeof(client_addr);
int ret;
char localIPBuf[20];

signal(SIGINT, (sig_t)SIGINTHandler);

/* 打开套接字,得到套接字描述符 */
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (0 > sockfd)
{
perror("socket error");
exit(EXIT_FAILURE);
}

/* 将套接字与指定端口号进行绑定 */
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
server_addr.sin_port = htons(SERVERPORT);

char buf[20];
get_local_ip_using_ifconf(buf);
puts(buf);


inet_ntop(AF_INET, &(server_addr.sin_addr), localIPBuf, sizeof(localIPBuf));
puts(localIPBuf);

ret = bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
if (0 > ret)
{
perror("bind error");
close(sockfd);
exit(EXIT_FAILURE);
}
/* 使服务器进入监听状态 */
ret = listen(sockfd, 50);
if (0 > ret)
{
perror("listen error");
close(sockfd);
exit(EXIT_FAILURE);
}
/* 阻塞等待客户端连接 */
while(1)
{
int clientTemp;
clientTemp = accept(sockfd, (struct sockaddr *)&client_addr, &addrlen);
if (0 > clientTemp)
{
perror("accept error");
close(sockfd);
exit(EXIT_FAILURE);
}
printf("有客户端接入...\n");
inet_ntop(AF_INET, &client_addr.sin_addr.s_addr, ip_str, sizeof(ip_str));
printf("客户端主机的 IP 地址: %s\n", ip_str);
printf("客户端进程的端口号: %d\n", client_addr.sin_port);
pthread_t thTemp;
pthread_create(&thTemp, NULL, clientRoutine, &clientTemp);
/* 接收客户端发送过来的数据 */

}

/* 关闭套接字 */
close(sockfd);
exit(EXIT_SUCCESS);
}

  • 效果
  • image-20220206222037573