最简单的代码实现服务器和客户端

之前搭建服务器都是用的开源框架来实现,心中一直有不少的疑惑,操作系统到底是怎么实现服务器功能? 最近结合《详解TCP/IP》和《深入理解计算机系统》这两本书得以管中窥豹,突然间有种豁然开朗的感觉。
将我个人的理解以作笔记,都知道socket是网络通信的基石,用socket写了一个serverapp和clientapp。
其中客户端clientapp.c的代码如下:

#include <stdio.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <string.h>
#include <netinet/in.h>
#include <unistd.h>

int open_clientfd(char *hostname,char *port);
int main(int argc, char * argv[]) {
    int clientfd;
    
    if (argc != 3) {
        printf("must three params \n");
        return 0;
    }
    char *hostname = argv[1];
    char *port = argv[2];
    clientfd = open_clientfd(hostname,port);
    char str[64];
    if (clientfd < 0) {
        return 0;
    }
    read(clientfd,str,64);
    printf("%s", str);
    close(clientfd);
    return 0;
    
}

int open_clientfd(char *hostname, char *port)
{
    int clientfd, rc;
    struct addrinfo hints, *listp, *p;
    memset(&hints,0,sizeof(struct addrinfo));
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags |=  AI_ADDRCONFIG;
    hints.ai_flags = AI_NUMERICSERV;
    if ((rc = getaddrinfo(hostname, port, &hints, &listp)) != 0) {
        printf("getaddrinfo: failed\n");
        return -2;
    }
    
    for (p = listp; p ; p = p->ai_next) {
        if ((clientfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0) {
            continue;/*socket faied try the next*/
        }
        
        if (connect(clientfd, p->ai_addr, p->ai_addrlen) != -1) {
            break;//Success
        }
        if (close(clientfd) < 0) {
            printf("open_lientfd: close failed\n");
            return -1;
        }
        
    }
    freeaddrinfo(listp);
    if (!p) { 
        printf("All connects faied \n");
        return -1;
    } else {
        return clientfd;
    }
}


服务器端serverapp.c的代码如下

#include <stdio.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <string.h>
#include <netinet/in.h>
#include <unistd.h>

int open_listenfd(char *port);
int main(int argc, const char * argv[]) {
    int listenfd, connfd = 0;
    socklen_t clientlen;
    listenfd = open_listenfd("8887");
    int connd = accept(listenfd, (struct sockaddr*)&clientlen, &clientlen);
    if (connfd < 1) {
        printf("appect error");
    }
    char str[] = "qiansheng \n";
    write(connd, str, sizeof(str));
    close(connfd);
    close(listenfd);
    return 0;
}


int open_listenfd(char *port) {
    
    struct addrinfo hints, *listp, *p;
    int listenfd = 0, optval = 1;
    memset(&hints, 0, sizeof(struct addrinfo));
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG; // 被动的  任何IP address
    hints.ai_flags |= AI_NUMERICSERV; // use port number
    getaddrinfo(NULL, port, &hints, &listp);
    
    for (p = listp; p ; p = p->ai_next) {
        if ((listenfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0) {
            continue;
        }
        
        setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (const void *)&optval, sizeof(int));
        
        if (bind(listenfd, p->ai_addr, p->ai_addrlen) == 0) {
            break;//Success
        }
        close(listenfd);
    }
    
    freeaddrinfo(listp);
    if (!p) {
        return -1;
    }
    if (listen(listenfd, 30) < 0) {
        close(listenfd);
        return -1;
    }
    return listenfd;
}

在终端使用gcc命令分别执行

gcc -g serverapp.c  -o  serverapp
gcc -g clientapp.c  -o  clientapp

在serverapp和clientapp可执行文件后面加上参数就可以运行了
以本机电脑为例

./serverapp 8887 /**为了避免跟系统端口重复,选个端口号大一点的*/
./clientapp 127.0.0.1 8887

终端就会打印我们想要的hello word了 其中getaddrinfo函数是核心,可重入,适用于任何协议。

int getaddrinfo(const char *host,const char *service, const struct addrinfo *hints, struct addrinfo **result);//如果成功返回0  错误则为非零的错误代码

getaddrinfo返回result,result 指向一个addrinfo结构的链表。其中每个结构指向一个对应于host和service的套接字地址结构如下图。

addrinfo

在调用getaddrinfo之后,会遍历这个列表,依次尝试每个套接字地址,直到调用socket和connect成功,建立连接。为了避免内存泄漏,最后需要调用freeaddrinfo函数,释放该链表。如果getaddrinfo返回非零的错误代码,程序可以调用gai_streeror将该代码转成消息字符串。

虽然很简单,服务器也是以这做为基石。对于前端同学了解服务器端的运行还是有很大的帮助。
多说两句,serverapp运行在本地,clientapp在局域网任何一台电脑上运行clientapp

./clientapp 172.32.42.42 8887  / **serverapp 所在局域网的IP地址*/

可能你会想,为什么只是局域网不是所有外网?当然也是可以运行外网,可是我们的外网IP地址都被路由器给屏蔽了呀,具体可参见NAT协议
最后吐槽下:在看网络相关的书籍的时候,总是搞不懂为什么会有如此多的协议,比如说有了IP地址为什么还要搞个mac地址,知乎上有这个问题,很多人的回答跟我猜想的一样,网络这个东西经过那么多人的改动,在不停的妥协和兼容下,越来越庞大,也越来越累赘,当然他们也是伟大的先知。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的使用C语言实现MQTT客户端连接服务器代码示例: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include "MQTTClient.h" #define ADDRESS "tcp://localhost:1883" #define CLIENTID "ExampleClientSub" #define TOPIC "MQTT Examples" #define QOS 1 #define TIMEOUT 10000L int main(int argc, char* argv[]) { MQTTClient client; MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer; int rc; MQTTClient_create(&client, ADDRESS, CLIENTID, MQTTCLIENT_PERSISTENCE_NONE, NULL); conn_opts.keepAliveInterval = 20; conn_opts.cleansession = 1; if ((rc = MQTTClient_connect(client, &conn_opts)) != MQTTCLIENT_SUCCESS) { printf("Failed to connect, return code %d\n", rc); exit(EXIT_FAILURE); } printf("Connected to MQTT broker\n"); MQTTClient_disconnect(client, 10000); MQTTClient_destroy(&client); return rc; } ``` 这个示例使用了Eclipse Paho提供的MQTTClient库,需要在代码中包含MQTTClient.h头文件,并且链接MQTTClient库。 在代码中,首先定义了MQTT服务器的地址、客户端ID、订阅主题、消息QoS等信息,然后创建了一个MQTTClient实例,并设置了连接选项,最后调用MQTTClient_connect()方法连接MQTT服务器。 如果连接成功,MQTTClient_connect()方法返回MQTTCLIENT_SUCCESS,否则返回其他错误码。连接成功后,可以进行MQTT消息的订阅和发布等操作。 最后,调用MQTTClient_disconnect()方法断开MQTT连接,并销毁MQTTClient实例。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值