6.21作业

1. 多线程并发服务器

#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 6666
#define IP "192.168.124.53"//终端输入ifconfig查看IP

#define ERR_MSG(msg){fprintf(stderr,"第%d行 ",__LINE__);perror(msg);}

//需要传递给分支线程的参数结构体
struct msg
{
    int newfd;
    struct sockaddr_in cin;
};

void* deal_cli_msg(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;
    }
    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(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");

    //获取连接成功的客户端信息,生成一个新的套接字文件描述符
    struct sockaddr_in cin;//存储客户端信息
    socklen_t addrlen = sizeof(cin);

    int newfd;
    pthread_t tid;
    struct msg clinfo;
    while(1)
    {
        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);
        clinfo.newfd = newfd;
        clinfo.cin = cin;

        if(pthread_create(&tid,NULL,deal_cli_msg,(void*)&clinfo)!=0)
        {
            fprintf(stderr,"pthread_create failed __%d__\n", __LINE__);                                                                
            break;
        }
        pthread_detach(tid);

    }
    //关闭文件描述符
    if(close(sfd) < 0)
    {
        ERR_MSG("close");
        return -1;
    }
    return 0;
}

//线程执行体:处理客户端的交互
void* deal_cli_msg(void* arg)
{
    int newfd = ((struct msg*)arg)->newfd;
    struct sockaddr_in cin = ((struct 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");
            break;
        }
        else if(0 == res)
        {
            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);

        //发送
        strcat(buf,"*_*");
        if(send(newfd,buf,sizeof(buf),0)<0)
        {
            ERR_MSG("send");
            break;
        }
        printf("发送成功\n");
    }
    close(newfd);
    if(close(newfd) < 0)
    {
        ERR_MSG("close");
        return 0;
    }
    pthread_exit(NULL);

2.select的TCP服务器

  1 #include <stdio.h>
  2 #include <sys/types.h>
  3 #include <sys/socket.h>
  4 #include <unistd.h>
  5 #include <arpa/inet.h>
  6 #include <netinet/in.h>
  7 #include <string.h>
  8 #include <sys/time.h>
  9 #include <sys/select.h>
 10 
 11 
 12 #define IP "192.168.124.47"     //ifconfig查看
 13 #define PORT 8888              //端口号的网络字节序(在1014-49151之间)
 14 #define ERR_MSG(msg) do{\
 15     fprintf(stderr,"line=%d \n",__LINE__);\
 16     perror(msg);\
 17 }while(0)
 18 
 19 int deal_keyboard_msg(fd_set readfds);
 20 int deal_cliConnect(int sfd,struct sockaddr_in  psavecin[],fd_set * preadfds,int *pmaxfd);
 21 int deal_cliRecvSend(int j,struct sockaddr_in savecin[],fd_set * preadfds,int * pmaxfd);
 22 
 23 int main(int argc, const char *argv[])
 24 {
 25     //创建流式套接字
 26     int sfd = socket(AF_INET,SOCK_STREAM,0);
 27     if (sfd < 0)
 28     {
 29         ERR_MSG("socket");
 30         return -1;
 31     }
 32     printf("流式套接字创建成功 sfd=%d\n",sfd);
 33 
 34     //允许端口被快速使用
 35     int reuse = 1;
 36     if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse))<0)
 37     {
 38         ERR_MSG("setsockopt");
 39         return -1;
 40     }
 41     printf("允许端口快速重用\n");
 42     //填充服务器的地址信息结构体,给bind函数使用
 43     //真实的地址信息结构体根据地址族制定,AF_INET-->main 7 ip
 44     struct sockaddr_in sin;
 45     sin.sin_family      = AF_INET;//必须填AF_INET(代表是流式套接字)
 46     sin.sin_port        = htons(PORT);//端口号的网络字节序
 47                                         //(htons代表是将主机字节序转换为网络字节序)
 48     sin.sin_addr.s_addr = inet_addr(IP);//本机IP,ifconfig查看,
 49                                         //inet_addr是.十进制转换为网络字节序
 50 
 51     //绑定服务器的地址信息,必须绑定
 52     if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin)) < 0)
 53     {
 54         ERR_MSG("bind");
 55         return -1;
 56     }
 57     printf("绑定成功\n");
 58 
 59     //将套接字设置为被监听状态
 60     if (listen(sfd,128)< 0)
 61     {
 62         ERR_MSG("listen");
 63         return -1;
 64     }
 65     printf("监听成功\n");
 66     /*
 67     //获取连接成功的客户端信息,生成一个新的套接字文件描述符
 68     struct sockaddr_in cin;
 69     socklen_t addrlen = sizeof(cin);
 70     */
 71 
 72 
 73     //创建读集合
 74     //-->fd——set本质上是一个结构体,其中只有一个整型数组
 75     //若不清空,回事一堆随机值,有可能会随机到有效的文件描述符编号
 76     //但是有效文件描述符编号不要检测,从而导致select异常接触阻塞
 77     fd_set readfds,tmpfds;
 78     FD_ZERO(&readfds); // 清空集合
 79 
 80     //将需要的文件描述符添加到集合中
 81     FD_SET(0,&readfds);
 82     FD_SET(sfd,&readfds);
 83 
 84     int maxfd = sfd;  // 集合中最大的文件描述符
 85 
 86     int s_res;
 87     int newsfd = -1;
 88     struct sockaddr_in savecin[1024];//将客户端的地址信息存储到对应文件描述符下标位置
 89 
 90     while(1)
 91     {
 92         tmpfds = readfds;
 93 
 94         //让内核监测集合中的文件描述符是否准备就绪 
 95         s_res = select(maxfd+1,&tmpfds,NULL,NULL,NULL);
 96         if(s_res<0)
 97         {
 98             ERR_MSG("select");
 99             return -1;
100         }
101         else if(s_res == 0)
102         {
103             printf("time out....\n");
104             break;
105         }
106         printf("__%d__\n",__LINE__);
107 
108         //能运行到当前位置说明集合中有文件描述符准备就绪
109         //判断集合中哪个文件描述符准备就绪,执行对应的处理函数 
110         /*
111          * 集合中会只能存下产生时间的文件描述符
112          * 0号准备就绪,则集合中会只剩下0
113          * sfd准备就绪,则集合中会只剩下sfd
114          * 若0和sfd均准备就绪,则集合中会剩下0和sfd
115          * 所以只要判断集合中剩下哪个文件描述符,就代表该文件描述符准备就绪
116          */
117         int i;
118         for(i = 0;i <= maxfd;i++)
119         {
120             //判断i代表的文件描述符是否在集合中
121             if(FD_ISSET(i,&tmpfds) == 0)
122             {
123                 continue;
124             }
125             //运行到这个位置,说明i代表的文件描述符在集合中
126             if( i == 0)
127             {
128                 printf("触发键盘输入事件\n");
129                 deal_keyboard_msg(readfds);
130             }
131             else if(sfd == i)
132             {
133                 printf("触发客户端连接事件\n");
134                 deal_cliConnect(sfd,savecin,&readfds,&maxfd);
135             }
136             else
137             {
138                 printf("触发客户端交互事件\n");
139                 deal_cliRecvSend(i,savecin,&readfds,&maxfd);
140             }
141         }
142 
143     }
144 
145 //关闭文件描述符
146     if(close(sfd)< 0)
147     {
148         ERR_MSG("close");
149         return -1;
150     }
151     printf("sfd关闭成功 \n");
152     return 0;
153 }
154 
155 //键盘输入事件
156 int deal_keyboard_msg(fd_set readfds)
157 {
158     int sndfd;
159     char buf[128];
160     int res = scanf("%d %s",&sndfd,buf);
161     while(getchar()!='\n');
162     if(res != 2)
163     {
164         printf("请输入正确的格式:fd string \n");
165         return -1;
166     }
167     //判断键盘输入文件描述符是否合法
168     if(sndfd <= 3 || FD_ISSET(sndfd,&readfds) == 0)
169     {
170         printf("sndfd = %d错误,请输入合法的文件描述符\n",sndfd);
171         return -1;
172     }
173     if(send(sndfd,buf,sizeof(buf),0)<0)
174     {
175         ERR_MSG("send");
176         return -1;
177     }
178     return 0;
179 
180 }
181 //处理客户端连接事件
182 int deal_cliConnect(int sfd,struct sockaddr_in  psavecin[],fd_set * preadfds,int *pmaxfd)
183 {
184     struct sockaddr_in cin;          //存储客户端的信息
185     socklen_t addrlen = sizeof(cin);
186     int newsfd =accept(sfd,(struct sockaddr *)&cin,&addrlen);
187     if(newsfd < 0)
188     {
189         ERR_MSG("accept");
190         return -1;
191     }
192     printf("[%s %d] newsfd=%d 客户端连接成功\n",inet_ntoa(cin.sin_addr),\
193             ntohs(cin.sin_port),newsfd);
194 
195     psavecin[newsfd] = cin;
196 
197     FD_SET(newsfd,preadfds);//客户端连接成功后若要检测客户端的交互事件
198 
199     //需要将newsfd添加到集合中,让内核检测
200     *pmaxfd =*pmaxfd>newsfd?*pmaxfd:newsfd;//跟新maxfd中的值
201     return 0;                                                                                                                                  
202 }
203 
204 //处理客户端交互事件
205 int deal_cliRecvSend(int j,struct sockaddr_in savecin[],fd_set * preadfds,int * pmaxfd)
206 {
207     char buf[128] ="";
208     ssize_t res ;
209     bzero(buf,sizeof(buf));
210     //接收
211     res = recv(j,buf,sizeof(buf),0);
212     if(res < 0)
213     {
214         ERR_MSG("recv");
215         return -1;
216     }
217     else if(res == 0)
218     {
219         printf("[%s %d] newsfd=%d 客户端下线\n",inet_ntoa(savecin[j].sin_addr),\
220                 ntohs(savecin[j].sin_port),j);
221 
222         //关闭文件描述符
223         close(j);
224         //将文件描述符从集合中删除
225         FD_CLR(j,preadfds);
226         //更新maxfd,进循环的条件
227         /*
228            while(maxfd > 0 && FD_ISSET(maxfd,&readfds)==0)
229            {
230            maxfd--;
231            }*/
232         while(FD_ISSET(*pmaxfd,preadfds)==0 && (*pmaxfd)-- > 0);
233         return 0;
234     }
235     printf("[%s %d] newsfd = %d  %s\n",inet_ntoa(savecin[j].sin_addr),\
236             ntohs(savecin[j].sin_port),j,buf);
237     //发送
238     strcat(buf,"*__*");
239     if(send(j,buf,sizeof(buf),0)<0)
240     {
241         ERR_MSG("send");
242         return -1;
243     }
244     printf("发送成功\n");
245 
246     return 0;
247 
248 }
~                                                                                                                                                  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值