多客户端与服务器数据互发
基于上几节描述,本节进行多客户端与服务器的数据互发。
创建服务器,每当一个新的客户端与服务器建立连接后,创建新的线程用于与客户端进行数据交互与处理;每个客户端同样创建线程,主线程用于发数据,子线程用于读数据
server端
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h> //IP转换函数
#include <ctype.h> //toupper函数头文件
#include <string.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <signal.h>
#include <netinet/in.h>
#include <pthread.h> //线程头文件
#include "wrap.h" //
#define MAXLINE 800 //client.c与server.c中的数据传输的buf的size
#define INET_ADDRSTRLEN 16
#define SERV_PORT 6666
struct s_info{ //定义一个结构体,将客户端的地址与connfd进行捆绑
struct sockaddr_in cliaddr;
int connfd;
};
//服务器端写数据
void *do_work_back(void* arg_back)
{
int n,i;
struct s_info *ts_back=(struct s_info*)arg_back;
char buf[MAXLINE];
char str[INET_ADDRSTRLEN];
while(1)
{
fgets(buf,MAXLINE,stdin);
printf("Data to client ip: %s,port: %d\n",inet_ntop(AF_INET,&ts_back->cliaddr.sin_addr.s_addr,str, sizeof(str)),ntohs(ts_back->cliaddr.sin_port));
Write(ts_back->connfd,buf,MAXLINE); //写回客户端
}
Close(ts_back->connfd);
pthread_exit(0);
}
//服务器端读数据
void *do_work(void* arg)
{
int n,i;
struct s_info *ts=(struct s_info*)arg;
char buf[MAXLINE];
char str[INET_ADDRSTRLEN];
while(1)
{
n=Read(ts->connfd,buf,MAXLINE);
if(n==0)
{
printf("The [client ip: %s,port: %d] has beens closed!\n",inet_ntop(AF_INET,&ts->cliaddr.sin_addr.s_addr,str, sizeof(str)),ntohs(ts->cliaddr.sin_port));
break;
}
//show client IP+PORT
printf("Data from client ip: %s,port: %d\n",inet_ntop(AF_INET,&ts->cliaddr.sin_addr.s_addr,str, sizeof(str)),ntohs(ts->cliaddr.sin_port));
printf("Data from client is:%s",buf);
}
Close(ts->connfd);
pthread_exit(0);
}
int main(void)
{
struct sockaddr_in servaddr, cliaddr;
socklen_t cliaddr_len;
int listenfd, connfd;
char buf[MAXLINE];
char str[MAXLINE];
int i, n;
pthread_t tid,tid_back; //定义pthread_t型的变量
struct s_info ts[100]; //创建结构体数组,设置可接收的客户端上限
i=0;
listenfd = Socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);
Bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
Listen(listenfd, 20);
printf("Accepting connections ...\n");
cliaddr_len=sizeof(cliaddr);
while(1)
{
connfd=Accept(listenfd,(struct sockaddr *)&cliaddr,&cliaddr_len);
//每收到一个客户端的链接,将该建立连接的客户端的地址与创建的文件描述符锁定至创建的结构体数组ts[i]中,并作为另起线程的参数
ts[i].cliaddr=cliaddr;
ts[i].connfd=connfd;
pthread_create(&tid,NULL,do_work,(void*)&ts[i]); //将ts[i]作为参数传递给子线程do_work()函数
pthread_detach(tid); //将线程设置成分离的,线程运行结束后会自动释放所有资源
pthread_create(&tid_back,NULL,do_work_back,(void*)&ts[i]);
pthread_detach(tid_back);
i++; //起下一线程
}
pthread_exit(0);
}
client 端
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <pthread.h> //线程头文件
#include "wrap.h"
#define MAXLINE 800
#define SERV_PORT 6666
struct s_info{ //定义一个结构体,将客户端的地址与connfd进行捆绑
struct sockaddr_in servaddr;
int sockfd;
};
void *do_work_client(void *arg)
{
int n;
char buf[MAXLINE];
char str[INET_ADDRSTRLEN];
struct s_info *ts=(struct s_info *)arg; //将(void *)类型的数据强转成(struct s_info型)
while(1)
{
n=Read(ts->sockfd,buf,MAXLINE);
if(n==0)
{
printf("The server has beens closed\n");
break;
}
printf("Data from server:%s",buf);
memset(buf,0,sizeof(buf));
}
Close(ts->sockfd);
pthread_exit(0);
}
int main(void)
{
struct sockaddr_in servaddr;
char buf[MAXLINE];
int sockfd;
pthread_t tid; //定义pthread_t型变量
struct s_info ts;
sockfd = Socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
inet_pton(AF_INET, "127.127.0.1", &servaddr.sin_addr);
servaddr.sin_port = htons(SERV_PORT);
Connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
ts.servaddr=servaddr;
ts.sockfd=sockfd;
//将服务器端的地址及客服端的文件描述符作为参数传递给子线程do_work_client()函数
pthread_create(&tid,NULL,do_work_client,(void*)&ts); //ts的数据以void的类型进行数据传输
pthread_detach(tid); //将线程设置成分离的,线程运行结束后会自动释放所有资源
while(1)
{
fgets(buf,sizeof(buf),stdin);
Write(sockfd,buf, strlen(buf));
}
Close(sockfd);
return 0;
}
编译
//生成可执行文件server
gcc server.c wrap.c -o server -lpthread
//生成可执行文件client
gcc client.c wrap.c -o client -lpthread