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

今天介绍的是基于ipv4的socket网络编程,我们知道socket API是一层的抽象的网络编程接口,但各网络协议的地址却是各不相同的。
下图是sockaddr数据结构图:
socket编程之TCP单进程的服务器_第1张图片
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编程之TCP单进程的服务器_第2张图片
socket API是一套通用的接口,可以接受各种类型的sockaddr结构体指针,但是参数需为void*类型的,由于socket API早于ANSIC所以那时没有void*类型,所以我们要将函数参数转为struct sockaddr *类型。

TCP连接所用到的一些函数

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

2、bind
socket编程之TCP单进程的服务器_第4张图片
函数作用:声明sockfd所处的状态为监听状态,将套接字地址与所创建字号联系起来
参数:
af: 通信发生的区域
type: 要建立的套接字类型
procotol: 使用的特定协议

3、accept和connect
socket编程之TCP单进程的服务器_第5张图片

socket编程之TCP单进程的服务器_第6张图片
这两个是为了完成连接,参数和上面的相同。accept是服务器端,connect是客户端
4、listen
socket编程之TCP单进程的服务器_第7张图片
功能:面向连接服务器,表明它愿意接收连接
返回值:成功返回0,失败返回-1

代码展示:
tcp_server.c:

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


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
#include
#include
#include
#include
#include
#include
#include

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;
}

socket编程之TCP单进程的服务器_第8张图片

socket编程之TCP单进程的服务器_第9张图片
上面只是做了一个本地的测试,在同一局域网的两台主机也是可以进行通信的。

你可能感兴趣的:(Linux,计算机网络,计算机网络,linux学习历程)