Linux网络编程--tcp服务器

转载 http://blog.csdn.net/lianghe_work/article/details/46468001

一、做为 TCP 服务器需要具备的条件呢?
1.具备一个可以确知的地址( bind() ):相当于我们要明确知道移动客服的号码,才能给他们电话;

2.让操作系统知道是一个服务器,而不是客户端( listen() ):相当于移动的客服,他们主要的职责是被动接听用户电话,而不是主动打电话骚扰用户;
3.等待连接的到来( accept() ):移动客服时刻等待着,来一个客户接听一个。

接收端使用 bind() 函数,来完成地址结构与socket 套接字的绑定,这样 ip、port 就固定了,发送端即可发送数据给有明确地址( ip+port ) 的接收端。
对于 TCP 服务器编程流程,有点类似于接电话过程

1.找个可以通话的手机(socket() )
2.插上电话卡固定一个号码( bind() )
3.职责为被动接听,给手机设置一个铃声来监听是否有来电( listen()) 
4. 有来电,确定双方的关系后,才真正接通不挂电话( accept() ) 
5. 接听对方的诉说( recv() ) 
6.适当给些回话( send() )
7.通信结束后,双方说再见挂电话( close())。

int bind( int sockfd, const struct sockaddr *myaddr,socklen_t addrlen );功能:
将本地协议地址与 sockfd 绑定,这样 ip、port 就固定了

参数:
sockfd:socket 套接字
myaddr: 指向特定协议的地址结构指针
addrlen:该地址结构的长度

返回值:
成功:返回 0

失败:-1

注意:bind只能绑定自身的地址及端口
使用实例:

// 设置本地地址结构体  
    struct sockaddr_in my_addr;  
    bzero(&my_addr, sizeof(my_addr));           // 清空,保证最后8字节为0      
    my_addr.sin_family = AF_INET;               // ipv4  
    my_addr.sin_port   = htons(port);           // 端口  
    my_addr.sin_addr.s_addr = htonl(INADDR_ANY);// ip,INADDR_ANY为通配地址其值为0  
      
    // 绑定  
    int err_log = bind(sockfd, (struct sockaddr*)&my_addr, sizeof(my_addr));  
    if( err_log != 0)  
    {  
        perror("binding");  
        close(sockfd);        
        exit(-1);  
    }  

int listen(int sockfd, int backlog);功能:

将套接字由主动修改为被动,使操作系统为该套接字设置一个连接队列,用来记录所有连接到该套接字的连接。更详细说明,请看《connect()、listen()和accept()三者的关系》。

参数:

sockfd: socket监听套接字

backlog:连接队列的长度

返回值:

成功:返回0

失败:其他

int accept( int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen );功能:

从已连接队列中取出一个已经建立的连接,如果没有任何连接可用,则进入睡眠等待(阻塞)。更详细说明,请看《connect()、listen()和accept()三者的关系》。

参数:

sockfd: socket监听套接字

cliaddr: 用于存放客户端套接字地址结构

addrlen:套接字地址结构体长度的地址

返回值:

成功:已连接套接字。注意:返回的是一个已连接套接字,这个套接字代表当前这个连接

失败:< 0

tcp_server代码:

#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>                         
#include <unistd.h>  
#include <sys/socket.h>  
#include <netinet/in.h>  
#include <arpa/inet.h>                  
int main(int argc, char *argv[])  
{  
    unsigned short port = 8000; // 本地端口   
    if(argc > 1)                       
    {  
        port = atoi(argv[1]);  
    }  
    //1.创建通信端点:套接字  
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);     
    if(sockfd < 0)  
    {  
        perror("socket");  
        exit(-1);  
    }  
      
    //设置本地地址结构体  
    struct sockaddr_in my_addr;  
    bzero(&my_addr, sizeof(my_addr));           // 清空,保证最后8字节为0      
    my_addr.sin_family = AF_INET;               // ipv4  
    my_addr.sin_port   = htons(port);           // 端口  
    my_addr.sin_addr.s_addr = htonl(INADDR_ANY);// ip,INADDR_ANY为通配地址其值为0  
      
    //2.绑定:将本地ip、端口与套接字socket相关联起来  
    int err_log = bind(sockfd, (struct sockaddr*)&my_addr, sizeof(my_addr));  
    if( err_log != 0)  
    {  
        perror("binding");  
        close(sockfd);        
        exit(-1);  
    }  
      
    //3.监听,监听套接字改为被动,创建连接队列  
    err_log = listen(sockfd, 10);   
    if(err_log != 0)  
    {  
        perror("listen");  
        close(sockfd);        
        exit(-1);  
    }     
      
    printf("listen client @port=%d...\n",port);  
  
    while(1)  
    {     
      
        struct sockaddr_in client_addr;          
        char cli_ip[INET_ADDRSTRLEN] = "";       
        socklen_t cliaddr_len = sizeof(client_addr);      
          
        int connfd = 0;  
          
        //4.从完成连接队列中提取客户端连接  
        connfd = accept(sockfd, (struct sockaddr*)&client_addr, &cliaddr_len);         
        if(connfd < 0)  
        {  
            perror("accept");  
            continue;  
        }  
  
        inet_ntop(AF_INET, &client_addr.sin_addr, cli_ip, INET_ADDRSTRLEN);  
        printf("----------------------------------------------\n");  
        printf("client ip=%s,port=%d\n", cli_ip,ntohs(client_addr.sin_port));  
          
        char recv_buf[512] = "";  
        while( recv(connfd, recv_buf, sizeof(recv_buf), 0) > 0 ) // 接收数据  
        {  
            printf("\nrecv data:\n");  
            printf("%s\n",recv_buf);  
        }  
          
        close(connfd);     //关闭已连接套接字  
        printf("client closed!\n");  
    }  
      
    close(sockfd);         //关闭监听套接字  
      
    return 0;  
}  

用windows的网络调试助手作为客户端,上面代码为服务器运行结果:

关闭连接:close()

使用 close() 函数即可关闭套接字,关闭一个代表已连接套接字将导致另一端接收到一个 0 长度的数据包,详情请看《 TCP 四次挥手》
做服务器时

关闭监听套接字( socket()和listen()之后的套接字 )将导致服务器无法接收新的连接,但不会影响已经建立的连接;
关闭 accept()返回的已连接套接字将导致它所代表的连接被关闭,但不会影响服务器的监听( socket()和listen()之后的套接字 )。

做客户端时
关闭连接就是关闭连接,不意味着其他。

如果客户端和服务器已经连接成功的前提下,通常的情况下,先关闭客户端,再关闭服务器,如果是先关闭服务器,立马启动服务器是,服务器绑定的端口不会立马释放(如下图),要过 1 分钟左右才会释放,为什么会这样的呢?请看《 TCP 四次挥手》。有没有方法让服务器每次启动都能立即成功?请看《端口复用》

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容