C 如何在线程之间共享变量?
我正在开发一个服务器客户端项目,该项目允许服务器交换消息 与一名客户单独会面。但是,我必须修改服务器,以便在服务器发送消息时,将消息发送到所有连接的客户端 我知道这涉及到在线程之间共享变量,但我不知道该怎么做 任何提示/指导都将不胜感激 服务器代码:C 如何在线程之间共享变量?,c,multithreading,sockets,pthreads,shared-variable,C,Multithreading,Sockets,Pthreads,Shared Variable,我正在开发一个服务器客户端项目,该项目允许服务器交换消息 与一名客户单独会面。但是,我必须修改服务器,以便在服务器发送消息时,将消息发送到所有连接的客户端 我知道这涉及到在线程之间共享变量,但我不知道该怎么做 任何提示/指导都将不胜感激 服务器代码: #include<stdio.h> #include<string.h> //strlen #include<stdlib.h> //strlen #include<sys/socket.h&g
#include<stdio.h>
#include<string.h> //strlen
#include<stdlib.h> //strlen
#include<sys/socket.h>
#include<arpa/inet.h> //inet_addr
#include<unistd.h> //write
#include<pthread.h> //for threading , link with lpthread
#define MAX 80
#define PORT 6543
#define SA struct sockaddr
// Function designed for chat between client and server.
void join( pthread_t *thread_id)
{
if(!pthread_join( *thread_id , NULL))
printf("thread %ld complted\n",*thread_id);
pthread_exit(0);//to exit the current thread
}
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: %s\t 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;
pthread_t thread_id,jointhread_id;
// 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 = 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
while(connfd = accept(sockfd, (SA*)&cli, &len))
{
if (connfd < 0) {
printf("server acccept failed...\n");
exit(0);
}
else
printf("server acccept the client..%d.\n",connfd);
if( pthread_create( &thread_id , NULL , func , (void*) &connfd) < 0)
{
perror("could not create thread");
return 1;
}
if( pthread_create( &jointhread_id , NULL , join , (void*) &thread_id) < 0)
{
perror("could not create thread");
return 1;
}
}
// After chatting close the socket
close(sockfd);
}
#包括
#包括//strlen
#包括//strlen
#包括
#包括//inet\u addr
#包括//写入
#包括//对于线程,使用lpthread链接
#定义最大值80
#定义端口6543
#定义SA结构sockaddr
//为客户端和服务器之间的聊天而设计的功能。
无效连接(pthread\u t*thread\u id)
{
如果(!pthread_join(*thread_id,NULL))
printf(“线程%ld complted\n”,*线程id);
pthread_exit(0);//退出当前线程
}
无效函数(int*sockfd)
{
字符buff[MAX];
int n;
//无限循环聊天
对于(;;){
bzero(buff,MAX);
//从客户端读取消息并将其复制到缓冲区中
读取(*sockfd,buff,sizeof(buff));
//包含客户端内容的打印缓冲区
printf(“从客户端:%s\t到客户端:”,buff);
bzero(buff,MAX);
n=0;
//在缓冲区中复制服务器消息
而((buff[n++]=getchar())!='\n');
//并将该缓冲区发送到客户端
写入(*sockfd,buff,sizeof(buff));
//如果消息包含“退出”,则服务器退出并结束聊天。
如果(strncmp(“退出”,buff,4)==0){
printf(“服务器退出…\n”);
打破
}
}
}
//驱动函数
int main()
{
int sockfd、connfd、len;
servaddr中的struct sockaddru,cli;
pthread_t thread_id,jointhread_id;
//套接字创建和验证
sockfd=套接字(AF_INET,SOCK_STREAM,0);
如果(sockfd==-1){
printf(“套接字创建失败…\n”);
出口(0);
}
其他的
printf(“套接字已成功创建..\n”);
bzero(&servaddr,sizeof(servaddr));
//分配IP、端口
servaddr.sin_family=AF_INET;
servaddr.sin\u addr.s\u addr=INADDR\u ANY;
servaddr.sinu端口=htons(端口);
//将新创建的套接字绑定到给定的IP和验证
if((bind(sockfd,(SA*)&servaddr,sizeof(servaddr)))!=0){
printf(“套接字绑定失败…\n”);
出口(0);
}
其他的
printf(“套接字成功绑定..\n”);
//现在服务器已准备好侦听和验证
如果((听(sockfd,5))!=0){
printf(“侦听失败…\n”);
出口(0);
}
其他的
printf(“服务器侦听..\n”);
len=sizeof(cli);
//接受来自客户端的数据包并进行验证
while(connfd=accept(sockfd,(SA*)&cli和len))
{
如果(connfd<0){
printf(“服务器访问失败…\n”);
出口(0);
}
其他的
printf(“服务器接受客户端..%d.\n”,connfd);
if(pthread_create(&thread_id,NULL,func,(void*)&connfd)<0)
{
perror(“无法创建线程”);
返回1;
}
if(pthread_create(&jointhread_id,NULL,join,(void*)和thread_id)<0)
{
perror(“无法创建线程”);
返回1;
}
}
//聊天结束后,关闭插座
关闭(sockfd);
}
不幸的是,有很多错误
线程函数的错误原型
如我在顶部评论中提到的connfd
的竞态条件(将connfd
作为指针传递给func
)
执行getchar
会从读取sockfd
我在下面制作了一个版本来说明这一点
但是,为了使代码更接近您所声明的目的,需要进行大量的重构。下面还有第二个版本说明了我对此的看法
这里有一个带注释的版本,显示了bug和一些修复(主要是为了让它干净地编译而做的) 它用
#if ORIG
包装原始代码,用#if FIX
包装新代码,每个地方都有一条关于它正在修复的bug的注释
#include <stdio.h>
#include <string.h> // strlen
#include <stdlib.h> // strlen
#include <sys/socket.h>
#include <arpa/inet.h> // inet_addr
#include <unistd.h> // write
#include <pthread.h> // for threading , link with lpthread
#define MAX 80
#define PORT 6543
#define SA struct sockaddr
#define ORIG 0
#define FIX 1
// Function designed for chat between client and server.
// NOTE/BUG -- the main thread has to join the thread
#if ORIG
void
join(pthread_t *thread_id)
{
if (!pthread_join(*thread_id, NULL))
printf("thread %ld complted\n", *thread_id);
// to exit the current thread
pthread_exit(0);
}
#endif
// NOTE/BUG: this is the _wrong_ signature for a thread function
#if ORIG
void
func(int *sockfd)
#else
void *
func(void *ptr)
#endif
{
#if FIX
int sockfd = (long) ptr;
#endif
char buff[MAX];
int n;
// infinite loop for chat
for (;;) {
bzero(buff, MAX);
// read the message from client and copy it in buffer
// NOTE/BUG: this has a race condition
// NOTE/BUG: we need the actual length
#if ORIG
read(*sockfd, buff, sizeof(buff));
#else
int rlen = read(sockfd, buff, sizeof(buff));
#endif
// print buffer which contains the client contents
printf("From client: %s\t To client : ", buff);
bzero(buff, MAX);
// copy server message in the buffer
// NOTE/BUG: this is destroying the data that was
#if ORIG
n = 0;
while ((buff[n++] = getchar()) != '\n');
#endif
// and send that buffer to client
#if ORIG
write(*sockfd, buff, sizeof(buff));
#else
write(sockfd, buff, rlen);
#endif
// if msg contains "Exit" then server exit and chat ended.
if (strncmp("exit", buff, 4) == 0) {
printf("Server Exit...\n");
break;
}
}
// NOTE/BUG: we must return the error code
#if FIX
return (void *) 0;
#endif
}
// Driver function
int
main()
{
int sockfd, connfd, len;
struct sockaddr_in servaddr, cli;
pthread_t thread_id, jointhread_id;
// 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");
// assign IP, PORT
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = 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");
// Accept the data packet from client and verification
len = sizeof(cli);
// NOTE/BUG: connfd can be zero for a valid connection
#if ORIG
while (connfd = accept(sockfd, (SA *) &cli, &len)) {
#else
while (1) {
connfd = accept(sockfd, (SA *) &cli, &len);
#endif
if (connfd < 0) {
printf("server acccept failed...\n");
exit(0);
}
else
printf("server acccept the client..%d.\n", connfd);
#if ORIG
if (pthread_create(&thread_id, NULL, func, (void *) &connfd) < 0) {
perror("could not create thread");
return 1;
}
#else
if (pthread_create(&thread_id, NULL, func, (void *) ((long) connfd)) < 0) {
perror("could not create thread");
return 1;
}
#endif
// NOTE/BUG -- creating a separate thread just to join the above thread does
// not help
#if ORIG
if (pthread_create(&jointhread_id, NULL, join, (void *) &thread_id) < 0) {
perror("could not create thread");
return 1;
}
#endif
}
// After chatting close the socket
close(sockfd);
return 0;
}
旁注:您的代码中有一个竞争条件。这是因为您正在将connfd
作为&connfd
传递。由于线程正在执行*sockfd
,如果accept
返回足够快,则connfd
[inmain
]中的值可能会更改为后续线程的值。线程将看到sockfd
发生不可预测的变化。修复方法是按值传递connfd
。使用:pthread_create(…,(void*)connfd)执行此操作代码>并将func
更改为:void func(void*ptr){int sockfd=(int)ptr;…}
#include <stdio.h>
#include <string.h> // strlen
#include <stdlib.h> // strlen
#include <sys/socket.h>
#include <arpa/inet.h> // inet_addr
#include <unistd.h> // write
#include <pthread.h> // for threading , link with lpthread
#define MAX 80
#define PORT 6543
#define SA struct sockaddr
#define ORIG 0
#define FIX 1
enum {
TSKSTATE_IDLE, // task slot free/available
TSKSTATE_PENDING, // task is being created
TSKSTATE_RUNNING, // task is alive and running
TSKSTATE_DONE // task has completed (but not reaped)
};
typedef struct tsk tsk_t;
struct tsk {
tsk_t *tsk_next; // chain pointer
pthread_t tsk_tid; // thread id
long tsk_xid; // sequential task id
int tsk_sockfd; // client socket descriptor
int tsk_state; // current task state
pthread_mutex_t tsk_mutex; // per-thread mutex
void *tsk_rtn; // thread's return value
};
// NOTE: using an array obviates the need for a master lock if we used a
// linked list here instead -- by passing TSKMAX to listen below [in main],
// we guarantee that main can always find a free task slot when it needs one
#define TSKMAX 5
tsk_t tsklist[TSKMAX]; // active task list
#define TSKFORALL(_tsk) \
_tsk = &tsklist[0]; _tsk < &tsklist[TSKMAX]; ++_tsk
long tskxid; // sequential task id
__thread tsk_t *tskcur; // current thread's tsk block
pthread_mutex_t master_lock = PTHREAD_MUTEX_INITIALIZER;
// lockall -- lock all threads
void
lockall(void)
{
pthread_mutex_lock(&master_lock);
}
// unlockall -- lock all threads
void
unlockall(void)
{
pthread_mutex_unlock(&master_lock);
}
// tsklock -- lock single thread
void
tsklock(tsk_t *tsk)
{
pthread_mutex_lock(&tsk->tsk_mutex);
}
// tskunlock -- unlock single thread
void
tskunlock(tsk_t *tsk)
{
pthread_mutex_unlock(&tsk->tsk_mutex);
}
// tskreapall -- release all completed threads
void
tskreapall(void)
{
tsk_t *tsk;
lockall();
for (TSKFORALL(tsk)) {
tsklock(tsk);
if (tsk->tsk_state == TSKSTATE_DONE) {
pthread_join(tsk->tsk_tid,&tsk->tsk_rtn);
tsk->tsk_state = TSKSTATE_IDLE;
}
tskunlock(tsk);
}
}
// tsksendall -- send message to all other clients
void
tsksendall(char *msg,int len)
{
tsk_t *tsk;
lockall();
for (TSKFORALL(tsk)) {
if (tsk == tskcur)
continue;
tsklock(tsk);
if (tsk->tsk_state == TSKSTATE_RUNNING)
write(tsk->tsk_sockfd,msg,len);
tskunlock(tsk);
}
}
void *
func(void *ptr)
{
tskcur = ptr;
char buff[MAX];
#if ORIG
int n;
#endif
tsklock(tskcur);
tskcur->tsk_state = TSKSTATE_RUNNING;
tskunlock(tskcur);
// infinite loop for chat
// NOTE: this loop still needs work ...
for (;;) {
bzero(buff, MAX);
// read the message from client and copy it in buffer
int rlen = read(tskcur->tsk_sockfd, buff, sizeof(buff));
// print buffer which contains the client contents
printf("From client: %s\t To client : ", buff);
bzero(buff, MAX);
// copy server message in the buffer
// NOTE/BUG: this is destroying the data that was
#if ORIG
n = 0;
while ((buff[n++] = getchar()) != '\n');
#endif
// and send that buffer to client
write(tskcur->tsk_sockfd, buff, rlen);
// if msg contains "Exit" then server exit and chat ended.
if (strncmp("exit", buff, 4) == 0) {
printf("Server Exit...\n");
break;
}
// echo message to all other clients
tsksendall(buff,rlen);
}
tsklock(tskcur);
tskcur->tsk_state = TSKSTATE_DONE;
close(tskcur->tsk_sockfd);
tskcur->tsk_sockfd = -1;
tskunlock(tskcur);
return (void *) 0;
}
// Driver function
int
main(void)
{
int sockfd, connfd;
socklen_t len;
struct sockaddr_in servaddr, cli;
int state;
tsk_t *tsktry;
tsk_t *tsknew;
// 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");
// assign IP, PORT
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = 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, TSKMAX)) != 0) {
printf("Listen failed...\n");
exit(0);
}
else
printf("Server listening..\n");
// Accept the data packet from client and verification
len = sizeof(cli);
for (TSKFORALL(tsktry)) {
pthread_mutex_init(&tsktry->tsk_mutex,NULL);
tsktry->tsk_state = TSKSTATE_IDLE;
}
while (1) {
connfd = accept(sockfd, (SA *) &cli, &len);
if (connfd < 0) {
printf("server acccept failed...\n");
exit(0);
}
else
printf("server acccept the client..%d.\n", connfd);
// reap all completed threads
tskreapall();
// find an idle slot
tsknew = NULL;
for (TSKFORALL(tsktry)) {
tsklock(tsktry);
state = tsktry->tsk_state;
if (state == TSKSTATE_IDLE) {
tsknew = tsktry;
tsknew->tsk_state = TSKSTATE_PENDING;
}
tskunlock(tsktry);
if (tsknew != NULL)
break;
}
tsknew->tsk_xid = ++tskxid;
tsknew->tsk_sockfd = connfd;
if (pthread_create(&tsknew->tsk_tid, NULL, func, tsknew) < 0) {
perror("could not create thread");
return 1;
}
}
// After chatting close the socket
close(sockfd);
return 0;
}