1. 程式人生 > >使用套接字實現簡單TCP伺服器客戶端模型

使用套接字實現簡單TCP伺服器客戶端模型

利用套接字實現一個簡單的TCP伺服器客戶端模型基本步驟如下: 1.建立套接字 #include <sys/types.h> #include <sys/socket.h>  int socket(int domain, int type, int protocol); 引數描述:      domian:協議域,AF_INET 對應 ipv4,AF_INET6  對應  ipv6, AF_UNIX 表示這個socket是非網路形式的unix域,可以用來進行非網路形式的程序間通訊。      type:指定socket型別      1.SOCK_STREAM 這個協議是按照順序的、可靠的、資料完整的基於位元組流的連線。  這是一個使用最多的socket型別,這個socket是使用TCP來進行傳輸。      2.SOCK_DGRAM 這個協議是無連線的、固定長度的傳輸呼叫。該協議是不可靠的,使用UDP來進行它的連線。      3.SOCK_RAW 這個socket型別提供單一的網路訪問,這個socket型別使用ICMP公共協議。(ping、traceroute使用該協議) 返回值:
     成功返回socket描述符,失敗返回-1 2.將伺服器IP與埠和套接字進行繫結 #include <sys/types.h> #include <sys/socket.h> int bind(int sockfd, const struct sockaddr *addr,  socklen_t addrlen); 引數描述:      sockfd:通過socket()函式建立,用來表示唯一一個socket      addr:指向要繫結給sockfd的協議地址(協議型別,IP,埠號)      addrlen:對應地址長度 返回值:      成功返回0,失敗返回-1 注意:
