REUSEADDR,服务器连接多个客户端,点对点聊天程序

来源:互联网 发布:小米主题 windows xp 编辑:程序博客网 时间:2024/06/13 07:04

1. REUSEADDR


服务器端尽可能使用REUSEADDR 选项
在绑定地址端口之前尽可能调用setsocktopt()来设置REUSEADDR套接字选项

使用了REUSEADDR选项后,可以使得不必等待TIME_WAIT状态消失就可以重启服务器

//设置REUSEADDR 选项    int on = 1;    run = setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));    if(run < 0)    {  printf("error setsokopt \n");        return -5;    }  


 
2.服务器连接多个客户端
 

使用多进程进行处理,用子进程处理链接
                   父进程继续处理accept();从完成链接队列中再获取一个
服务器端有两个套接字,一个已连接套接口,主要是用来通信的
                     一个监听调节口,主要实现三次握手的过程

客户端只有一个套接字,链接套接字

//服务器端函数#include<stdio.h>#include<sys/socket.h>#include<netinet/in.h>#include<arpa/inet.h>#include<string.h>#include<stdlib.h>void do_service(int conn){    //实现回射通信    char recvbuf[1024];    while(1)    {           memset(recvbuf,0,sizeof(recvbuf));int ret=0;//接受客户端的请求,read()失败返回-1        ret = read(conn,recvbuf,sizeof(recvbuf)); //返回实际接受到的字节数        if(ret == 0)  //说明客户端关闭了        {            printf("client close \n");            break;        }        else if(ret == -1) //-1表示失败了        {           printf("error read() \n");           break;        }//对请求进行处理printf("%s",recvbuf);//对客户端进行数据应答        write(conn,recvbuf,ret);    }}int main(){    int listenfd;    int run;    //首先调用socket()函数创建套接字    listenfd =socket(PF_INET,SOCK_STREAM,IPPROTO_TCP); //在LINUX 下可以用man socket 查询该函数的具体描述    if(listenfd < 0)    {printf("ERROR\n");return -1;    }    struct sockaddr_in servaddr;           //IPV4的地址结构    memset(&servaddr,0,sizeof(servaddr));      // 初始化地址    servaddr.sin_family = AF_INET;         //地址的家族    servaddr.sin_port = htons(5188);       //端口号,2个字节,这里需要的是网络字节序(大端),需要将5188转换为网络字节序    servaddr.sin_addr.s_addr = htonl(INADDR_ANY); //设置地址,INADDR_ANY表示本地的任意地址       //设置REUSEADDR 选项    int on = 1;    run = setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));    if(run < 0)    {  printf("error setsokopt \n");        return -5;    }        //第二步为套接字绑定一个本地地址    run=bind(listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr));    if(run  < 0)    {printf("error\n");        return -2;    }    //第三步:使套接字处于监听状态    run = listen(listenfd,SOMAXCONN);    if(run < 0)    {       printf("error\n");       return -3;    }    struct sockaddr_in peeraddr;         //定义对方的地址    socklen_t peerlen =sizeof(peeraddr); //定义对方的地址长度大小,注意需要有初始值,负责 accept()会失败    int conn;    pid_t pid;    //第四步:从已完成连接队列中返回第一个连接,如果没有连接过来一直处于阻塞状态    while(1)    {    conn = accept(listenfd,(struct sockaddr *)&peeraddr,&peerlen);    if(conn < 0)    {printf("error\n");return  -4;    }     //如果连接成功可以将对方的连接地址打印出来    printf("ip= %s ,port=%d \n",inet_ntoa(peeraddr.sin_addr),ntohs(peeraddr.sin_port)); //创建进程        pid = fork();if(pid == -1) // 失败{   printf("error fork()\n");   return -1;} if(pid == 0) //fork()为子进程返回0        {            close(listenfd);  //子进程不需要处理监听,只需要处理链接            do_service(conn); //服务器相应函数            exit(EXIT_SUCCESS);}else         //为父进程返回非负整数        {    close(conn);     //父进程不需要处理链接,关闭链接套接口  }    }    return 0;}

3. 点对点聊天程序 
 
在服务器端/客户端创建一个线程专门用来从键盘上获取字符,发送到客户端
                另外一个进程专门用来进行接受字符

