socket编程之TCP单进程的服务器

本文介绍了基于ipv4的socket网络编程,讲解了socket API如何作为抽象接口处理不同类型的地址,并详细阐述了TCP连接中涉及的socket、bind、listen、accept和connect等关键函数的作用及参数。通过示例代码展示了简单的TCP服务器(tcp_server.c)和客户端(tcp_client.c)的实现,这些代码适用于在同一局域网内的主机间通信。

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

今天介绍的是基于ipv4的socket网络编程,我们知道socket API是一层的抽象的网络编程接口,但各网络协议的地址却是各不相同的。
下图是sockaddr数据结构图:
这里写图片描述
ipv4和ipv6的地址格式定义在netinet/in.h中,ipv4地址用sockaddr_in结构体表示,包括16位的端口号和32位的ip地址,ipv6地址用sockaddr_in6表示,包括16位的端口号和128位的ip地址和一些控制字段。UNIX Domain Socket的地址格式定义在sys/un.h中,⽤用sockaddr_un结构体表 ⽰示。
IPv4、IPv6和UNIX Domain Socket的地 址类型分别定义为常数AF_INET、AF_INET6、AF_UNIX。所以,我们根据结构体首地址类型字段就可以确定结构体中的内容。

sockaddr_in在/usr/include/usr/include/linux/in.h里面
这里写图片描述
socket API是一套通用的接口,可以接受各种类型的sockaddr结构体指针,但是参数需为void*类型的,由于socket API早于ANSIC所以那时没有void*类型,所以我们要将函数参数转为struct sockaddr *类型。

TCP连接所用到的一些函数

1、socket函数
这里写图片描述
作用 :socket函数是一种可用于根据指定的地址族、数据类型和协议来分配一个套接口的描述字及其所用的资源的函数
参数:
domain:  一个地址描述。目前仅支持AF_xxxx格式:AF_INET代表IPv4地址
type:  新套接字的类型描述:SOCK_STREAM : 提供面向连接的稳定数据传输,即TCP协议。 SOCK_dGRAM:表示面向数据报的传输协议,即UDP协议
protocol : 套接字所用的协议。如调用者不想指定,可用0指定,表示缺省。
返回值:成功返回文件描述符,失败返回-1

2、bind
这里写图片描述
函数作用:声明sockfd所处的状态为监听状态,将套接字地址与所创建字号联系起来
参数:
af: 通信发生的区域
type: 要建立的套接字类型
procotol: 使用的特定协议

3、accept和connect
这里写图片描述

这里写图片描述
这两个是为了完成连接,参数和上面的相同。accept是服务器端,connect是客户端
4、listen
这里写图片描述
功能:面向连接服务器,表明它愿意接收连接
返回值:成功返回0,失败返回-1

代码展示:
tcp_server.c:

#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<fcntl.h>
#include<unistd.h>


static usage(const char* proc)
{
    printf("Usage: %s[local_ip] [local_port]\n",proc);
}

int startup(const char* ip,int port)
{
    int sock = socket(AF_INET,SOCK_STREAM,0);  //创建socket
    if(sock < 0)
    {
        perror("socket");
        exit(2);
    }
    printf("sock:%d\n",sock);

    struct sockaddr_in local;
    local.sin_family = AF_INET;
    local.sin_port = htons(port);
    local.sin_addr.s_addr = inet_addr(ip);

    if(bind(sock,(struct sockaddr*)&local,sizeof(local)) < 0)  //进行绑定
    {
        perror("bind");
        exit(3);
    }

    if(listen(sock,10) < 0)  //将资源设置为监听状态
    {
        perror("listen");
        exit(4);
    }


    return sock;
}



int main(int argc,char* argv[])
{
    if(argc != 3)
    {
        usage(argv[0]);
        return 1;
    }
    int listen_sock = startup(argv[1],atoi(argv[2]));//上面返回的是listen的sockt

    while(1)
    {
        struct sockaddr_in client;
        socklen_t len = sizeof(client);
        int new_sock = accept(listen_sock,(struct sockaddr*)&client,&len);//监听套接字

        if(new_sock < 0)
        {
            perror("accept");
            continue;
        }

    //printf("get a new client [%s:%d]\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));
    printf("get a new client\n");
    //客户端和服务器未能建立连接   继续监听

    //先read后write
    while(1)
    {
        char buf[1024];
        ssize_t s = read(new_sock,buf,sizeof(buf)-1);
        if(s > 0)
        {
            buf[s] = 0;
            printf("client# %s\n",buf);
            write(new_sock,buf,strlen(buf));
        }
        else if(s == 0 )
        {
            printf("client close!!!\n");
            break;
        }
        else
        {
            perror("read");
            break;
        }
    }
    }
    return 0;

}

tcp_client.c:

#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<fcntl.h>

static void usage(const char* proc)
{
    printf("%s [local_ip] [local_port]\n",proc);
}

int main(int argc,const char* argv[])
{
    if(argc != 3)
    {
        usage(argv[0]);
        return 1;
    }

    int sock = socket(AF_INET,SOCK_STREAM,0);
    if(sock < 0)
    {
        perror("socket");
        return 2;
    }
    struct sockaddr_in server;
    server.sin_family = AF_INET;
    server.sin_port = htons(atoi(argv[2]));
    server.sin_addr.s_addr = inet_addr(argv[1]);

    if(connect(sock,(struct sockaddr*)&server,sizeof(server)) < 0)
    {
        perror("connect");
        return 3;
    }

    //客户端不需要绑定  也不需要监听
    //客户端是先write后read

    char buf[1024];
    while(1)
    {
        printf("please Enter#  ");
        fflush(stdout);
        ssize_t s = read(0,buf,sizeof(buf)-1);
        if(s > 0)
        {
            buf[s-1] = 0;
            write(sock,buf,strlen(buf));
            ssize_t _s = read(sock,buf,sizeof(buf)-1);
            if(_s > 0 )
            {
                buf[_s] = 0;
                printf("server echo# %s\n",buf);
            }
        }
    }


    close(sock);
    return 0;
}

这里写图片描述

这里写图片描述
上面只是做了一个本地的测试,在同一局域网的两台主机也是可以进行通信的。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值