使用linux套接字实现的简易服务器与客户端

别来无恙 提交于 2020-01-09 20:31:23

【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>

本文中的代码是我在学习linux套接字编程时写的,运行环境为RedHat,编译环境为G++

测试环境有两台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)为

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

编译这两个文件,分别输入命令

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

运行这两个程序前,先要关闭linux的防火墙。

方法为在su权限下输入命令

iptables -F

先在198.168.8.99上运行服务器server,然后再在192.168.8.123上运行client。在客户端输入字符串Hello World!,服务器端收到了这个字符串后会在前面加入字符串“[RETURN] ”然后返回给客户端。效果如下图所示

server端

client端

关于这两段代码,需要注意两点:

1)因为TCP连接在最后有一个TIME_WAIT状态,因此在server程序停掉后,要隔一分钟打开,才能用client重新连接上

2)G++的检查比GCC要严格,服务器server.cpp代码中的server_len和client_len,不能声明为int类型,而要声明为socklen_t类型

END

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!