基於TCP的客戶端和服務器端的代碼設計


實驗平台

linux

實驗內容

編寫TCP服務器和客戶端程序,程序運行時服務器等待客戶端連接。一旦連接成功,服務器顯示客戶端的IP地址和端口號,並向客戶端發送字符串

實驗原理

TCP是面向連接的通信,其主要實現過程如下:

我們將服務器代碼分為兩部分。

1. init_tcp_server() tcp服務器的初始化

2. main() 實現讀寫數據

這樣做的好處是main函數不必寫的特別冗長,利於維護。從框架上來說,服務器的初始化也與讀、寫無關。

tcp服務器的初始化----init_tcp_server()

1. 創建socket

sockfd = socket(AF_INET, SOCK_STREAM, 0); //AF_INT:ipv4, SOCK_STREAM:tcp協議

2. 設置socket  當然這一步可以省略

int32_t opt = 1;
ret = setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

詳細說明:

3. 綁定(bind函數)

將socket和地址(包括ip,port)綁定。需要定義一個結構體地址,以便於將port的主機字節序轉化成網絡字節序

struct sockaddr_in serveraddr;    //地址結構體

bind函數

bind(sockfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr))

4. listen監聽,將接收到的客戶端放入隊列

 listen(sockfd,10)  //第二個參數是隊列長度

5. 調用accept函數,從隊列獲取請求,返回socket描述符,如果沒有請求(沒有客戶端連接),將會阻塞,直到獲取請求

int fd=accept(sockfd, (struct sockaddr*)&clientaddr, &clientaddr_len);

至此服務器初始化完成,返回成功連接的套接字fd。

服務器端代碼如下:tcpserver.c

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

#define PORT 1234
#define BACKLOG 10
#define BUFFER_SIZE 100

/**
 * @brief 初始化tcp服務器
 * @param[in] listenfd 監聽套接字
 * @return -1 - 失敗, socket 文件句柄 - 成功
 */
int32_t init_tcp_server(int32_t listenfd)
{
    struct sockaddr_in server;
    struct sockaddr_in client;
    
    int32_t connectfd = 0;    
    int32_t addrlen;
    int32_t ret = 0;
    addrlen = sizeof(client);

    /**< 創建一個tcp套接字 */
    listenfd = socket(AF_INET, SOCK_STREAM, 0);
    if (listenfd == -1)
    {
        perror("create socket failed!\n");
        exit(1);
    }

    /**< 設置一個tcp套接字 */
    int32_t opt = 1;
    ret = setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    if (ret < 0)
    {
        perror("set socket failed!\n");
        exit(1);
    }

    /**< 設置服務器監聽所有的IP地址 */
    bzero(&server, sizeof(struct sockaddr_in));
    server.sin_family = AF_INET;
    server.sin_port = htons(PORT);              /**< 主機字節序轉化成網絡字節序 */
    server.sin_addr.s_addr = htonl(INADDR_ANY);

    /**< 與服務器進行綁定 */
    if (bind(listenfd, (struct sockaddr *)&server, sizeof(server)) == -1)
    {
        perror("bind error");
        exit(1);
    }
    
    /**< 監聽 */
    if (listen(listenfd, BACKLOG) == -1)
    {
        perror("listen error");
        exit(1);
    }

    /**< 等待客戶端連接,如果沒有,一直阻塞 */
    if ((connectfd = accept(listenfd, (struct sockaddr *)&client, &addrlen)) == -1)        
    {
        perror("accept error");
        close(listenfd);
        close(connectfd);
        exit(1);
    }
    printf("You got a connection from client's ip is %s, port is %d\n", inet_ntoa(client.sin_addr), htons(client.sin_port));    

    return connectfd;
}

int main()
{
    int32_t listenfd = 0;
    int32_t connectfd = 0;
    char buf[BUFFER_SIZE] = "Welcome to my server";

    connectfd = init_tcp_server(listenfd);

    send(connectfd, buf, BUFFER_SIZE, 0);    /**< 發送信息到客戶端 */
    close(connectfd);
    close(listenfd);
}


客戶端

同樣,將客戶端代碼分成兩部分:

1. init_tcp_client() tcp客戶端的初始化

2. main() 實現讀寫數據

客戶端的初始化較為簡單,如上圖,只要實現socket和connect函數即可。但是我們希望可以手動輸入客戶端連接的IP地址,便於以后擴展,因此需要給客戶端初始化傳入一個參數。例如,輸入:

./tcpclient 127.0.0.1

客戶端代碼如下:tcpclient.c

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

#define PORT 1234
#define BUFFER_SIZE 100

int32_t init_tcp_client(char *ipaddr)
{
    int sockfd = 0;
    struct sockaddr_in server;

    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
    {
        perror("create socket failed!\n");
        exit(1);
    }
    
    bzero(&server, sizeof(struct sockaddr_in));
    server.sin_family = AF_INET;
    server.sin_port = htons(PORT);
    inet_pton(AF_INET, ipaddr, &server.sin_addr.s_addr);       /**< 點分十進制轉換成二進制的網絡字節序 */
    
    if (connect(sockfd, (struct sockaddr *)&server, sizeof(server)) == -1)
    {
        perror("connect error");
        exit(1);
    }
    
    return sockfd;
}

int32_t main(int argc, char*argv[])
{
    int32_t sockfd, num;
    char buf[BUFFER_SIZE];
    if (argc != 2)
    {
        printf("Usage:%s <IP Address>\n",argv[0]);
        exit(1);
    }
    sockfd = init_tcp_client(argv[1]);

    if ((num = recv(sockfd, buf, BUFFER_SIZE, 0)) == -1)
    {
        perror("recv error");
        exit(1);
    }

    buf[num - 1] = '\0';
    printf("Server Message: %s\n", buf);
    close(sockfd);

    return 0;
}

Makefile文件如下:

all:server client

server:tcpserver.c
    gcc tcpserver.c -o server
    
client:tcpclient.c
    gcc tcpclient.c -o client
    
clean:
    rm -rf server client

實驗結果如下:

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM