使用套接字实现简单TCP服务器客户端模型

利用套接字实现一个简单的TCP服务器客户端模型基本步骤如下:

1.创建套接字
#include
#include
 int socket(int domain, int type, int protocol);
参数描述:
     domian:协议域,AF_INET 对应 ipv4,AF_INET6  对应  ipv6, AF_UNIX 表示这个socket是非网络形式的unix域,可以用来进行非网络形式的进程间通信。
     type:指定socket类型
     1.SOCK_STREAM 这个协议是按照顺序的、可靠的、数据完整的基于字节流的连接。  这是一个使用最多的socket类型,这个socket是使用TCP来进行传输。
     2.SOCK_DGRAM 这个协议是无连接的、固定长度的传输调用。该协议是不可靠的,使用UDP来进行它的连接。
     3.SOCK_RAW 这个socket类型提供单一的网络访问,这个socket类型使用ICMP公共协议。(ping、traceroute使用该协议)
返回值:
     成功返回socket描述符,失败返回-1

2.将服务器IP与端口和套接字进行绑定
#include
#include
int bind(int sockfd, const struct sockaddr *addr,  socklen_t addrlen);
参数描述:
     sockfd:通过socket()函数创建,用来表示唯一一个socket
     addr:指向要绑定给sockfd的协议地址(协议类型,IP,端口号)
     addrlen:对应地址长度
返回值:
     成功返回0,失败返回-1
注意:
在IPv4因特网域AF_INET中,套接字地址用结构sockaddr_in表示,如下:
struct sockaddr_in
{
     sa_family_t sin_family;     //unsigned short 地址族
     in_port_t sin_sport;         //uint16_t
     struct in_addr sin_addr;  //IPv4
};
struct in_addr
{
     in_addr_t s_addr;            //uint32_t
};

3.监听请求
#include
#include
int listen(int sockfd, int backlog);
参数描述:
     sockfd:要监听的套接字
     backlog:连接队列长度
     backlog 具体一些是什么意思呢?每一个连入请求都要进入一个连入请求队列,等待listen 的程序调用accept()函数来接受这个连接。当系统还没有调用accept()函数的时候,如果有很多连接,那么本地能够等待的最大数目就是backlog 的数值。
返回值:
     成功返回0,失败返回-1

4.服务器端接受监听到的请求
当服务器端进行监听到一个连接请求时,就可以使用accept()函数来接受这个请求。
#include
#include
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
参数描述:
     socket:服务器的套接字
     addr:用于存储客户端的协议地址(协议类型,IP,端口号)
     addrlen:地址长度(sizeof(addr))
返回值:
     成功返回一个新的套接字描述符,代表与返回客户端的TCP连接

5.客户端连接服务器
#include
#include
int connect(int sockfd, const struct sockaddr *addr,  socklen_t addrlen);
参数描述:
     sockfd:客户端的sockfd
     addr:用于存储服务器的协议地址(协议类型,IP,端口号)
     addrlen:地址长度(sizeof(addr))
返回值:
     成功返回0,失败返回-1

注意:
在实现过程中还应注意网络字节序的问题,主机的字节序是不确定的,有的使用大端存储,有的使用小端存储,为了不使网络通信因为机器之间存储模式而造成错误,TCP/IP协议规定网络数据流使用大端字节序。
下面库函数可以完成主机序列和为网络序列的转换:
#include
//主机序列到网络序列
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
//网络序列到主机序列
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);

server:
#include
#include
#include
#include
#include
#include
#include
static void Usage(char* proc)
{
    printf("Usage: %s [local_ip] [local_port]\n", proc);
}

int startup(char* ip, int port)
{
    //1.创建文件特性套接字
    //AF_INET ipv4,SOCK_STREAM,基于字节流服务
    int sock = socket(AF_INET, SOCK_STREAM, 0);
    if(sock < 0)
    {
    perror("socket");
    return 2;
    }
    printf("sock =  %d\n", sock);

    struct sockaddr_in local;
    //确定地址协议类型
    local.sin_family = AF_INET;
    //绑定端口
    local.sin_port = htons(port);
    //绑定ip
    //这里的IP是点分十进制,使用inet_addr()可以将其转换成二进制
    local.sin_addr.s_addr = inet_addr(ip);
    //2.绑定网络特性将套接字与服务器ip和端口号绑定
    if(bind(sock, (struct sockaddr*)&local, sizeof(local)) < 0)
    {
    perror("bind");
    return 3;
    }
    //3.监听请求
    if(listen(sock, 10) < 0)
    {
    perror("listen");
    return 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]));
    while(1)
    {
    //用于存储连接的客户端地址信息(地址类型,IP,端口号)
    struct sockaddr_in client;
    socklen_t len = sizeof(client);
    //4.接受监听到的连接
    int new_sock = accept(listen_sock,(struct sockaddr*)&client, &len);
    if(new_sock < 0)
    {
        perror("accept");
        return 5;
    }
    while(1)
    {
        char buf[1024];
        //从套接字读取信息到buf中
        ssize_t s = read(new_sock, buf, sizeof(buf) - 1);
        if(s > 0)
        {
        buf[s] = 0;
        printf("client#  %s\n", buf);
        printf("Please Enter# ");
        fflush(stdout);
        //从键盘输入信息到buf中
        ssize_t _s = read(0, buf, sizeof(buf)-1);
        buf[_s-1] = 0;
        //发送信息到套接字
            write(new_sock, buf, strlen(buf));   
        }
        else if(s == 0)
        {
        printf("client colse !!\n");
        return 6;
        }
        else
        {
        perror("read");
        return 7;
        }
    }

    }
    return 0;
}
client:
#include
#include
#include
#include
#include
#include

static void Usage(char *proc)
{
    printf("Usage %s [server_ip] [server_port]\n", proc);
}
int main(int argc, 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("connect111");
    return 3;
    }
    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;
        printf("server# ");
        fflush(stdout);
        //将缓冲区内容通过套接字发送到服务器
        write(sock, buf, strlen(buf));
        //在从套接字中读取服务器的回应信息
        ssize_t _s = read(sock, buf, sizeof(buf)-1);
        if(_s > 0)
        {
        buf[_s] = 0;
        printf("%s\n", buf);
        }
        if(s < 0)
        {
        perror("read");
        return 4;
        }
    }
    }
    return 0;
}

你可能感兴趣的:(计算机网络,Linux)