使用linux套接字實現的簡易服務器與客戶端

本文中的代碼是我在學習linux套接字編程時寫的,運行環境爲RedHat,編譯環境爲G++linux

測試環境有兩臺linux虛擬機,地址分別設爲192.168.8.123和192.168.8.99編程

其中,192.168.8.123上運行了客戶端,其源碼(client.cpp)爲服務器

#include<sys/types.h>     //data types
#include<sys/socket.h>    //main sockets header
#include<stdio.h>         //standard buffered input/output
#include<netinet/in.h>    //Internet address family
#include<arpa/inet.h>     //definitions for internet operations
#include<unistd.h>        //standard symbolic constants and types
#include<stdlib.h>        //standard library definitions
#include<string.h>        //string operions

int main()
{
    struct sockaddr_in address;
    //#include<netinet/in.h>
    //struct sockaddr_in includes at least the following member:
    //  sa_family_t     sin_family  AF_INET
    //  in_port_t       sin_port    Port number
    //  struct in_addr  sin_addr    IP address
    //The sockaddr_in structure is used to store addresses 
    //for the Internet address family 

    address.sin_family = AF_INET;
    address.sin_addr.s_addr = inet_addr("192.168.8.99");
    address.sin_port = htons(9734);
    int len = sizeof(address);

    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    //#include<sys/types.h>
    //#include<sys/socket.h>
    //int socket(int domain, int type, int protocal);
    //0.socket() creates an endpoint for communication and returns a descriptor
    //1.[domain] argument specifies a communication domain
    //  Name       Purpose
    //  AF_UNIX    Local communication
    //  AF_INET    IPv4 Internet protocols
    //2.socket has the indicated [type] which specifies the communication semantics
    //  SOCK_STREAM: Provides sequenced, reliable, two-way, 
    //               connection-based byte streams
    //3.The [protocol] specifies a particular protocol to be used with the socket.
    //  Normally only a single protocol exists to support a particular socket
    //  type within a given protocol family, in which case protocol can be 
    //  specified as 0

    int result = connect(sockfd, (struct sockaddr *)&address, len);
    //#include<sys/types.h>
    //#include<sys/socket.h>
    //int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
    //0.The connect() system call connects the socket referred to by the file
    //  descriptor [sockfd] to the address specified by [addr]. The [addrlen]
    //  argument specifies the size of [addr]
    //1.If the connection or binding succeeds, zero is returned.
    //  On error, -1 is returned, and [errno] is set appropriately.

    if(result == -1)
    {
        perror("oops: client");
        //#include<stdio.h>
        //void perror(const char *s);
        //0.The routine perror() produces a message on the standard
        //  error output, describing the last error encountered
        //  during a call to a system or library function.

        exit(1);
        //#include<stdlib.h>
        //void exit(int status);
        //0.The exit() function causes normal process termination 
        //  and the value of [status & 0377] is returned to the parent
    }

    char input[100];
    printf("Please Enter Your Input: ");
    fgets(input, 100, stdin);
    for(int counter = 0; counter < strlen(input); counter++)
    {
        if(input[counter] == '\n')
        {
            input[counter] = '\0';
        }
    }
    write(sockfd, input, 100);
    //#include<unistd.h>
    //ssize_t write(int fd, const void *buf, size_t count);
    //0.write() writes up to [count] bytes from the buffer pointed to the
    //  file referred by the file descriptor [fd]
    //1.On success, the number of bytes written is returned.
    //  On error, -1 is returned, and [errno] is set appropriately.
    printf("Send Input: %s\n", input);

    char output[100];
    read(sockfd, output, 100);
    //#include<unistd.h>
    //ssize_t read(int fd, void *buf, size_t count);
    //0.read() attempts to read up to [count] bytes from file descriptor
    //  [fd] into the buffer starting at [buf]
    //1.On success, the number of bytes read is returned, and the file position
    //  is advanced by this number.
    //  On error, -1 is returned, and [errno] is set appropriately.
    printf("Receive Output: %s\n", output);

    close(sockfd);
    //#include<unistd.h>
    //int close(int fd);
    //0.close() closes a file descriptor, so that it no longer refers to any
    //  file and may be reused. Any record locks held on the file it was 
    //  associated with, and owned by the process, are removed.
    //1.close() returns zero on success.
    //  On error, -1 is returned, and [errno] is set appropriately.

    exit(0);
}