在IPv4因特網域AF_INET中,套接字地址用結構sockaddr_in表示,如下: struct sockaddr_in {      sa_family_t sin_family;     //unsigned short 地址族      in_port_t sin_sport;         //uint16_t      struct in_addr sin_addr;  //IPv4 }; struct in_addr {      in_addr_t s_addr;            //uint32_t }; 3.監聽請求 #include <sys/types.h> #include <sys/socket.h> int listen(int sockfd, int backlog); 引數描述:
     sockfd:要監聽的套接字      backlog:連線佇列長度      backlog 具體一些是什麼意思呢?每一個連入請求都要進入一個連入請求佇列,等待listen 的程式呼叫accept()函式來接受這個連線。當系統還沒有呼叫accept()函式的時候,如果有很多連線,那麼本地能夠等待的最大數目就是backlog 的數值。 返回值:      成功返回0,失敗返回-1 4.伺服器端接受監聽到的請求 當伺服器端進行監聽到一個連線請求時,就可以使用accept()函式來接受這個請求。 #include <sys/types.h> #include <sys/socket.h> int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); 引數描述:      socket:伺服器的套接字      addr:用於儲存客戶端的協議地址(協議型別,IP,埠號)      addrlen:地址長度(sizeof(addr)) 返回值:      成功返回一個新的套接字描述符,代表與返回客戶端的TCP連線 5.客戶端連線伺服器 #include <sys/types.h> #include <sys/socket.h> int connect(int sockfd, const struct sockaddr *addr,  socklen_t addrlen); 引數描述:      sockfd:客戶端的sockfd      addr:用於儲存伺服器的協議地址(協議型別,IP,埠號)      addrlen:地址長度(sizeof(addr)) 返回值:      成功返回0,失敗返回-1 注意: 在實現過程中還應注意網路位元組序的問題,主機的位元組序是不確定的,有的使用大端儲存,有的使用小端儲存,為了不使網路通訊因為機器之間儲存模式而造成錯誤,TCP/IP協議規定網路資料流使用大端位元組序。 下面庫函式可以完成主機序列和為網路序列的轉換: #include <arpa/inet.h> //主機序列到網路序列 uint32_t htonl(uint32_t hostlong); uint16_t htons(uint16_t hostshort); //網路序列到主機序列 uint32_t ntohl(uint32_t netlong); uint16_t ntohs(uint16_t netshort);
server:
#include<stdio.h>
#include<string.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<error.h>
#include<unistd.h>
#include<sys/types.h>
static void Usage(char* proc)
{
    printf("Usage: %s [local_ip] [local_port]\n", proc);
}

int startup(char* ip, int port)
{
    //1.建立檔案特性套接字
    //AF_INET ipv4,SOCK_STREAM,基於位元組流服務
    int sock = socket(AF_INET, SOCK_STREAM, 0);
    if(sock < 0)
    {
    perror("socket");
    return 2;
    }
    printf("sock =  %d\n", sock);

    struct sockaddr_in local;
    //確定地址協議型別
    local.sin_family = AF_INET;
    //繫結埠
    local.sin_port = htons(port);
    //繫結ip
    //這裡的IP是點分十進位制,使用inet_addr()可以將其轉換成二進位制
    local.sin_addr.s_addr = inet_addr(ip);
    //2.繫結網路特性將套接字與伺服器ip和埠號繫結
    if(bind(sock, (struct sockaddr*)&local, sizeof(local)) < 0)
    {
    perror("bind");
    return 3;
    }
    //3.監聽請求
    if(listen(sock, 10) < 0)
    {
    perror("listen");
    return 4;
    }
    return sock;
}

int main(int argc, char* argv[])
{
    if(argc != 3)
    {
    Usage(argv[0]);
    return 1;
    }
    //獲取套接字
    int listen_sock = startup(argv[1], atoi(argv[2]));
    while(1)
    {
    //用於儲存連線的客戶端地址資訊(地址型別,IP,埠號)
    struct sockaddr_in client;
    socklen_t len = sizeof(client);
    //4.接受監聽到的連線
    int new_sock = accept(listen_sock,(struct sockaddr*)&client, &len);
    if(new_sock < 0)
    {
        perror("accept");
        return 5;
    }
    while(1)
    {
        char buf[1024];
        //從套接字讀取資訊到buf中
        ssize_t s = read(new_sock, buf, sizeof(buf) - 1);
        if(s > 0)
        {
        buf[s] = 0;
        printf("client#  %s\n", buf);
        printf("Please Enter# ");
        fflush(stdout);
        //從鍵盤輸入資訊到buf中
        ssize_t _s = read(0, buf, sizeof(buf)-1);
        buf[_s-1] = 0;
        //傳送資訊到套接字
            write(new_sock, buf, strlen(buf));   
        }
        else if(s == 0)
        {
        printf("client colse !!\n");
        return 6;
        }
        else
        {
        perror("read");
        return 7;
        }
    }

    }
    return 0;
}
client:
#include<stdio.h>
#include<string.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<unistd.h>
#include<sys/types.h>

static void Usage(char *proc)
{
    printf("Usage %s [server_ip] [server_port]\n", proc);
}
int main(int argc, char* argv[])
{
    if(argc != 3)
    {
    Usage(argv[0]);
    return 1;
    }
    //建立套接字
    int sock = socket(AF_INET, SOCK_STREAM,0);
    if(sock < 0)
    {
    perror("socket");
    return 2;
    }
    struct sockaddr_in server;
    server.sin_family = AF_INET;
    server.sin_port = htons(atoi(argv[2]));
    server.sin_addr.s_addr = inet_addr(argv[1]);
    //將建立的套接字,連入指定的網路服務中,這裡連到了伺服器
    if(connect(sock,(struct sockaddr*)&server, sizeof(server)) < 0)
    {
    perror("connect111");
    return 3;
    }
    char buf[1024];
    while(1)
    {
    printf("Please Enter# ");
    fflush(stdout);
    //從鍵盤寫入內容到緩衝區
    ssize_t s = read(0, buf, sizeof(buf)-1);
    if(s >0)
    {
        buf[s-1] = 0;
        printf("server# ");
        fflush(stdout);
        //將緩衝區內容通過套接字傳送到伺服器
        write(sock, buf, strlen(buf));
        //在從套接字中讀取伺服器的迴應資訊
        ssize_t _s = read(sock, buf, sizeof(buf)-1);
        if(_s > 0)
        {
        buf[_s] = 0;
        printf("%s\n", buf);
        }
        if(s < 0)
        {
        perror("read");
        return 4;
        }
    }
    }
    return 0;
}