多线程并发服务器
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <pthread.h>
#define PORT 8888 //1024~49151
#define IP "192.168.124.128"
#define ERR_MSG(msg) {fprintf(stderr,"%d\n",__LINE__);\
perror(msg);\
}
void *deal_ser_msg(void *arg);
typedef struct
{
int newfd;
struct sockaddr_in cin;
}msg;
int main(int argc, const char *argv[])
{
//服务器
//创建套接字tcp
int sfd=socket(AF_INET,SOCK_STREAM,0);
if(sfd<0)
{
ERR_MSG("socket");
}
printf("流式套接字创建成功\n");
//允许端口快速复用
int reuse = 1;
if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0)
{
ERR_MSG("setsockopt");
return -1;
}
printf("允许端口快速重用\n");
//填充服务器的地址信息结构体,bind 使用
//真实的地址信息结构体根据地址族,AF_INET ---->man 7 ip
struct sockaddr_in sin;
sin.sin_family=AF_INET; //必须填AF_INET
sin.sin_port=htons(PORT); //点分十进制转网络字节序,端口号的网络字节序。
sin.sin_addr.s_addr=inet_addr(IP); //点分十进制转网络字节序,本机IP。
//绑定服务器的IP和端口号,服务器必须绑定
if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin))<0)
{
ERR_MSG("bind");
return -1;
}
printf("绑定成功\n");
//将套接字设置为被动监听状态
if(listen(sfd,128)<0)
{
ERR_MSG("listen");
return -1;
}
printf("监听成功\n");
while(1)
{
//获取链接成功后的套接字,创建新的文件描述符
struct sockaddr_in cin;
socklen_t addrlen=sizeof(cin);
int newfd=accept(sfd,(struct sockaddr*)&cin,&addrlen);
if(newfd<0)
{
ERR_MSG("accept");
return -1;
}
printf("[ %s : %d ]newfd=%d 客户端链接成功\n",\
inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),newfd);
msg info;
info.newfd=newfd;
info.cin=cin;
//创建线程
pthread_t tid;
pthread_create(&tid,NULL,deal_ser_msg,&info);
pthread_detach(tid);
}
//关闭套接字/关闭文件描述符
if(close(sfd)<0)
{
ERR_MSG("close");
return -1;
}
return 0;
}
void *deal_ser_msg(void *arg)
{
int newfd=((msg*)arg)->newfd;
struct sockaddr_in cin=((msg*)arg)->cin;
//接收
char buf[128]="";
ssize_t res;
while(1)
{
bzero(buf,sizeof(buf));
res=recv(newfd,buf,sizeof(buf),0);
if(res<0)
{
ERR_MSG("recv");
return NULL;
}
else if(res==0)
{
printf("[ %s : %d ]客户端下线\n",\
inet_ntoa(cin.sin_addr),PORT);
break;
}
printf("newfd=%d ;%s\n",newfd,buf);
//发送
strcat(buf,"***");
write(newfd,buf,sizeof(buf));
}
close(newfd);
}
IO多路复用:select的TCP服务器
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <sys/select.h>
#include <sys/time.h>
#define PORT 8888 //1024~49151
#define IP "192.168.124.128"
#define ERR_MSG(msg) {fprintf(stderr,"%d\n",__LINE__);\
perror(msg);\
}
int deal_keybord_msg(fd_set readfds);
int deal_cliconnect(int sfd,struct sockaddr_in *save,fd_set *readfds,int *maxsfd);
int deal_clirecsend(int i,struct sockaddr_in *save,fd_set *readfds,int *maxsfd);
int main(int argc, const char *argv[])
{
//服务器
//创建套接字tcp
int sfd=socket(AF_INET,SOCK_STREAM,0);
if(sfd<0)
{
ERR_MSG("socket");
}
printf("流式套接字创建成功\n");
//允许端口快速复用
int reuse = 1;
if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0)
{
ERR_MSG("setsockopt");
return -1;
}
printf("允许端口快速重用\n");
//填充服务器的地址信息结构体,bind 使用
//真实的地址信息结构体根据地址族,AF_INET ---->man 7 ip
struct sockaddr_in sin;
sin.sin_family=AF_INET; //必须填AF_INET
sin.sin_port=htons(PORT); //点分十进制转网络字节序,端口号的网络字节序。
sin.sin_addr.s_addr=inet_addr(IP); //点分十进制转网络字节序,本机IP。
//绑定服务器的IP和端口号,服务器必须绑定
if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin))<0)
{
ERR_MSG("bind");
return -1;
}
printf("绑定成功\n");
//将套接字设置为被动监听状态
if(listen(sfd,128)<0)
{
ERR_MSG("listen");
return -1;
}
printf("监听成功\n");
//创建一个读集合
fd_set readfds,tmpfds;
//清空集合
FD_ZERO(&readfds);
//将需要的文件描述符添加到集合中,
FD_SET(0,&readfds);
FD_SET(sfd,&readfds);
int maxsfd=sfd;
struct sockaddr_in save[1024];
int s_res;
int newfd;
char buf[128]="";
ssize_t res;
while(1)
{
//因为每次执行只会留下就绪的文件描述符,
//下次循环的时候需要再次添加到集合中
tmpfds=readfds;
//让内核监测文件描述符是否准备就绪
s_res=select(maxsfd+1,&tmpfds,NULL,NULL,NULL);
if(s_res<0)
{
ERR_MSG("select");
return -1;
}
else if(0==s_res)
{
printf("时间到了\n");
return -1;
}
//能运行到当前位置,则代表集合中的文件描述符准备就绪
//判断集合中呢个文件描述符准备就绪,执行对应的函数
//集合中只会留下准备就绪的文件描述符
//0号准备就绪就只会剩下0;
for(int i=0;i<=maxsfd;i++)
{
//判断i是否在集合中
if(FD_ISSET(i,&tmpfds)==0)
{
continue;
}
printf("sfd=%d\n",sfd);
if(0==i) //判断0号是否在集合中
{
printf("触发键盘事件\n");
deal_keybord_msg(readfds);
}
else if(sfd==i) //判断sfd 是否在集合中
{
printf("触发connept事件\n");
deal_cliconnect(sfd,save,&readfds,&maxsfd);
}
else
{
printf("触发客户端交互事件\n");
deal_clirecsend(i,save,&readfds,&maxsfd);
}
}
}
//关闭文件描述符
if(close(sfd)<0)
{
ERR_MSG("close");
return -1;
}
if(close(newfd)<0)
{
ERR_MSG("close");
return -1;
}
return 0;
}
//处理客户端交互事件
int deal_clirecsend(int i,struct sockaddr_in *save,fd_set *readfds,int *maxsfd)
{
char buf[128]="";
//接收数据
bzero(buf,sizeof(buf));
ssize_t res=recv(i,buf,sizeof(buf),0);
if(res<0)
{
ERR_MSG("recv");
return -1;
}
else if(res==0)
{
printf("[ %s : %d ]客户端下线\n",\
inet_ntoa(save[i].sin_addr),ntohs(save[i].sin_port));
//关闭文件描述符
close(i);
//从集合中删除
FD_CLR(i,readfds);
//更新maxsfd
while(FD_ISSET(*maxsfd,readfds)==0&&*maxsfd-->0);
return -1;
}
printf("[ %s : %d ]buf=%s\n",\
inet_ntoa(save[i].sin_addr),ntohs(save[i].sin_port),buf);
//发送
strcat(buf,"***");
write(i,buf,sizeof(buf));
printf("发送成功\n");
}
//处理客户端链接事件
int deal_cliconnect(int sfd,struct sockaddr_in *save,fd_set *readfds,int *maxsfd)
{
struct sockaddr_in cin;
socklen_t addrlen=sizeof(cin);
int newfd=accept(sfd,(struct sockaddr*)&cin,&addrlen);
if(newfd<0)
{
ERR_MSG("accept");
return -1;
}
printf("[ %s : %d ]newfd=%d 客户端链接成功\n",\
inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),newfd);
save[newfd]=cin;
//将newfd添加到集合中
FD_SET(newfd,readfds);
//更新manfds
*maxsfd=*maxsfd>newfd?*maxsfd:newfd;
return 0;
}
//输入键盘事件
int deal_keybord_msg(fd_set readfds)
{
int serfd;
char buf[128]="";
int res=scanf("%d %s",&serfd,buf);
while(getchar()!='\n');
if(res!=2)
{
printf("请输入正确的格式\n");
return -1;
}
if(serfd<=3&&FD_ISSET(serfd,&readfds)==0)
{
printf("serfd=%d 请输入正确的文件描述符\n",serfd);
return -1;
}
if(send(serfd,buf,sizeof(buf),0)<0)
{
ERR_MSG("send");
return -1;
}
printf("服务器发送数据成功,serfd=%d\n",serfd);
return 0;
}
select的TCP客户端
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <sys/select.h>
#include <sys/time.h>
#define PORT 8888 //1024~49151
#define IP "192.168.124.128"
#define ERR_MSG(msg) {fprintf(stderr,"%d\n",__LINE__);\
perror(msg);\
}
int main(int argc, const char *argv[])
{
//创建流式套接字--socket
int cfd=socket(AF_INET,SOCK_STREAM,0);
if(cfd<0)
{
ERR_MSG("socket");
return -1;
}
printf("流式套接字创建成功\n");
//非必须绑定--bind
struct sockaddr_in cin;
cin.sin_family=AF_INET;
cin.sin_port=ntohs(PORT);
cin.sin_addr.s_addr=inet_addr(IP);
//连接服务器端
if(connect(cfd,(struct sockaddr*)&cin,sizeof(cin))<0)
{
ERR_MSG("connect");
return -1;
}
printf("连接服务器[%s :%d]成功cfd=%d\n",IP,PORT,cfd);
//IO多路复用
fd_set readfds,tmpfds;
FD_ZERO(&readfds);
FD_SET(0,&readfds);
FD_SET(cfd,&readfds);
int maxcfd=cfd;
while(1)
{
tmpfds=readfds;
int s_res=select(maxcfd+1,&tmpfds,NULL,NULL,NULL);
if(s_res<0)
{
ERR_MSG("select");
return -1;
}
else if(0==s_res)
{
printf("超时了\n");
break;
}
printf("解除阻塞成功\n");
for(int i=0;i<=maxcfd;i++)
{
if(FD_ISSET(i,&tmpfds)==0)
{
continue;
}
if(0==i)
{
//发送数据
char buf[128]="";
fgets(buf,sizeof(buf),stdin);
buf[strlen(buf)-1]='\0';
if(send(cfd,buf,sizeof(buf),0)<0)
{
ERR_MSG("send");
continue;
}
printf("客户端[%s :%d]发送数据成功,cfd=%d\n",IP,PORT,cfd);
}
else if(i==cfd)
{
char buf[128]="";
//接收数据
int res=recv(cfd,buf,sizeof(buf),0);
if(res<0)
{
ERR_MSG("recv");
continue;
}
else if(0==res)
{
printf("服务器[%s :%d]下线\n",IP,PORT);
return -1;
}
printf("[%s:%d]buf=%s cfd=%d\n",IP,PORT,buf,cfd);
}
}
}
//关闭文件描述符
if(close(cfd)<0)
{
ERR_MSG("close");
return -1;
}
return 0;
}