192.168.8.99上運行了服務器,其源碼(server.cpp)爲app

#include<sys/types.h>
#include<sys/socket.h>
#include<stdio.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>

int main()
{
    int server_sockfd, client_sockfd;
    socklen_t server_len, client_len;
    struct sockaddr_in server_address;
    struct sockaddr_in client_address;

    server_sockfd = socket(AF_INET, SOCK_STREAM, 0);

    server_address.sin_family = AF_INET;
    server_address.sin_addr.s_addr = inet_addr("192.168.8.99");
    server_address.sin_port = htons(9734);
    server_len = sizeof(server_address);

    bind(server_sockfd, (struct sockaddr *)&server_address, server_len);
    //#include<sys/types.h>
    //#include<sys/socket.h>
    //int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
    //0.When a socket is create with socket(), it exists in a name space
    //  (address family) but has no address assigned to it. bind() assigns
    //  the address specified to by [addr] to the socket referred to by the
    //  file descriptior [sockfd]. [addrlen] specifies the size, in bytes,
    //  of the address structure pointed to by [addr].
    //  Traditionally, this operation is called "assigning a name to a socket"
    //1.On success, zero is returned. 
    //  On error, -1 is returned, and [errno] is set appropriately.

    listen(server_sockfd, 5);
    //#include<sys/types.h>
    //#include<sys/socket.h>
    //int listen(int sockfd, int backlog)
    //0.listen() marks the socket referred to by [sockfd] as a passive socket,
    //  that is, as a socket that will be used to accept incoming connection
    //  requests using accept()
    //1.The [sockfd] argument is a file descriptor that refers to a socket of
    //  type SOCK_STREAM or SOCK_SEQPACKET
    //2.The [backlog] argument defines the maximum length to which queue of
    //  pending connections for [sockfd] may grow
    //3.On success, zero is returned. 
    //  On error, -1 is returned, and [errno] is set appropriately.

    while(1)
    {
        printf("server waiting\n");

        client_len = sizeof(client_address);
        client_sockfd = accept(server_sockfd, 
            (struct sockaddr *)&client_address, &client_len);
        //#include<sys/types.h>
        //#include<sys/socket.h>
        //int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
        //0.The accept() system call is used with connection-based socket types
        //  (SOCK_STREAM, SOCK_SEQPACKET). It extracts the first connection 
        //  request on the queue of pending connections for the listening socket,
        //  [sockfd], creates a new connected socket, and returns a new file 
        //  descriptor referring to that socket. The newly created socket is 
        //  not in the listening state. The original socket [sockfd] is 
        //  unaffected by this call.

        char input[100];
        read(client_sockfd, input, 100);
        printf("Receive Input: %s\n", input);

        char output[100] = "[RETURN] ";
        strcat(output, input);
        write(client_sockfd, output, 100);
        printf("Send Output: %s\n", output);

        close(client_sockfd);
    }
}

編譯這兩個文件,分別輸入命令dom

g++ client.cpp -o client
g++ server.cpp -o server

運行這兩個程序前,先要關閉linux的防火牆。socket

方法爲在su權限下輸入命令ide

iptables -F

先在198.168.8.99上運行服務器server,而後再在192.168.8.123上運行client。在客戶端輸入字符串Hello World!,服務器端收到了這個字符串後會在前面加入字符串「[RETURN] 」而後返回給客戶端。效果以下圖所示oop

server端學習

client端測試

關於這兩段代碼,須要注意兩點:

1)由於TCP鏈接在最後有一個TIME_WAIT狀態,所以在server程序停掉後,要隔一分鐘打開,才能用client從新鏈接上

2)G++的檢查比GCC要嚴格,服務器server.cpp代碼中的server_len和client_len,不能聲明爲int類型,而要聲明爲socklen_t類型

END

相關文章
相關標籤/搜索