多线程的并发TCP通信,select的服务器及客户端

多线程并发TCP

客户端

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include <string.h>

#define PORT 8888
#define IP "192.168.10.10"
#define ERR_MSG(msg) do{\
    fprintf(stderr,"line=%d",__LINE__);\
    perror(msg);\
}while(0)
int main(int argc, const char *argv[])
{

    //创建套接字
    int fd = socket(AF_INET,SOCK_STREAM,0);
    if(fd<0)
    {
        ERR_MSG("socket");
        return -1;
    }
    printf("-流式套接字\n");
    //允许端口号重复使用
    int reuse = 1;
    if(setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse))<0)
    {
        ERR_MSG("setsockopt");
        return -1;
    }
    printf("端口允许快速重用\n");

    //创建一个地址信息结构体,bind绑定需要用到
    struct sockaddr_in sin;
    sin.sin_family = AF_INET;
    sin.sin_port   = htons(PORT);
    sin.sin_addr.s_addr = inet_addr(IP);


    //连接服务器
    if(connect(fd,(struct sockaddr*)&sin,sizeof(sin))<0)
    {
        ERR_MSG("connect");
        return -1;
    }
    printf("[%s:%d]连接服务器成功\n",IP,PORT);


    //接收与发送

    char buf[128];
    ssize_t size;
    while(1)
    {
        //发送
        printf("客户端请输入>>>");                                                                   
        fgets(buf,sizeof(buf),stdin);
        buf[strlen(buf)-1]='\0';

        if(send(fd,buf,sizeof(buf),0)<0)
        {
            ERR_MSG("send");
            return -1;
        }
        printf("发送成功\n");

        //接收
        bzero(buf,sizeof(buf));//清空数组内容
//      size = read(fd,buf,sizeof(buf));
        size = recv(fd,buf,sizeof(buf),0);
//      size = recvfrom(fd,buf,sizeof(buf),0,NULL,NULL);
        if(size<0)
        {
            ERR_MSG("recv");
            return -1;
        }else if(size==0)
        {
            printf("[%s:%d]服务器下线\n",inet_ntoa(sin.sin_addr),ntohs(sin.sin_port));
            break;
        }
        printf("[%s:%d] 接收成功%d:%s\n",IP,PORT,fd,buf);
    }

    close(fd);

    return 0;
}

IO流中select

服务器端

 #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/time.h>
 #include <string.h>                                                                                                                              
 
 #define PORT 8888   //1014~49151
 #define IP "192.168.148.17"
 #define ERR_MSG(msg) do{\
     fprintf(stderr,"line:%d",__LINE__);\
     perror(msg);\
 }while(0)
 
 
 int Keyboardvt(fd_set readfds);                                     //键盘事件
 int clientct(int psfd,int *pmaxfds,fd_set *preadfds,struct sockaddr_in psavecin[]);   //客户端连接事件
 int Interactionvt(int *pmaxfds,int i,struct sockaddr_in psavecin[],fd_set *preadfds);    //客户端交互事件    
 int prepare(int *pmaxfds,fd_set *ptmpfds,int s_res); //检查文件描述符是否准备就绪
 
 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;
     }
     printf("允许端口被重复使用\n");
     //填充服务器的地址信息结构体,给bind函数使用
     //真实的地址信息结构体根据地址族制定,AF_LNET-->>man 7 ipp
     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;
     }
     printf("监听成功!\n");
 
 
     //创建一个读集合
     fd_set readfds,tmpfds;
     FD_ZERO(&readfds);      //清空集合
 
     //将文件描述符表加入到集合
     FD_SET(0,&readfds);
     FD_SET(sfd,&readfds);
 
     int s_res;
     int newfds = -1;
     int maxfds = sfd;      //标记文件描述符中最大的一个存到max中
     struct sockaddr_in savecin[1024];
         while(1)                //让内核监听集合中的文件描述符是否准备就绪
         {
             tmpfds = readfds;   //把文件描述符表拷贝到tmp中,便于操作
             prepare(&maxfds,&tmpfds,s_res);
 
             printf("__%d__\n",__LINE__);
 
             //判断那个文件描述符准备就绪了,就执行哪一个文件描述符
         for(int i=0;i<=maxfds;i++)
         {
             if(FD_ISSET(i,&tmpfds)==0)
             {
                 continue;
             }
             if(0==i)
             {
                 printf("键盘事件被触发\n");
                 Keyboardvt(readfds);
 
             }
             else    if(sfd == i)
             {
                 printf("客户端连接被触发\n");
                 clientct(sfd,&maxfds,&readfds,savecin);
             }
             else
             {
                 printf("触发客户端交互事件\n");
                 Interactionvt(&maxfds,i,savecin,&readfds);
 
             }
         }
     }
     close(sfd);
     return 0;
 }
 
 int  prepare(int *pmaxfds,fd_set *ptmpfds,int s_res)
 {
     s_res = select(*pmaxfds+1,ptmpfds,NULL,NULL,NULL);
     if(s_res<0)
     {
         ERR_MSG("select");
         return -1;
     }
     else if(s_res == 0)
     {
         printf("time out\n");
         return 0;
     }
     return 0;
 }
 int Interactionvt(int *pmaxfds,int i,struct sockaddr_in psavecin[],fd_set *preadfds)
 {
     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]客户端下线\n %d",inet_ntoa(psavecin[i].sin_addr),ntohs(psavecin[i].sin_port),i);
         close(i);
         FD_CLR(i,preadfds); //把文件描述法冲readfds中删除
 
         while(FD_ISSET(*pmaxfds,preadfds) == 0 && *pmaxfds-- > 0); //更新集合中的最大文件描述符表
         return 0;
     }
     printf("[%s:%d] newfd=%d:%s\n",inet_ntoa(psavecin[i].sin_addr),ntohs(psavecin[i].sin_port),i,buf);
 
 
 
     //发送
 
     if(send(i,buf,sizeof(buf),0)<0)
     {
         ERR_MSG("send");
         return -1;
     }
     printf("发送成功\n");
 
     return 0;
 }
 
 int  clientct(int psfd,int *pmaxfds,fd_set *preadfds,struct sockaddr_in psavecin[])
 {
     struct sockaddr_in cin;
     socklen_t addrlen = sizeof(cin);
 
     int newsfd = accept(psfd,(struct sockaddr*)&cin ,&addrlen);
     if(newsfd<0)
     {
         ERR_MSG("accept");
         return -1;
     }
     printf("[%s:%d] newfd=%d 客户端连接成功\n",\
             inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),newsfd);
 
     psavecin [newsfd] = cin;
     FD_SET(newsfd,preadfds);  //将newfa加入到readfds中,用于监听文件描述符
     *pmaxfds = *pmaxfds>newsfd?*pmaxfds:newsfd; //判断目前最大的文件描述法并更新到max中
     return 0;
 }
 
 
 int  Keyboardvt(fd_set readfds)
 {
     int sndfd;
     char buf[128];
     int res = scanf("%d %s",&sndfd,buf);
     while(getchar()!='\n');
     if(res != 2)
     {
         printf("请输入正确格式:fd , string\n");
         return -1;
     }
     if(sndfd<=3 || FD_ISSET(sndfd,&readfds) == 0)
     {
 
         printf("sndfd=%d错误,请输入合法文件描述符\n",sndfd);
         return -1;
     }
     if(send(sndfd,buf,sizeof(buf),0)<0)
     {
         ERR_MSG("send");
         return -1;
     }
     return 0;
 }

