Linux網絡編程——服務器與客戶端

1 單連接C/S

簡單實現單連接的服務器與客戶端,服務器處理客戶端的字符爲大寫返回給客戶端

  • 服務器端
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <ctype.h>
#include <arpa/inet.h>
#define SERV_PORT 6666
#define SERV_IP "127.0.0.1"
int main()
{
    int lfd,cfd;
    char buf[BUFSIZ],clie_IP[BUFSIZ];
    int n;
    struct sockaddr_in serv_addr, clie_addr;
    socklen_t clie_addr_len;
    /*創建一個socket 指定IPv4協議族 TCP協議*/
    lfd = socket(AF_INET, SOCK_STREAM, 0);

    /*初始化一個地址結構 man 7 ip 查看對應信息*/
    bzero(&serv_addr, sizeof(serv_addr));           //將整個結構體清零
    serv_addr.sin_family = AF_INET;                 //選擇協議族爲IPv4
    serv_addr.sin_port = htons(SERV_PORT);          //綁定端口號    
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);//INADDR_ANY這個宏可以自動轉換爲本地有效的IP
    /*綁定服務器地址結構*/
    bind(lfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr));

    listen(lfd, 128);   //同一時刻允許向服務器發起鏈接請求的數量
    printf("wait for client connect ...\n");

    clie_addr_len = sizeof(clie_addr);/*獲取客戶端地址結構大小*/

    /*參數1是sfd; 參2傳出參數, 參3傳入傳出參數, 全部是client端的參數*/
    cfd = accept(lfd, (struct sockaddr *)&clie_addr, &clie_addr_len);
    printf("client IP:%s\tport:%d\n", 
            inet_ntop(AF_INET, &clie_addr.sin_addr.s_addr, clie_IP, sizeof(clie_IP)), 
            ntohs(clie_addr.sin_port));
    while(1){
        /*讀取客戶端發送數據*/
        n = read(cfd, buf, sizeof(buf));
        //小寫轉大寫發送給客戶端
        for(i=0;i<n;i++)
            buf[i] = toupper(buf[i]);
        write(cfd, buf,n);
    }
    close(lfd);
    close(cfd);
    return 0;
}
  • 客戶端
client.c
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>

#define SERV_IP "127.0.0.1"
#define SERV_PORT 6666
int main()
{
    int sfd,serv_addr_len,cfd;
    char buf[BUFSIZ];
    int n;

    sfd = socket(AF_INET, SOCK_STREAM, 0);

    struct sockaddr_in serv_addr;//服務器IP+port
    bzero(&serv_addr, sizeof(serv_addr));//清零
    serv_addr.sin_family = AF_INET;
    inet_pton(AF_INET, SERV_IP, &serv_addr.sin_addr.s_addr);//IP字符串轉換爲網絡字節序 參3:傳出參數
    serv_addr.sin_port = htons(SERV_PORT);//端口號轉網絡字節序

    connect(sfd,(struct sockaddr*)&serv_addr, sizeof(serv_addr));
    while(1){
        fgets(buf, sizeof(buf), stdin);/*從標準輸入獲取數據*/
        write(sfd, buf, strlen(buf));/*將數據寫給服務器*/
        len = read(sfd, buf, sizeof(buf));/*從服務器讀迴轉換後數據*/
        write(STDOUT_FILENO, buf,len); /*寫至標準輸出*/
    }
    close(sfd);
    return 0;
}

2 多進程併發C/S

  • 服務器端(多進程併發)
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <strings.h>
#include <ctype.h>
#define SERV_PORT 8888
void wait_child(int signo){
    while(waitpid(0,NULL,WNOHANG)>0);//子進程回收
    return ;
}
int main()
{
    pid_t pid;
    int lfd,cfd;
    struct sockaddr_in serv_addr,clie_addr;
    socklen_t clie_addr_len;
    char buf[BUFSIZ],clie_IP[BUFSIZ];
    int n,i;

    lfd = socket(AF_INET, SOCK_STREAM,0);
    bzero(&serv_addr, sizeof(serv_addr));//	清零
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(SERV_PORT);
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    //inet_pton(AF_INET,"192.168.43.1",serv_addr.sin_addr.s_addr);
    bind(lfd,(struct sockaddr *)&serv_addr, sizeof(serv_addr));
    listen(lfd,128);

    while(1){//接活,處理新到來的連接
        clie_addr_len = sizeof(clie_addr);
        cfd = accept(lfd, (struct sockaddr *)&clie_addr, &clie_addr_len);
        printf("client IP:%s, port:%d\n",
            inet_pton(AF_INET,&clie_addr.sin_addr.s_addr, clie_IP, sizrof(clie_IP)),
            ntohs(clie_addr.sin_port) )
        pid = fork();
        if(pid<0){
            perror("fork error");
            exit();
        }else if(pid==0){   //子進程
            close(lfd); 
            break;
        }else{
            close(cfd);
            signal(SIGCHLD,wait_child);//處理殭屍進程,子進程
        }
    }
    if(pid==0){ //子進程處理連接的數據
        while(1){
            n = read(cfd,buf,sizeof(buf));
            if(n==0){   //client closed
                close(cfd);
                return 0;
            }else if(n==-1){
                perror("read error");
                exit(1);
            }else{
                for(i=0;i<n;i++)
                    buf[i] = toupper(buf[i]);
                write(cfd,buf,n);
                write(STDOUT_FILENO,buf,n);
            }
        }
    }
    return 0;
}

3 多線程併發

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <strings.h>
#include <ctype.h>

#define MAXLINE 8192
#define SERV_PORT 8000
struct s_info{
    struct sockaddr_in cliaddr;
    int connfd;
}
void *do_work(void *arg){
    int n,i;
    struct s_info *ts = (struct s_info*)arg;
    char buf[MAXLINE];
    char str[INET_ADDRSTRLEN];  //define INET_ADDRSTRLEN 16
    while(1){
        n = read(ts->connfd,buf,MAXLINE);
        if(n==0){
            printf("the client %d closed...\n",ts->connfd);
            break;
        }
        printf("reveived from %s at PORT %d\n",
            inet_ntop(AF_INET, &(*ts).cliaddr.sin_addr, str, sizeof(str)),
            ntohs((*ts).cliaddr.sin_port));
        for(i=0;i<n;i++)
            buf[i] = toupper(buf[i]);
        write(STDOUT_FILENO,buf,n);
        write(ts->connfd,buf,n);
    }
    close(ts->connfd);
    return (void*)0;
}
int main()
{
    struct sockaddr_in serv_addr, clie_addr;
    socklen_t clie_addr_len;
    int listenfd,connfd;
    pthread_t tid;
    struct s_info ts[256];
    int i=0;
    
    listenfd = socket(AF_INET,SOCK_STREAM, 0);
    bzero(&serv_addr, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    serv_addr.sin_port = htons(SERV_PORT);
    bind(listenfd,(struct sockaddr *)&serv_addr, sizeof(serv_addr));
    listen(listenfd,128);
    printf("Accepting client connet ...\n");
    while(1){
        clie_addr_len = sizeof(clie_addr);
        connfd = accept(listenfd,(struct sockaddr *)&clie_addr, &clie_addr_len);//阻塞監聽客戶端請求
        ts[i].cliaddr = clie_addr;
        ts[i].connfd = connfd;
        /*達到線程最大數時,pthread_create出錯處理,增加服務器穩定性 */
        pthread_create(&tid,NULL,do_work,(void*)&ts[i]);
        pthread_detach(tid);    //子線程分離,防止殭屍線程產生
        i++;//統計是第幾個線程
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章