TCP/IP编程入门-案例学习

本文通过一个简单的案例介绍如何使用C语言实现基本的网络通信。案例包括一个服务器端程序,用于接收客户端发送的两个数值及运算符,并返回计算结果;以及一个客户端程序,用于输入数值并获取计算结果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

     从作为一名数据分析从业者,一直不太了解后台开发岗位所使用的各种网络编程方法,于是也就老想闹明白,这个东西究竟是咋玩的? 本文试图通过一个小案例,来管窥下这个领域的编程套路。参考的书籍主要是《TCP/IP网络编程》(尹圣丽 著 金国哲 译)。

我们知道,网络编程主要就是实现服务端与客户端的通信,那么就涉及到一系列通信过程的协议,也就是双方要商量好有一套共同的语境。这就又跳到计算机网络知识中的OSI网络体系模型,共分了7层(具体可参考相关书籍)。实际应用中,可简化理解为4层,从上往下为 应用层、传输层、网络层、链路层。TCP就是传输层中的一种协议,是一种基于连接的,比较可靠的,不会发生数据丢失的协议,相对应的是UDP协议。 IP对应的是网络层,这其中也有一堆不同的协议,如IPv4, IPv6等。

在编写网络通信过程时,最重要的几个环节如下:

对于服务端: 建立套接字、绑定网络地址、监听、接受、读取/写入、关闭连接

对于客户端:建立套接字、请求连接、读取/写入、关闭连接

      

案例问题

    1. 写一个server,根据客户端传入的两个数值,进行数学计算,如加减乘除。

    2. 写一个client,向server传入若两个数值,以及所需的计算方法


练习过程

以下练习过程在一台笔记本上完成,server, client部署在同一个机器上。编程语言为C。

服务端代码如下:

#include <sys/socket.h>
#include <netinet/in.h>
#include <strings.h>
#include <printf.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <zconf.h>

#define BUF_SIZE 1024

long doMathWork(char op, int num1, int num2);

