套接字(socket)编程简单实现server-client聊天程序

1、socket

      在TCP/IP协议中,一个IP地址标识网络通讯中唯一一台主机,而一个IP地址+一个TCP(或UDP)端口号就可以标识网络通讯中的一个进程,此时的IP地址+端口号即称为socket。
      内存中的多字节数据相对于内存地址有大小端之分,磁盘文件中的多字节数据相对于文件中的偏移地址也有大小端之分,网络数据流同样也有大小端之分:先发出的数据是低地址,后发出的数据是搞地质,TCP/IP协议规定网络数据流采用大端字节序,即低地址高字节。
      1)计算机分为大端机和小端机,那么如何使同样的C代码在大端机和小端机上都能正常运行呢?库函数一节为我们提供了转换的接口:

include
unit32_t htonl(unit32_t hostlong);//32位的长整数从主机字节序转换为网络字节序
unit16_t htons(unit16_t hostshort);//16位的短整数从主机字节序转换为网络字节序
unit32_t ntohl(unit32_t netlong);//32位的长整数从网络字节序转换为主机字节序
unit16_t ntohs(unit16_t netshort);//16位的短整数从网络字节序转换为主机字节序

      2)由于网络传输是二进制比特流传输,所以必须将我们常用的十进制的IP地址与网络字节序的二进制形式的IP源码互相转换才可以将数据传输到准确的地址,下面是地址转换函数介绍:

int inet_aton(const char* cp, struct in_addr *inp);//将字符串cp的十进制转换为网络字节序的二进制形式后存储到inp中
char* inet_ntoa(struct in_addr *in);//将网络字节序的二进制形式转换为十进制的字符串形式,返回字符串的首地址

2、socket地址的数据类型及相关函数

      1)结构体
            IPV4套接字地址结构体:

struct sockaddr_in{
unit8_t sin_len;
sa_famliy_t sin_famliy;//协议家族
in_port_t sin_port;//端口号
struct in_addr  sin_addr;//IP地址
char sin_zero[8];
};

      通用套接字地址结构体:

struct sockaddr{
unit8_t sa_len;
sa_famliy sa_famlity;
char sa_data[14];
};

      2)相关函数
      socket:

套接字(socket)编程简单实现server-client聊天程序_第1张图片

       bind:
套接字(socket)编程简单实现server-client聊天程序_第2张图片

       listen:
套接字(socket)编程简单实现server-client聊天程序_第3张图片

       accept与connect:
套接字(socket)编程简单实现server-client聊天程序_第4张图片

       send和recv:
套接字(socket)编程简单实现server-client聊天程序_第5张图片

       close:
套接字(socket)编程简单实现server-client聊天程序_第6张图片

3、下面通过简单的客户端/服务器程序实例来学习socket API。

      server端即服务器端:服务器由于不知道客户何时回请求建立连接,所以必须绑定端口之后进行监听;
      client端即客户端:只需向服务器端发送连接请求(connect);
      客户端主动发起请求连接,服务器接受连接请求,完成“三次握手”;服务器与客户端都可以发起断开连接请求,完成”四次挥手“的过程。
      基本框架图(此图为摘录):

套接字(socket)编程简单实现server-client聊天程序_第7张图片

代码如下:
server.c:

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

#define _PORT_ 9999
#define _BACKLOG_ 10

int main()
{
    int sock = socket(AF_INET, SOCK_STREAM, 0);
    if(sock < 0){
        perror("socket");
        exit(1);
    }

    int opt = 1;
    setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    struct sockaddr_in server_socket;
    struct sockaddr_in client_socket;                                           
    bzero(&server_socket, sizeof(server_socket));
    server_socket.sin_family = AF_INET;
    server_socket.sin_addr.s_addr = htonl(INADDR_ANY);
    server_socket.sin_port = htons(_PORT_);

    if(bind(sock, (struct sockaddr*)&server_socket, sizeof(struct sockaddr_in)) < 0)
    {
        perror("bind");
        close(sock);
        exit(2);
    }

    if(listen(sock, _BACKLOG_) < 0)
    {
        perror("listen");
        close(sock);
        exit(3);
    }

    printf("bind and listen success, wait accept..\n");

    while(1)
    {
        socklen_t len = 0;                                                      
        int client_sock = accept(sock, (struct sockaddr*)&client_socket, &len);
        if(client_sock < 0)
        {
            perror("accept");
            close(sock);
            exit(4);
        }
        char buf_ip[INET_ADDRSTRLEN];
        memset(buf_ip, '\0', sizeof(buf_ip));
        inet_ntop(AF_INET,&client_socket.sin_addr, buf_ip, sizeof(buf_ip));
        printf("get connect, ip is : %s port is : %d\n",buf_ip, ntohs(client_socket.sin_port));
        while(1)
        {
            char buf[1024];
            memset(buf, '\0',sizeof(buf));
            read(client_sock, buf, sizeof(buf));
            printf("client#:%s\n",buf);
            printf("server#:");

            memset(buf, '\0', sizeof(buf));
            fgets(buf, sizeof(buf), stdin);
            buf[strlen(buf)-1] = '\0';
            write(client_sock, buf, strlen(buf)+1);
            printf("please wait...\n");                                         
        }
    }
    close(sock);
    return 0;
}

client.c:

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

#define SERVER_PORT 9999
#define SERVER_IP "192.168.43.121"

int main(int argc, char *argv[])
{
    if(argc != 2)
    {   printf("Usage: client IP \n");
        return 1;
    }
    char *str = argv[1];
    char buf[1024];
    memset(buf, '\0', sizeof(buf));

    struct sockaddr_in server_sock;
    int sock = socket(AF_INET, SOCK_STREAM, 0);
    bzero(&server_sock, sizeof(server_sock));
    server_sock.sin_family = AF_INET;
    inet_pton(AF_INET, SERVER_IP, &server_sock.sin_addr);
    server_sock.sin_port = htons(SERVER_PORT);
    int ret = connect(sock, (struct sockaddr*)&server_sock, sizeof(server_sock));
    if(ret < 0)
    {
        printf("perror");
        return 1;
    }
    printf("connect success....\n");
    while(1)
    {
        printf("client#:");
        fgets(buf, sizeof(buf), stdin);
        buf[strlen(buf)-1] = '\0';
        write(sock, buf, sizeof(buf));
        if(strncasecmp(buf, "quit", 4) == 0)
        {
            perror("perror");
            break;
        }
        printf("please wait...\n");
        read(sock, buf, sizeof(buf));
        printf("server$: %s\n",buf);                                            
    }
    close(sock);
    return 0;
}

4、测试结果

      打开一个terminal运行服务器端server,再打开另外一个terminal运行客户端client,并加上通过ifconfig查出的IP地址即可实现服务器端与客户端的连接。

这里写图片描述

你可能感兴趣的:(Linux)