服務器增強版(TCP)

1) 服務器端存在的問題:
*recv會阻塞
解決方法:1、多線程技術
2、 超時返回

2)如何用accept獲取客戶端IP和端口號
3)TCP通信應用場景:註冊統計信息、智能聊天、雲計算
4)補充知識
計算機數據存儲有兩種字節優先順序:高位字節優先和低位字節優先。Internet上數據以高位字節優先順序在網絡上傳輸,所以對於在內部是以低位字節優先方式存儲數據的機器,在Internet上傳輸數據時就需要進行轉換,否則就會出現數據不一致。
  下面是幾個字節順序轉換函數:
htonl():把32位值從主機字節序轉換成網絡字節序
htons():把16位值從主機字節序轉換成網絡字節序
ntohl():把32位值從網絡字節序轉換成主機字節序
ntohs():把16位值從網絡字節序轉換成主機字節序

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

void* rcv_data(void* arg);
void save_files(const char* str);
pthread_mutex_t m;
int main()
{ 
    pthread_mutex_init(&m,NULL);
//創建套接字     
    int socket_listen=socket(AF_INET,SOCK_STREAM,0);

    int opt_val=1;
    setsockopt(socket_listen,SOL_SOCKET,SO_REUSEADDR,&opt_val,sizeof(opt_val));
                                                     //設置文件 
//綁定地址
    struct sockaddr_in myaddr;
    myaddr.sin_family = AF_INET;
    myaddr.sin_addr.s_addr=INADDR_ANY;
    myaddr.sin_port=htons(6666);

    if(-1==bind(socket_listen,(struct sockaddr*)&myaddr,sizeof(myaddr)))
    {                          
        perror("bind");
        exit(-1);
    }
//將套接字設置爲監聽狀態
    listen(socket_listen,5);

    struct timeval rcv_timeout;//設置超時爲100ms
    rcv_timeout.tv_sec=0;
    rcv_timeout.tv_usec=100000;

    int sock_conn;
    struct sockaddr_in client_addr;
    socklen_t len;

    while(1)//一直監聽
    {
//接受客戶端連接請求
        len=sizeof(client_addr);

        sock_conn=accept(socket_listen,(struct sockaddr*)&client_addr,&len);
        setsockopt(sock_conn,SOL_SOCKET,SO_RCVTIMEO,&rcv_timeout,sizeof(rcv_timeout));//設置超時時間
        printf("%s:%d已近連接!\n",inet_ntoa(client_addr.sin_addr),ntohs(client_addr.sin_port));
        if(sock_conn==-1)
        {
            perror("accept");
            continue;
        }
        pthread_t tid;
        if(0!=pthread_create(&tid,NULL,rcv_data,(void*)sock_conn))
        {   
            perror("pthread_create");
            close(sock_conn);
        }
    }

//7、關閉監聽套接字
    close(socket_listen);
    pthread_mutex_destroy(&m);
    return 0;

}
void* rcv_data(void* arg)
{
    pthread_detach(pthread_self());
    int sock_conn=(int)arg;
    //收發數據

    char msg[100];
    int ret;

    ret=recv(sock_conn,msg,sizeof(msg),0);//0表默認,不加額外控制
                                        //阻塞到收到消息,或連接斷開 
    if(ret>0)
    {
        msg[ret]='\0';
        printf("客戶端說:%s\n",msg);
        save_files(msg);
    }
    else if(ret==0)
    {
        printf("接收失敗,連接斷開\n");
    }
    else
    {
        if(errno==EAGAIN || errno==EWOULDBLOCK)
        {
            printf("接收超時!\n");
        }
        else
        {
            printf("其他錯誤\n");
        }
    }

    strcpy(msg,"收到您的信息");          
    ret=send(sock_conn,msg,strlen(msg),0);
//6、斷開連接,即關閉連接套接字
    close(sock_conn);
    return NULL;
}
void save_files(const char* str)
{
    pthread_mutex_lock(&m);//保證下面三句話不被打斷
    FILE* fp=fopen("name.txt","a");
    fprintf(fp,"%s\n",str);
    fclose(fp);
    pthread_mutex_unlock(&m);
}
//解決阻塞問題
//1.主線程接電話,然後分配給其他線程receive
//2.超時返回

//端口複用
//顯示對方IP
//當客戶端send,而服務器端關閉了連接套接字時,服務器進程會收到13號信號,導致進程結束,爲了避免這種情況,要用信號處理函數對信號處理。

//應用:註冊統計信息
//羣聊天室
//雲計算
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章