int main(int argc, char* argv[]){

    int server_sock, client_sock;
    char msg[BUF_SIZE];
    size_t str_len;
    //声明网络地址的结构体
    struct sockaddr_in server_addr;  //服务端网络地址
    struct sockaddr_in client_addr;  //客户端网络地址
    int i;

    socklen_t client_addr_size;

    //声明计算类型, 以及两个数值变量
    char op;
    int num1, num2;
    long result;

    if(argc != 2){
        printf("Usage: %s <port> \n", argv[0]);
        exit(1);
    }

    char* port = argv[1];

    //第1步, 建立服务端套接字
    server_sock = socket(PF_INET /*IPv4*/,SOCK_STREAM /*TCP*/,0);
    if(server_sock == -1){
        fputs("socket error", stderr);
        fputc('\n', stderr);
        exit(-1);
    }

    //第2步, 绑定网络地址
    //定义网络地址结构体
    bzero(&server_addr, sizeof(server_addr));
    server_addr.sin_family = AF_INET;  //IPv4地址族
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    server_addr.sin_port = htons(atoi(port));

    if(bind(server_sock, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1){
        fputs("bind error", stderr);
        fputc('\n', stderr);
        exit(-2);
    }

    //第3步, 建立监听
    if(listen(server_sock, 1) == -1){
        fputs("listen error", stderr);
        fputc('\n', stderr);
        exit(-3);
    }

    //第4步, 接受访问
    client_addr_size = sizeof(client_addr);
    client_sock = accept(server_sock, (struct sockaddr*)&client_sock, &client_addr_size);
    if(client_sock == -1){
        fputs("accept error", stderr);
        fputc('\n', stderr);
        exit(-4);
    }else{
        printf("Connected to client........\n");
    }

    //第5步, 传输内容
    //首先接收一个计算类型, 如加减乘除
    read(client_sock, msg, BUF_SIZE-1);
    msg[strlen(msg)-1] = 0;
    op = msg[0];
    printf("op: %c \n", op);

    read(client_sock, msg, BUF_SIZE-1);
    num1 = atoi(msg);
    printf("num1: %d \n", num1);

    read(client_sock, msg, BUF_SIZE-1);
    num2 = atoi(msg);
    printf("num2: %d \n", num2);

    result = doMathWork(op, num1, num2);
    write(client_sock, (char*)&result, sizeof(result));

    //第6步, 关闭连接
    close(client_sock);
    close(server_sock);

    return 0;
}


long doMathWork(char op, int num1, int num2){
    long result = 0L;
    switch(op){
        case '+':
            result = num1 + num2;
            break;
        case '-':
            result = num1 - num2;
            break;
        case '*':
            result = num1 * num2;
            break;
        case '/':
            if(num2 == 0){
                fputs("cannot divide zero",stderr);
                fputc('\n',stderr);
                exit(-1);
            }
            result = num1 / num2;
            ;
        default:
            printf("You have input %c \n", op);
            fputs("only allow input: + - * / \n", stderr);
            break;
    }
    printf("result: %li \n" , result);
    return result;
}

客户端代码

#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <strings.h>
#include <arpa/inet.h>
#include <zconf.h>

#define BUF_SIZE 1024

//注意到服务端程序中,有多个地方使用类似的逻辑来处理函数返回值,故可定义一个公用的函数
void error_msg(char* message);

int main(int argc, char* argv[]){

    int client_sock;
    struct sockaddr_in server_addr;
    char* message[BUF_SIZE];

    int str_len;
    long result;

    char* ip;
    char* port;
    if(argc != 3){
        printf("Usage: %s <IP> <PORT>" , argv[0]);
        exit(-1);
    }
    ip = argv[1];
    port = argv[2];

    //第1步, 建立套接字
    client_sock = socket(PF_INET,SOCK_STREAM,0);
    if(client_sock == -1) error_msg("socket() error");

    //第2步, 连接服务端套接字
    bzero(&server_addr,sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = inet_addr(ip);
    server_addr.sin_port = htons(atoi(port));

    if(connect(client_sock, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1){
        error_msg("connect() error");
    }else{
        puts("Connection Created ............\n");
    }

    //第3步, 通信内容
    printf("Input a kind of opeator(+ - * /): ");
    fgets((char *) message, BUF_SIZE, stdin);
    write(client_sock, message, strlen((const char *) message));

    printf("Input the first number: ");
    fgets((char *) message,BUF_SIZE, stdin);
    write(client_sock, message, strlen((const char *) message));

    printf("Input the second number: ");
    fgets((char *) message,BUF_SIZE, stdin);
    write(client_sock, message, strlen((const char *) message));

    read(client_sock, &result, 4);
    printf("math result: %li \n", result);

    //第4步, 关闭连接
    close(client_sock);
    return 0;
}

void error_msg(char* message){
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}

运行结果

       服务端:


              

客户端:


本例子就是Delphi中如何利用Socket编写通信程序。 计算机网络是由一系列网络通信协议组成的,其中的核心协议是传输层的TCP/IP和UDP协议。TCP是面向连接的,通信双方保持一条通路,好比目前的电话线,使用telnet登陆BBS,用的就是TCP协议;UDP是无连接的,通信双方都不保持对方的状态,浏览器访问Internet时使用的HTTP协议就是基于UDP协议的。TCP和UDP协议都非常复杂,尤其是TCP协议,为了保证网络传输的正确性和有效性,必须进行一系列复杂的纠错和排序等处理。   Socket是建立在传输层协议(主要是TCP和UDP)上的一种套接字规范,最初是由美国加州Berkley大学提出,它定义两台计算机间进行通信的规范(也是一种编程规范),如果说两台计算机是利用一个“通道“进行通信,那么这个“通道“的两端就是两个套接字。套接字屏蔽了底层通信软件和具体操作系统的差异,使得任何两台安装了TCP协议软件和实现了套接字规范的计算机之间的通信成为可能。   微软的Windows Socket规范(简称winsock)对Berkley的套接字规范进行了扩展,利用标准的Socket的方法,可以同任何平台上的Socket进行通信;利用其扩展,可以更有效地实现在Windows平台上计算机间的通信。在Delphi中,其底层的Socket也应该是Windows的Socket。Socket减轻了编写计算机间通信软件的难度,但总的说来还是相当复杂的(这一点在后面具体会讲到);Inprise在Delphi中对Windows Socket进行了有效的封装,使得用户可以很方便地编写网络通信程序。 本例子就是Delphi中如何利用Socket编写通信程序。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值