//服务器端函数#include<stdio.h>#include<sys/socket.h>#include<netinet/in.h>#include<arpa/inet.h>#include<string.h>#include<stdlib.h>#include<errno.h>#include<signal.h>void handler(int sig){   printf("recv a sig = %d\n",sig);   printf("服务器端子进程退出\n");   exit(0);}int main(){    int listenfd;    int run;     extern int errno;    //首先调用socket()函数创建套接字    listenfd =socket(PF_INET,SOCK_STREAM,IPPROTO_TCP); //在LINUX 下可以用man socket 查询该函数的具体描述    if(listenfd < 0)    {printf("ERROR\n");return -1;    }    struct sockaddr_in servaddr;           //IPV4的地址结构    memset(&servaddr,0,sizeof(servaddr));      // 初始化地址    servaddr.sin_family = AF_INET;         //地址的家族    servaddr.sin_port = htons(5188);       //端口号,2个字节,这里需要的是网络字节序(大端),需要将5188转换为网络字节序    servaddr.sin_addr.s_addr = htonl(INADDR_ANY); //设置地址,INADDR_ANY表示本地的任意地址       //设置REUSEADDR 选项    int on = 1;    run = setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));    if(run < 0)    {  printf("error setsokopt \n");        return -5;    }        //第二步为套接字绑定一个本地地址    run=bind(listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr));    if(run  < 0)    {printf("error\n");        return -2;    }    //第三步:使套接字处于监听状态    run = listen(listenfd,SOMAXCONN);    if(run < 0)    {       printf("error\n");       return -3;    }    struct sockaddr_in peeraddr;         //定义对方的地址    socklen_t peerlen =sizeof(peeraddr); //定义对方的地址长度大小,注意需要有初始值,负责 accept()会失败    int conn;    pid_t pid;    //第四步:从已完成连接队列中返回第一个连接,如果没有连接过来一直处于阻塞状态      conn = accept(listenfd,(struct sockaddr *)&peeraddr,&peerlen);    if(conn < 0)    {printf("error\n");return  -4;    }    //如果连接成功可以将对方的连接地址打印出来    printf("ip= %s ,port=%d \n",inet_ntoa(peeraddr.sin_addr),ntohs(peeraddr.sin_port));    //创建进程            pid = fork();    if(pid == -1) // 失败    {        printf("error fork()\n");return -1;    }    if(pid == 0) //fork()为子进程返回0,专门用来发送数据    {           close(listenfd);  //子进程不需要处理监听,只需要处理链接          int writeerro;        char sendbuff[1024];        signal(SIGUSR1,handler); //信号处理函数,当收到这个信号时,调用信号处理函数        while( fgets(sendbuff,sizeof(sendbuff),stdin)!=NULL)        {                 writeerro=write(conn,sendbuff,strlen(sendbuff));             memset(sendbuff,0,sizeof(sendbuff));        }    }    else         //为父进程返回非负整数,专门用来接受数据    {        char recvbuff[1024];        while(1)        {            memset(recvbuff,0,sizeof(recvbuff));            int ret = read(conn,recvbuff,sizeof(recvbuff));            if(ret == -1)            {                printf("error read \n");                break;            }            else if(ret == 0)            {                printf("peer close \n");                break;            }            fputs(recvbuff,stdout);        }        kill(pid,SIGUSR1);//当父进程退出时向子进程发送SIGUSR1信号        printf("服务器端父进程退出\n");        close(conn);        exit(0);    }    return 0;}

//客户端程序
#include<stdio.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<string.h>
#include<stdlib.h>
 
int main()
{
    int sock;
    int error=0;
    //第一步 创建客户端套接字
    sock=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP);
    if(sock < 0)
    {
    printf("error socket();\n");
        return -1;
    }
    //设置连接地址
    struct sockaddr_in client;
    memset(&client,0,sizeof(client));
    client.sin_family=AF_INET;
    client.sin_port=htons(5188);
    client.sin_addr.s_addr =inet_addr("127.0.0.1");
    //客户端发起连接
    error=connect(sock,(struct sockaddr *)&client,sizeof(client));
    if(error<0)
    {
        printf("error connect();%d\n",error);
        return -2;
    }
    pid_t pid;
    pid = fork();
    if(pid == -1)
    {
        printf("error fork \n");
        return -1;
    }
    else if(pid == 0) //子进程返回0,专门用来接收数据
    {    
        char recvbuff[1024];
        while(1)
        {
            memset(recvbuff,0,sizeof(recvbuff));
            int ret = read(sock,recvbuff,sizeof(recvbuff));
            if(ret == -1)
            {
                printf("error read \n");
            }
            else if(ret == 0)
            {
                printf("peer close \n");
                break;
            }
            fputs(recvbuff,stdout);
        }
        printf("客户端子进程退出\n");         
        exit(0);
    }
    else //为父进程返回非负整数,专门用来发送数据  
    {
        char sendbuff[1024];
        while( fgets(sendbuff,sizeof(sendbuff),stdin)!=NULL)
        {
             write(sock,sendbuff,strlen(sendbuff));
             memset(sendbuff,0,sizeof(sendbuff));
        }
        close(sock);
        printf("客户端父进程退出\n");
    }
    return 0;
}

阅读全文
0 0