客户端

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <errno.h>
#include <netinet/in.h>

#define IP "192.168.10.10"
#define PORT 8888

#define ERR_MSG do{\
    fprintf(stderr,"__%d__\n",__LINE__);\
    perror("msg");\
}while(0);

int main(int argc, const char *argv[])
{
    //创建一个socket创建流失套接字
    int sfd = socket(AF_INET,SOCK_STREAM,0);
    if(sfd<0)
    {
        ERR_MSG("socket");
        return -1;
    }
    printf("套接字创建完成\n");
    //绑定服务器的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(connect(sfd,(struct sockaddr*)&sin,sizeof(sin))<0)
    {
        ERR_MSG("connect");
            return -1;
    }
    printf("连接服务器成功\n");

    fd_set readfds,tmpfds;     //创建一个读集合

    FD_ZERO(&readfds);  //清空文件描述符

    FD_SET(0,&readfds); //把文件描述符加入到集合中
    FD_SET(sfd,&readfds);
    int maxfd = sfd;
    char buf[128],buf1[128];
    ssize_t res1,res;
    int s_res;
    while(1)
    {
        //发送接收
        tmpfds = readfds;   //拷贝文教描述符便于操作
        //让内核检验集合中那个文件描述符准备完成
        s_res = select(maxfd+1,&tmpfds,NULL,NULL,NULL);
        if(s_res<0)
        {
            ERR_MSG("select");
            return -1;
        }else if(s_res == 0)
        {
            printf("time out\n");
            break;
        }
        //循环判断触发那个事件

        if(FD_ISSET(0,&tmpfds))                                                                       
        {
            //  printf("发送信息>>>\n");
            bzero(buf,sizeof(buf));
            scanf("%s",buf);
            printf("------发送信息成功----\n");

            res = send(sfd,buf,sizeof(buf),0);
            if(res<0)
            {
                ERR_MSG("send");
            }
        //  printf("[%s:%d]客户端发:%s\n",IP,PORT,buf);
        }
        if(FD_ISSET(sfd,&readfds))
        {
            bzero(buf1,sizeof(buf1));
            res1 = recv(sfd,buf1,sizeof(buf1),0);
            if(res1<0)
            {
                ERR_MSG("recv");
                return -1;
            }
            else if(res1 ==0)
            {
                printf("[%s;%d]服务器下线 sfd: %d\n",IP,PORT,sfd);
                break;
            }
            printf("[%s:%d]服务器发:%s sfd=%d\n",IP,PORT,buf1,sfd);
        }
    }
    close(sfd);
    return 0;
}
相关推荐
生产队队长38 分钟前
Hadoop3:Yarn常用Shell命令
linux·运维·服务器·yarn
雨笋情缘1 小时前
Linux查询某个目录中包含的文件总个数和文件总存储,结果写入文件中
linux·运维·服务器
邓汪涛2 小时前
Git入门 本地仓库 远端仓库 多分支
git·gitee·github
辣码甄源2 小时前
“让爷看看”!开源智能坐姿守护者:PoseMonitor
后端·开源·github
陪我养猪吧2 小时前
常见网络端口号
服务器·网络
pin️‍灼灼灼灼3 小时前
Linux——线程练习
linux·服务器·算法
Crystal_bit3 小时前
服务器BMC基础知识总结
服务器
WolvenSec3 小时前
网络基础:OSPF 协议
服务器·网络·安全·网络安全·智能路由器
薛定谔式炼丹3 小时前
linux问题总结2
linux·运维·服务器