linux下实现服务器与客户端(连接时给客户端发送时间)

OSI七层模型
应用层,表达层,会话层,传输层,网络层,数据链路层,物理层
(顺口溜式记忆:应用表示会话层,传输网络链路层,还有一个物理层)
TCP/IP四层模型
应用层,传输层,互联网络层,网络接口层

实现一个服务器向连接的客户端发送系统时间

从命令行接受端口号

服务器中止时打印出结束信息
linux下实现服务器与客户端(连接时给客户端发送时间)_第1张图片
sockfd设为全局变量
sig_handler为终止信号运行函数
linux下实现服务器与客户端(连接时给客户端发送时间)_第2张图片

创建socket

int socket(int domain, int type, int protocol);
应用层:面向用户
传输层:如TCP,UDP
网络层:如IP
socket位于应用层与传输层之间
返回值:套接字描述符(一切皆文件)
domain:协议族(family)决定地址类型如:AF_INET
type:socket类型如:SOCK_STREAM,SOCK_DGRAM
protocol:指定协议如:IPPROTO_TCP、IPPTOTO_UDP
或者填0会根据type选择对应的
这个套接字用于监听
linux下实现服务器与客户端(连接时给客户端发送时间)_第3张图片
socket结构体如下:
linux下实现服务器与客户端(连接时给客户端发送时间)_第4张图片

绑定socket结构体bind

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
把地址族的特定地址给socket,不调用会随机分配端口号
返回值:成功返回0
sockfd:套接字描述符
addr:套接字协议地址
addr:地址大小
linux下实现服务器与客户端(连接时给客户端发送时间)_第5张图片

监听listen

int listen(int sockfd, int backlog);
返回值:成功返回0
sockfd:套接字描述符
backlog:队列最大连接数
linux下实现服务器与客户端(连接时给客户端发送时间)_第6张图片

接受客户端请求accept

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
返回值:一个新的连接客户端的描述符(区别于前面监听用的)
sockfd:监听用套接字
addr:客户端的地址信息,不需要可设为NULL
addrlen:地址长度,同样可为NULL
linux下实现服务器与客户端(连接时给客户端发送时间)_第7张图片

调用系统IO通信

通过write给客户端发送服务器当前时间
linux下实现服务器与客户端(连接时给客户端发送时间)_第8张图片
右边终端:编译-运行-终止
linux下实现服务器与客户端(连接时给客户端发送时间)_第9张图片
服务器源码:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

int sockfd;

void sig_handler(int signo)
{
    if(signo == SIGINT)
    {
        printf("server close\n");
        close(sockfd);
        exit(1);
    }
}

/*输出连接的客户端的信息*/
void out_addr(struct sockaddr_in *clientaddr)
{
    //网序转主机序
    int port = ntohs(clientaddr->sin_port);
    char ip[16];
    memset(ip,0,sizeof(ip));
    inet_ntop(AF_INET,
        &clientaddr->sin_addr.s_addr,ip,sizeof(ip));
    printf("client:%s(%d) connected\n",ip,port);
}

void do_service(int fd)
{
    long t = time(0);
    char *s = ctime(&t);
    size_t size = strlen(s) * sizeof(char);
    if(write(fd,s,size) != size)
    {
        perror("write error");
    }
}


int main(int argc,char *argv[])
{
    if(argc < 2)
    {
        printf("usage:%s #port\n",argv[0]);
        exit(1);
    }

    if(signal(SIGINT,sig_handler) == SIG_ERR)
    {
        perror("signal sigint error");
        exit(1);
    }

    /*创建socket
     *socket为结构体
     *AF_INET: IPV4
     *SOCKET_STREAM:tcp协议
     */
    sockfd = socket(AF_INET,SOCK_STREAM,0);

    /*
     * socket跟地址绑定(ip/port)
     */
    struct sockaddr_in serveraddr;
    memset(&serveraddr,0,sizeof(serveraddr));
    /*填入ip,port,internet地址族*/
    serveraddr.sin_family = AF_INET;//ipv4
    serveraddr.sin_port = htons(atoi(argv[1]));//port
    /*可以绑定指定ip
    如:serveraddr.sin_addr.s_addr = "192.168.1.10";
    或者用系统定义的宏
    #define INADDR_ANY (uint32_t)0x00000000
    可以响应所有接口的请求
    */
    serveraddr.sin_addr.s_addr = INADDR_ANY;
    if(bind(sockfd,(struct sockaddr*)&serveraddr,
        sizeof(serveraddr)) < 0)
    {
        perror("bind error");
        exit(1);
    }

    /*监听指定端口
     *接受来自服务器的请求
   *将客户端请求放置对应队列  
    */
    if(listen(sockfd,10) < 0)//10为队列长度
    {
        perror("listen error");
        exit(1);
    }

    /*调用accept从队列中获取一个客户端请求
     *若没有链接会阻塞,直到有一个连接
     */
    struct sockaddr_in clientaddr;
    socklen_t clientaddr_len = sizeof(clientaddr);
    while(1)
    {
        int fd = accept(sockfd,(struct sockaddr*)&clientaddr,
            &clientaddr_len);
        if(fd < 0)
        {
            perror("accept error");
            continue;
        }

        /*调用系统IO通信*/
        out_addr(&clientaddr);
        do_service(fd);

        /*关闭套接字*/
        close(fd);
    }

    return 0;
}

实现客户端

从命令行接受ip和端口号 创建套接字

linux下实现服务器与客户端(连接时给客户端发送时间)_第10张图片

连接服务器connect

int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
对应于服务器listen
返回值:成功返回0失败-1
sockfd:客户端套接字描述符
addr:要连接的服务器地址
addrlen:地址长度
linux下实现服务器与客户端(连接时给客户端发送时间)_第11张图片

调用系统IO通信

read读取来自服务器的消息
linux下实现服务器与客户端(连接时给客户端发送时间)_第12张图片

客户端源代码:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

int main(int argc,char *argv[])
{
    if(argc < 3)
    {
        printf("usage:%s ip port\n",argv[0]);
        exit(1);
    }

    /*创建socket*/
    int sockfd = socket(AF_INET,SOCK_STREAM,0);
    if(sockfd < 0)
    {
        perror("socket error");
        exit(1);
    }

    struct sockaddr_in serveraddr;
    memset(&serveraddr,0,sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_port = htons(atoi(argv[2]));
    inet_pton(AF_INET,argv[1],&serveraddr.sin_addr.s_addr);

    /*连接到服务器*/
    if(connect(sockfd,(struct sockaddr*)&serveraddr,
        sizeof(serveraddr)) < 0)
    {
        perror("connect error");
        exit(1);
    }

    /*系统IO通信*/
    char buffer[1024];
    memset(buffer,0,sizeof(buffer));
    size_t size;
    if((size = read(sockfd,buffer,sizeof(buffer))) < 0)
    {
        perror("read error");
    }
    if(write(1,buffer,size) != size)
    {
        perror("write error");
    }

    /*关闭套接字*/
    close(sockfd);
    return 0;
}

编译运行服务器和客户端结果(左边服务器):

linux下实现服务器与客户端(连接时给客户端发送时间)_第13张图片
(服务器循环,每连接一个客户端就发送系统时间)

你可能感兴趣的:(linux)