1.多线程
#include<stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<string.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/wait.h>
#include<signal.h>
#include<pthread.h>
#define ERR_MSG(msg) {printf("%d\n",__LINE__);\
perror(msg);}
#define PORT 8888
#define IP "192.168.124.38"
struct cli{
int newfd;
struct sockaddr_in cin;
};
struct cli dcli;
void*add(void*arg);
int main(int argc, const char *argv[])
{
int sfd=socket(AF_INET,SOCK_STREAM,0);
if(sfd<0){
ERR_MSG("socket");
return -1;
}
printf("创建流试套接字:sfd=%d\n",sfd);
//允许端口号被重复使用
int reuse=1;
if((setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse)))<0){
ERR_MSG("setsockopt");
return -1;
}
//填充服务器地址信息结构体,真实地址信息结构体根据地址族制定
//AF_INET--->man 7 ip
struct sockaddr_in sin;
sin.sin_family=AF_INET;
sin.sin_port=htons(PORT);
sin.sin_addr.s_addr=inet_addr(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;
}
struct sockaddr_in cin;//存储客服端信息
socklen_t addrlen=sizeof(cin);
pthread_t tid;
while(1){
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);
dcli.newfd=newfd;
dcli.cin=cin;
if(pthread_create(&tid,NULL,add,(void*)&dcli)!=0){
printf("%d\n",__LINE__);
return -1;
}
pthread_detach(tid);
}
close(sfd);
return 0;
}
void* add(void*arg){
char buf[128]="";
ssize_t res;
int newfd=dcli.newfd;
struct sockaddr_in cin;
cin=dcli.cin;
while(1){
bzero(buf,sizeof(buf));
res=recv(newfd,buf,sizeof(buf),0);
if(res<0){
ERR_MSG("recv");
break;
}
else if(res==0){
printf("[%s:%d]:newfd=%d 客服端下线\n",inet_ntoa(cin.sin_addr),\
ntohs(cin.sin_port),newfd);
break;
}
printf("[%s:%d]:newfd=%d :%s\n",inet_ntoa(cin.sin_addr),\
ntohs(cin.sin_port),newfd,buf);
//发送
res=send(newfd,buf,sizeof(buf),0);
if(res<0){
ERR_MSG("send");
break;
}
}
close(newfd);
return 0;
}
实现
2.io多路复用服务器
服务器
#include<stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<string.h>
#include<unistd.h>
#include <sys/select.h>
#include <sys/time.h>
#define ERR_MSG(msg) {printf("%d\n",__LINE__);\
perror(msg);}
#define PORT 8888
#define IP "192.168.1.17"
int deal_keyboard(fd_set readfds);
int deal_cliConnect(int sfd, struct sockaddr_in savecin[], fd_set* readfds, int* maxd);
int deal_clirescsend(int i, struct sockaddr_in savecin[], fd_set *readfds, int *maxd);
int main(int argc, const char *argv[])
{
int sfd=socket(AF_INET,SOCK_STREAM,0);
if(sfd<0){
ERR_MSG("socket");
return -1;
}
printf("创建流试套接字:sfd=%d\n",sfd);
//允许端口号被重复使用
int reuse=1;
if((setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse)))<0){
ERR_MSG("setsockopt");
return -1;
}
//填充服务器地址信息结构体,真实地址信息结构体根据地址族制定
//AF_INET--->man 7 ip
struct sockaddr_in sin;
sin.sin_family=AF_INET;
sin.sin_port=htons(PORT);
sin.sin_addr.s_addr=inet_addr(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;
}
//创建读集合
fd_set readfds ;
fd_set temefds;
//将需要的文件描述符添加到集合中
FD_ZERO(&readfds); //清空
FD_SET(0,&readfds);
FD_SET(sfd,&readfds);
int newfd=-1;
int ss_res;
int maxd=sfd;
struct sockaddr_in savecin[1024];
while(1)
{
temefds=readfds;
//让内核检测文件描述符是否准备就绪
ss_res=select(maxd+1,&temefds,NULL,NULL,NULL);
if(ss_res<0){
ERR_MSG("select");
return -1;
}
else if(ss_res==0){
printf("sssss\n");
break;
}
//判断集合中哪个文件描述符准备就绪,执行对应的处理函数
for(int i=0;i<=maxd;i++)
{
if(FD_ISSET(i,&temefds)==0){
continue;
}
if(i==0){
printf("触发键盘输入事件\n");
deal_keyboard(readfds);
}
else if(sfd==i)
{
printf("客户端连接事件\n");
deal_cliConnect(sfd,savecin,&readfds,&maxd);
}
else
{
printf("客户端交互事件\n");
deal_clirescsend(i,savecin,&readfds,&maxd);
}
}
}
if(close(sfd)<0){
ERR_MSG("close");
return -1;
}
return 0;
}
int deal_keyboard(fd_set readfds)
{
int ssfd;
char buf[128];
int res=scanf("%d %s",&ssfd,buf);
while(getchar()!='\n');
if(res!=2)
{
printf("请输入正确格式");
return -1;
}
if(ssfd<=3||FD_ISSET(ssfd,&readfds)==0)
{
printf("文件描述符错误\n");
return -1;
}
if(send(ssfd,buf,sizeof(buf),0)<0)
{
ERR_MSG("send");
return -1;
}
}
int deal_cliConnect(int sfd, struct sockaddr_in savecin[], fd_set* readfds, int* maxd)
{
struct sockaddr_in cin;
socklen_t addrlen=sizeof(cin);
printf("触发客服端连接\n");
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);
savecin[newfd]=cin;
FD_SET(newfd,readfds);
*maxd=*maxd>newfd?*maxd:newfd;
return 0;
}
int deal_clirescsend(int i, struct sockaddr_in savecin[], fd_set *readfds, int *maxd)
{
char buf[128]="";
ssize_t res;
//接收
bzero(buf,sizeof(buf));
res=recv(i,buf,sizeof(buf),0);
if(res<0){
ERR_MSG("recv");
return -1;
}
else if(res==0){
printf("[%s:%d]:newfd=%d 客服端下线\n",inet_ntoa(savecin[i].sin_addr),\
ntohs(savecin[i].sin_port),i);
close(i); //关闭文件描述符
FD_CLR(i,readfds);//将文件描述符从集合中删除
while(FD_ISSET(*maxd,readfds)==0&&(*maxd)-- >0);//更新maxd
return 0;
}
printf("[%s:%d]:newfd=%d :%s\n",inet_ntoa(savecin[i].sin_addr),\
ntohs(savecin[i].sin_port),i,buf);
//发送
strcat(buf,"*_*");
res=send(i,buf,sizeof(buf),0);
if(res<0){
ERR_MSG("send");
return -1;
}
return 0;
}
客户端
#include<stdio.h>
#include <sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<string.h>
#include<unistd.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#define ERR_MSG(msg) {printf("%d\n",__LINE__);\
perror(msg);}
#define PORT 8888
#define IP "192.168.1.17"
int main(int argc, const char *argv[])
{
int cfd=socket(AF_INET,SOCK_STREAM,0);
if(cfd<0){
ERR_MSG("socket");
return -1;
}
printf("创建流试套接字:cfd=%d\n",cfd);
//填充服务器地址信息结构体,真实地址信息结构体根据地址族制定
//AF_INET--->man 7 ip
struct sockaddr_in sin;
sin.sin_family =AF_INET;
sin.sin_port =htons(PORT);
sin.sin_addr.s_addr=inet_addr(IP);
if(connect(cfd,(struct sockaddr*)&sin,sizeof(sin))<0){
ERR_MSG("connect");
return -1;
}
//创建读集合
fd_set readfds ;
fd_set temefds;
//将需要的文件描述符添加到集合中
FD_ZERO(&readfds); //清空
FD_SET(0,&readfds);
FD_SET(cfd,&readfds);
char buf[128]="";
ssize_t res;
int ss_res;
while(1)
{
temefds=readfds;
//让内核检测文件描述符是否准备就绪
ss_res=select(cfd+1,&temefds,NULL,NULL,NULL);
if(ss_res<0){
ERR_MSG("select");
return -1;
}
else if(ss_res==0){
printf("sssss\n");
break;
}
if(FD_ISSET(0,&temefds)!=0){
//发送
bzero(buf,sizeof(buf));
scanf("%s",buf);
while(getchar()!='\n');
if(send(cfd,buf,sizeof(buf),0)<0)
{
ERR_MSG("send");
return -1;
}
}
if(FD_ISSET(cfd,&temefds)!=0){
bzero(buf,sizeof(buf));
res=recv(cfd,buf,sizeof(buf),0);
if(res<0){
ERR_MSG("recv");
return -1;
}
else if(res==0){
printf("[%s:%d]cfd:%d服务器下线\n",IP,PORT,cfd);
break;
}
printf("[%s:%d]cfd=%d: %s\n",IP,PORT,cfd,buf);
}
}
if(close(cfd)<0){
ERR_MSG("close");
return -1;
}
return 0;
}
实现