多线程实现
需求:
1、实现服务器与客户端的通信,服务器可接收多个客户端。
2、服务器发送消息时,所有客户端都可收到。
3、客户端发送信息时,只有服务器可收到。
4、服务器发送“bye”时,包括服务器程序及所有客户端程序都结束,客户端发送“bye”时,本客户端结束(当然,服务器对应的线程也会结束)。
5、服务器要在输出客户端消息时,要连同对应客户端的IP一起输出。
对于上述需求,对应序号的解决方法如下:
1. 选用TCP(UDP也可)进行通信,主进程监听并创建与客户端对应的线程
2. 分为main(),发送信息线程处理函数,接受信息线程处理函数。
具体:
只要有一个接受客户端,就要创建服务器发送信息线程,为了编写的方便,也可没有接受客户端就创建服务器发送信息线程,只要服务器端有输入,就要向每个客户端发送,这就要求服务器发送线程能对主进程创建的所有客户端socket文件描述符,进行操作(写操作)。所以全局变量需包括所有socket描述符(也可在main中定义而传地址)。但是,如何判断某个socket文件描述符是否关闭、可用,就要在初始时,将socket文件描述符置为0或负数,并且当对应的客户端线程关闭前,需要close(socket)后,置socket为0或负数。当服务器端读取到字符串后,就遍历整个socke数组,判断是否大于0,大于就发送信息到此socket描述符。
3. 正常解决
4. 在客户端、服务器端发送完信息后,都将此信息与”bye”比较,是“bye“就退出。
5. 在服务器accept一个客户端后,需要将得到的客户端地址IP记录,并传递给将要创建的与此客户端交互的线程
6. 由2、5可得到,传递给服务器接受信息线程的参数为:socket文件描述符,IP
//server.c
#include
#include
#include //struct
sockaddr_in
#include
#include
#include
#include
#include
#include
#include
#define PORT 5000
#define A(x) (struct sockaddr*)(&x)
#define ACCEPT_NUM 5
pthread_t m_id;//send massage's pthread_id
pthread_t id[ACCEPT_NUM];//thread num
struct thread_arg{
int fd; //指向同一个文件描述符
char ip[100];
};
struct thread_arg arg[ACCEPT_NUM];//向recv_func传递的参数类型
char server_massage[1024]={0};
//int thread_num=0;//已经存在的线程数
//recv_ massage
void* recv_func(void* p)
{
int i=(int)p;
char buf[1000];
int readn=0;
for(;;){
memset(buf,0,sizeof(buf));
readn=read(arg[i].fd,buf,sizeof(buf)-1);//读取客户端来的信息
if(readn<=0) break;//出错或者对方关闭就退出
buf[readn] = '\0';
printf("clientip-%s :
%s\n",arg[i].ip,buf);
if(strncmp(buf,"bye",3)==0) break;
}
close(arg[i].fd);
arg[i].fd=0;//说明对应描述符已经关闭,易于send_massage线程的判断
}
void* send_massage(void*para){
int nread=-1;
int i=0;
while((nread=read(0,server_massage,sizeof(server_massage)))){
server_massage[nread]='\0';
for(i=0;i
if(arg[i].fd>0){
write(arg[i].fd,server_massage,strlen(server_massage)+1);
}
}
if(strncmp(server_massage,"bye",3)== 0)
{
exit(0);
}
}
}
int main()
{
int server_sock;
server_sock = socket(AF_INET, SOCK_STREAM, 0);//建立套接字
struct sockaddr_in si;
si.sin_family = AF_INET;//跟前面的一致
si.sin_addr.s_addr = INADDR_ANY;//0,本机的任何ip
si.sin_port = htons(PORT);
if(bind(server_sock, A(si),
sizeof(si))<0){
printf("%s\n",strerror(errno));exit(1);
}
pthread_create(&m_id,NULL,send_massage,NULL);
listen(server_sock, 5);
int j=0;
for(j=0;j
{
socklen_t len = sizeof(si);
arg[j].fd =
accept(server_sock,A(si),&len);//等待客户端连接
if(arg[j].fd<0)
{
perror("accept failed");
return -1;
}
//成功就返回新套接字
//welcome friend from
char buf[100], ip[100];
inet_ntop(si.sin_family,&si.sin_addr,ip,sizeof(ip));
printf("%s到此一游\n", ip);
sprintf(buf,"welcome friend from
\n",
ip,ntohs(si.sin_port));
write(arg[j].fd,buf,strlen(buf));
memcpy(arg[j].ip,ip,100);
pthread_create(&id[j],NULL,recv_func,(void*)j);
// thread_num++;
}
pthread_join(m_id,NULL);
}
//client
//client
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define PORT 5000 // The port
which is communicate with server
#define LENGTH 1024 // Buffer length
void* send_massage(void *);
void* recv_massage(void* );
int main(int argc, char *argv[])
{
int sockfd;
// Socket
file descriptor
int num;
// Counter of received bytes
int
recv_pid;
int send_pid;
struct sockaddr_in
remote_addr; // Host address
information
if (argc != 2)
{
printf ("Usage: client SERVER_IP (ex: ./client
192.168.0.94).\n");
return (0);
}
if ((sockfd =
socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
printf("ERROR: Failed to obtain Socket
Descriptor!\n");
return (0);
}
remote_addr.sin_family =
AF_INET; //
Protocol Family
remote_addr.sin_port =
htons(PORT); // Port
number
inet_pton(AF_INET,
argv[1], &remote_addr.sin_addr); // Net
Address
if (connect(sockfd,
(struct sockaddr *)&remote_addr,
sizeof(struct sockaddr)) == -1)
{
printf ("ERROR: Failed to connect to the
host!\n");
return (0);
}
else
{
printf ("OK: Have connected to the
%s\n",argv[1]);
}
pthread_create(&send_pid,NULL,send_massage,(void*)sockfd);
pthread_create(&recv_pid,NULL,recv_massage,(void*)sockfd);
pthread_join(send_pid,NULL);
pthread_join(recv_pid,NULL);
return (0);
}
//thread_to recv massaga
void* recv_massage(void* arg)
{
char revbuf[LENGTH]={0}; // Receive buffer
int sockfd=(int )arg;
int num;
memcpy(revbuf,"hello",6);
while
(strncmp(revbuf,"bye",3) != 0) // Check remoter command
{
memset(revbuf,0,LENGTH);
num = read(sockfd, revbuf, LENGTH);
if(num<=0){
exit(0);
}
revbuf[num]='\0';
printf("server: %s \n",revbuf);
}
exit(0);
}
//thread to send massage
void* send_massage(void *arg)
{
int fd=(int)arg;
char sendbuf[LENGTH]={0};
int nread=0;
while(1){
printf("please input some word:\n");
//scanf("%s",sendbuf);
nread=read(0,sendbuf,LENGTH);
if(nread<=0)
{
exit(0);
}
sendbuf[nread]='\0';
write(fd,sendbuf,strlen(sendbuf)+1);
if(strncmp(sendbuf,"bye",3)==0)
{
exit(0);
}
}
}