您的位置:首页 > 其它

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

2017-12-17 21:51 211 查看
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;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息