macos下簡單的socket服務器+客戶端


TCP客戶端服務器編程模型:

服務器:

  1. 調用socket函數創建套接字
  2. 調用bind綁定本地IP和端口
  3. 調用listen啟動監聽(准備好接收客戶端鏈接的隊列)
  4. 調用accept從已連接隊列中提取第一個連接。(如果沒有,會阻塞。)
  5. 調用I/O函數(read/write)與客戶端通訊
  6. 調用close關閉套接字。(多個套接字)

客戶端:

  1. 調用socket創建套接字
  2. 調用connect連接服務器
  3. 調用I/O函數(read/write)與服務器通訊
  4. 調用close關閉套接字

 

Linux下和Mac下代碼一樣的,可能有頭文件不太一樣,用man命令進去查看即可。

功能:

  1. 客戶端連到服務器,服務器打印連接的客戶端IP和端口,返回給客戶端當前服務器時間。
  2. 服務器加了一個信號捕獲函數,ctrl+c停止服務器進程。
  3. 如果不寫客戶端,使用telnet 127.0.0.1 8888 依然可以收到服務器返回的時間字符串。

服務器代碼 time_tcp_server.c:

#include <sys/socket.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <time.h>
#include <string.h>
#include <netdb.h>
#include <arpa/inet.h>

#define SERVER_PORT 8888
#define LISTEN_QUEUE_SISE 10

int socketfd;

void signal_handler(int signo)
{
    printf("this serveice close\n");
    close(socketfd);
    exit(1);
}

void out_clientinfo(const struct sockaddr_in* outsockaddr)
{
    char ipstr[16];
    memset(ipstr, 0, sizeof(ipstr));
    // 將地址從網絡字節序轉換為點分十進制
    inet_ntop(AF_INET, &outsockaddr->sin_addr.s_addr, ipstr, sizeof(ipstr));

    printf("Connected by %s(%d)\n", ipstr, ntohs(outsockaddr->sin_port));
}

void dosomething(int fd)
{
    //獲取系統當前時間
    long t = time(0);
    char* times = ctime(&t);
    size_t size = strlen(times)*sizeof(char);
    //將時間寫回到客戶端
    if(write(fd, times, size) != size)
    {
        perror("write to client error");
    }
}


int main(int argc, char const *argv[])
{
    if (signal(SIGINT, signal_handler) == SIG_ERR)
    {
        perror("signal error");
        exit(1);
    }

    // 1 sokect
    // AF_INET ipv4
    // SOCK_STREAM tcp
    if((socketfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        perror("socket error");
        exit(1);
    }

    // 2 bind 綁定本地地址和端口
    struct sockaddr_in serveraddr;
    memset(&serveraddr, 0, sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;//ipv4
    serveraddr.sin_port = htons(SERVER_PORT); //端口
    serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);//響應任意網卡的請求
    if(bind(socketfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr)) < 0)
    {
        perror("bind error");
        exit(1);
    }

    // 3 listen 啟動監聽 通知系統接受來自客戶端的連接 准備好連接隊列
    if(listen(socketfd, LISTEN_QUEUE_SISE) < 0)
    {
        perror("listen error");
        exit(1);
    }
    struct sockaddr_in clientaddr;
    socklen_t clientaddr_len = sizeof(clientaddr);
    while(1)
    {    
        // 4 accept 從隊列拿出第一個
        // clientaddr獲取客戶端的地址信息,是傳出參數
        int clientfd = accept(socketfd, (struct sockaddr*)&clientaddr, &clientaddr_len);
        if (clientfd  < 0)
        {
            perror("accept error");
            continue;
        }
        // 5 read/write
        out_clientinfo(&clientaddr);
        dosomething(clientfd);

        // 6 close
        close(clientfd);
    }
    
    // 6 close
    return 0;
}

客戶端代碼time_tcp_client:

#include <sys/socket.h>
#include <stdlib.h>
#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>

#define SERVER_PORT 8888
#define SERVER_IP 127.0.0.1

int main(int argc, char const *argv[])
{
    //1 創建socket

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

    //2 connect
    struct sockaddr_in serveraddr;
    memset(&serveraddr, 0, sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_port = htons(SERVER_PORT);

    if(connect(socketfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr)) < 0 )
    {
        perror("connect error");
        exit(1);
    }

    //3 read write
    char buf[1024];
  //read是阻塞函數 如果服務器沒有下發消息,會一直阻塞在這里,知道收到消息。
if (read(socketfd, buf, sizeof(buf)) > 0) { printf("%s",buf); } //4 close close(socketfd); return 0; }

 

本例只是簡單的處理,服務器返回客戶端一個時間,然后關閉了socket。

如果要進行雙向通信,服務器勢必要調用read函數,而read默認阻塞,那么如果客戶端不向服務器發送數據,則主線程一直阻塞,其它客戶端無法連接成功。這就需要處理高並發問題。

服務器高並發處理的三種方式

  1. 多進程 https://www.cnblogs.com/lan0725/p/11634709.html
  2. 多線程 https://www.cnblogs.com/lan0725/p/11639142.html
  3.  I/O多路復用


免責聲明!

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



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