socket编程(3-1)

本章目标

  • REUSEADDR
  • 处理多客户连接(process-per-connection)
  • 点对点聊天程序实现

 

1.回顾

服务器端通过套接字conn,客户端通过套接字sock,他们可以互相通信


可想而知,这两个套接字都有自己的地址。
对于服务器来说,它的套接字地址是在绑定的时候确定的,
对于客户端来说,它的套接字是在连接成功后确定的。端口号是自动获取的。


注意:connect函数的第三个参数一定要初始化。不然会连接失败。

服务器和客户端通过两个套接字实现虚连接,是一种逻辑连接

 

构成一个连接 四要素 : 服务器IP port 客户端IP port


2.缺陷

服务器重启后,不能立即使用
出现错误提示: bind:address already in use

这里的主要原因在于:服务器重启后,又要去绑定地址,这时候的地址是不能绑定成功的。因为此时
网络处于一个状态 netstat -an|grep TIME_WAIT
renzhuang@geroge:~/netcoding/08socket$ netstat -an |grep TIME_WAIT
tcp 0 0 127.0.0.1:5188 127.0.0.1:56457 TIME_WAIT

服务器端处于TIME_WAIT状态

解决:用REUSEADDR来解决


并不意味着服务器启动后,还可以启动一个服务器!!!明显地址已经被使用了。。

服务器端代码:

#include<stdio.h>
#include <sys/socket.h>
#include<sys/types.h>
#include<unistd.h>

#include <netinet/in.h>
#include <arpa/inet.h>

#include<stdlib.h>
#include<errno.h>
#include<string.h>

int main()
{
    /*create a socket*/
    int sockfd;
    if((sockfd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP)) < 0)
        printf("socket err");
    
    /*init addr*/    
    struct sockaddr_in servaddr;
    memset(&servaddr,0,sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(5188);
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    /*servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");*/
    /*inet_aton("127.0.0.1",&servaddr.sin_addr);*/

    int on = 1;/*开启地址重复利用*/
    if(setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)) < 0)
        printf("setsockopt err");

    /*将创建的套接字与本地地址进行绑定*/
    if(bind(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr)) < 0)
        printf("blind err");

    /*监听套接字*/
    if (listen(sockfd,SOMAXCONN) < 0 )
        printf("listen err:");
    struct sockaddr_in peeraddr;
    socklen_t peerlen = sizeof(peeraddr);
    int conn;
    /*主动套接字*/
    if ((conn = accept(sockfd,(struct sockaddr*)&peeraddr,&peerlen)) < 0 )
        printf("accept err");

    printf("ip = %s,port = %d\n",inet_ntoa(peeraddr.sin_addr),ntohs(peeraddr.sin_port));
    /*处理通信的细节*/
    char recvbuf[1024];
    while(1)
    {
    
        memset(recvbuf,0,sizeof(recvbuf));
        int ret  = read(conn,recvbuf,sizeof(recvbuf));
        fputs(recvbuf,stdout);
        write(conn,recvbuf,ret);
    }

    close(conn);
    close(sockfd);

    return 0;

}

客户端代码没有更改:

#include<stdio.h>
#include <sys/socket.h>
#include<sys/types.h>
#include<unistd.h>

#include <netinet/in.h>
#include <arpa/inet.h>

#include<stdlib.h>
#include<errno.h>
#include<string.h>


int main()
{
    /*创建套接字*/    
    int sockfd;
    if((sockfd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP)) < 0)
        printf("socket err");

    /*serv addr and port*/    
    struct sockaddr_in servaddr;
    memset(&servaddr,0,sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(5188);
    servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    
    /*连接,一旦连接成功,sockfd套接字就处于已连接状态,从逻辑上
      是与服务器端的conn套接字连接*/    
    if(connect(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr)) <0)
        printf("connect err");
    
    
    char sendbuf[1024]={0};
    char recvbuf[1024]={0};
    while(fgets(sendbuf,sizeof(sendbuf),stdin) != NULL)
    {
        write(sockfd,sendbuf,strlen(sendbuf));
        read(sockfd,recvbuf,sizeof(recvbuf));
    
        fputs(recvbuf,stdout);
        memset(sendbuf,0,sizeof(sendbuf));
        memset(recvbuf,0,sizeof(recvbuf));
        
    }

    close(sockfd);

    return 0;

}

 

现在服务器重启后可以立即使用,不会处于TIME_WAIT状态。

但是,现在服务器只能处理单个客户端连接。下一节,我们将处理多个客户的连接情况(并发)

posted @ 2016-12-09 15:29  ren_zhg1992  阅读(118)  评论(0)    收藏  举报