网络编程--采用TCP协议的C/S架构实现

采用TCP协议的C/S架构实现


以下例子虽然实现了多个客户端访问,但是仍然是阻塞模式,即一个客户访问时会阻塞其他客户的连接和访问)。


模块封装


将一些通用的代码全部封装起来,以后直接调用函数即可:
通过网络封装代码头文件为:tcp_net_socket.h

#ifndef __TCP__NET__SOCKET__H
#define __TCP__NET__SOCKET__H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <signal.h>

extern int tcp_init(const char * ip, int port);
extern int tcp_accept(int sfd);
extern int tcp_connect(const char * ip, int port);
extern void signalhandler(void);

#endif

具体的通用函数封装如下:(tcp_net_socket.c)

#include "tcp_net_socket.h"

// 初始化操作,包括以下操作
// 1. 建立监听 socket
// 2. 把本地 IP 和端口信息,绑定到监听socket;
// 3. 进行监听
int tcp_init(const char * ip, int port)
{
    // 建立本地socket
    int sfd = socket(AF_INET, SOCK_STREAM, 0);
    if(-1 == sfd) {
        perror("socket");
        exit(-1);
    }

    // bind:把本地 Socket 绑定到主机地址上
    struct sockaddr_in serveraddr;
    bzero(&serveraddr, sizeof(struct sockaddr));
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_port = htons(port);
    serveraddr.sin_addr.s_addr = inet_addr(ip);
    if(-1 == bind(sfd, (struct sockaddr *)&serveraddr, sizeof(struct sockaddr))) {
        perror("bind");
        close(sfd);
        exit(-1);
    }

    // listen:监听是否有客户端的请求
    if(-1 == listen(sfd, 10)) {
        perror("listen");
        close(sfd);
        exit(-1);
    }

    return sfd;
}

// 用于服务器端的接收,主要工作如下:
// 1. 接受来自客户端的请求,并返回与客户端对应的新socket;
int tcp_accept(int sfd)
{
    // 建立新socket
    struct sockaddr_in clientaddr;
    bzero(&clientaddr, sizeof(struct sockaddr));
    int addrlen = sizeof(struct sockaddr);
    int new_fd = accept(sfd, (struct sockaddr *)&clientaddr, &addrlen);
    if(-1 == new_fd) {
        perror("accept");
        close(sfd);
        exit(-1);
    }
    printf("%s %d success connect\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));

    return new_fd; // 返回客户端的 Socket
}

// connect:客户但主动向服务器发起连接请求。
int tcp_connect(const char * ip, int port)
{
    // 建立socket 
    int sfd = socket(AF_INET, SOCK_STREAM, 0);
    if(-1 == sfd) {
        perror("socket");
        exit(-1);
    }

    // 填写服务器IP和端口信息到socket的
    struct sockaddr_in serveraddr;
    bzero(&serveraddr, sizeof(struct sockaddr));
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_port = htons(port);
    serveraddr.sin_addr.s_addr = inet_addr(ip);

    // 向服务器发起连接请求
    if(-1 == connect(sfd, (struct sockaddr *)&serveraddr, sizeof(struct sockaddr))) {
        perror("connect");
        close(sfd);
        exit(-1);
    }

    return sfd; // 返回服务器的 Socket
}

// 忽略中断和退出信号
// 让服务器在按下 Ctrl + C 或Ctrl + \时不会退出
void signalhandler(void)
{
    sigset_t sigSet;
    sigemptyset(&sigSet);
    sigaddset(&sigSet, SIGINT);
    sigaddset(&sigSet, SIGQUIT);
    sigprocmask(SIG_BLOCK, &sigSet, NULL);
}

服务器的实现


服务器端:tcp_net_server.c

#include "tcp_net_socket.h"

int main(int argc, char * argv[])
{
    if(argc < 3) {
        printf("usage:./servertcp ip port\n");
        exit(-1);
    }

    // 注册信号
    signalhandler();

    // 初始化socket
    int sfd = tcp_init(argv[1], atoi(argv[2]));

    // 用while循环表示可以与多个客户端接收和发送,但仍然是阻塞式的
    while(1) {
        // 接收客户端的连接
        int cfd = tcp_accept(sfd);
        char buf[512] = {0};

        // 与客户端进行通信
        if(-1 == recv(cfd, buf, sizeof(buf), 0)) {
            perror("recv");
            close(cfd);
            close(sfd);
            exit(-1);
        }
        printf("%s: %d: 正在处理...\n", __FILE__, __LINE__);
        puts(buf);
        usleep(5000000);
        printf("%s: %d: 处理完成...\n", __FILE__, __LINE__);
        if(-1 == send(cfd, "hello, world", 12, 0)) {
            perror("send");
            close(cfd);
            close(sfd);
            exit(-1);
        }
        close(cfd);
    }
    close(sfd);
    return 0;
}

客户端的实现


客户端:tcp_net_client.c

#include "tcp_net_socket.h"

int main(int argc, char * argv[])
{
    if (argc < 3) {
        printf("usage: ./clientaddr ip port\n");
        exit(-1);
    }

    // 建立连接
    int sfd = tcp_connect(argv[1], atoi(argv[2]));
    char buf[512] = {0};
    send(sfd, "hello", 6, 0);
    recv(sfd, buf, sizeof(buf), 0);
    puts(buf);
    close(sfd);
}

编译执行


# gcc -o tcp_net_server tcp_net_server.c tcp_net_socket.c
# gcc -o tcp_net_client tcp_net_client.c tcp_net_socket.c
# ./tcp_net_server 127.0.1.1 8888
# ./tcp_net_client 127.0.1.1 8888

编译成共享库文件


# gcc -fpic -c tcp_net_socket.c -o tcp_net_socket.o
# gcc -shared tcp_net_socket.o -o libtcp_net_socket.so
# cp lib*.so /lib
# cp tcp_net_socket.h /usr/include

这样头文件包含可以用 include<tcp_net_socket.h>了,以后再用到的时候就可以直接用。

# gcc -o main main.c -l tcp_net_socket
// 其中 main.c 要包含头文件:include <tcp_net_socket.h>
// main.c 代表实际的源文件
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值