多线程并发服务器
服务端
#include <stdio.h>
#include <string.h>
#include <sys/errno.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <ctype.h>
#include <pthread.h>
#define PORT 8888
#define BUFFSIZE 1024
#define ERRORCODE -1
void *RW_Work(void *arg)
{
int *afd = (int *)arg;
while (1)
{
int i;
char recv_buf[BUFFSIZE];
int n = read(*afd, recv_buf, sizeof(recv_buf));
if (n == 0)
{
perror("connect interrupt");
break;
}
fputs(recv_buf, stdout);
for (i = 0; i < n; i++)
{
recv_buf[i] = toupper(recv_buf[i]);
}
write(*afd, recv_buf, sizeof(recv_buf));
}
close(*afd);
pthread_exit(NULL);
}
int main(int argc, char *argv[])
{
char buf[BUFSIZ], client_addr_INFO[BUFFSIZE];
int accept_fd, listen_fd;
socklen_t client_len;
struct sockaddr_in listen_addr, accept_addr;
char buffer[BUFFSIZE];
int buffer_len, i;
int on = 1;
pthread_t tid;
// 创建socket
listen_fd = socket(AF_INET, SOCK_STREAM, 0);
if (listen_fd == -1)
{
printf("创建socket error: %s \n", strerror(errno));
return ERRORCODE;
}
// bind
if (setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)))
{
printf("setsockopt error: %s \n", strerror(errno));
return ERRORCODE;
}
listen_addr.sin_family = AF_INET;
listen_addr.sin_port = htons(PORT);
listen_addr.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(listen_fd, (struct sockaddr *)&listen_addr, sizeof(listen_addr)) < 0)
{
printf("bind error: %s \n", strerror(errno));
return ERRORCODE;
}
// listen
if (listen(listen_fd, 5) == -1)
{
printf("listen error: %s \n", strerror(errno));
return ERRORCODE;
}
printf("服务端创建成功,等待连接\n");
// accept
while (1)
{
client_len = sizeof(accept_addr);
/* code */
accept_fd = accept(listen_fd, (struct sockaddr *)&accept_addr, &client_len);
if (accept_fd == -1)
{
printf("accept error: %s \n", strerror(errno));
return ERRORCODE;
}
printf("accept ip : %s,port : %d\n",
inet_ntop(AF_INET, &accept_addr.sin_addr.s_addr, client_addr_INFO, sizeof(client_addr_INFO)),
ntohs(accept_addr.sin_port));
//printf("accept ip : %s,port : %d\n", inet_ntoa(accept_addr.sin_addr), ntohs(accept_addr.sin_port));
pthread_create(&tid, NULL, RW_Work, (void *)&accept_fd);
pthread_detach(tid);
}
//close(accept_fd);
close(listen_fd);
return 0;
}
代码运行
为什么线程库需要在编译时显式链接,而其它的库不需要呢?
因为多线程的支持通常在运行时才需要,因此需要在链接时特别指定。
这就要说到大多数标准C或C++库(如libc、libstdc++)在编译器启动时就自动被链接了。
线程创建
创建时机
服务端在accept成功之后,创建一个线程,将接收到的accept_fd交给线程处理,线程通过这个accept_fd与客户端进行读写操作。
创建线程函数原型
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
参数意义
pthread_t *thread
指向线程标识符的指针
const pthread_attr_t *attr
const pthread_attr_t *attr:一个指向pthread_attr_t类型对象的指针,用于设置线程属性。如果值为NULL,则使用默认属性。通过修改这个属性对象,可以改变新线程的某些特性,如堆栈大小、调度参数等
void *(*start_routine) (void *)
指向线程运行函数的指针。这个函数是新线程的入口点,它接受一个指向void的指针作为参数,通常用于传递线程所需的数据
void *arg
传递给start_routine函数的参数。这个参数可以是任何类型,但在调用start_routine时,它会被转换为void *类型
函数功能描述
The pthread_create() function starts a new thread in the calling
process. The new thread starts execution by invoking start_routine();
arg is passed as the sole argument of start_routine().
这个函数总体来说是在一个正在调用的进程中创建一个新的线程,而这个线程会invoking(借助)所指定的start_routine(函数),arg作为这个函数的唯一参传递给start_routine。
函数返回值
On success, pthread_create() returns 0; on error, it returns an error
number, and the contents of *thread are undefined.
这个意思就是说成功创建会返回0,失败会返回一个错误代码,这是一个int型数据,不同的数字代表了不同的错误类型。
错误代码
例如:
EAGAIN:通常是11号错误,表示“资源暂时不可用”,在这个上下文中,它指的是无法创建新线程因为资源限制。
EINVAL:通常是22号错误,表示“无效的参数”,意味着某个函数调用传递了一个无效的参数。
EPERM:通常是1号错误,表示“操作不允许”,通常用于表示没有足够的权限执行某个操作。