Linux网络编程--tcp服务器

一、做为 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只能绑定自身的地址及端口

使用实例:

[csharp]  view plain  copy
  1. // 设置本地地址结构体  
  2.     struct sockaddr_in my_addr;  
  3.     bzero(&my_addr, sizeof(my_addr));           // 清空,保证最后8字节为0      
  4.     my_addr.sin_family = AF_INET;               // ipv4  
  5.     my_addr.sin_port   = htons(port);           // 端口  
  6.     my_addr.sin_addr.s_addr = htonl(INADDR_ANY);// ip,INADDR_ANY为通配地址其值为0  
  7.       
  8.     // 绑定  
  9.     int err_log = bind(sockfd, (struct sockaddr*)&my_addr, sizeof(my_addr));  
  10.     if( err_log != 0)  
  11.     {  
  12.         perror("binding");  
  13.         close(sockfd);        
  14.         exit(-1);  
  15.     }  

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代码:

[csharp]  view plain  copy
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <string.h>                         
  4. #include <unistd.h>  
  5. #include <sys/socket.h>  
  6. #include <netinet/in.h>  
  7. #include <arpa/inet.h>                  
  8. int main(int argc, char *argv[])  
  9. {  
  10.     unsigned short port = 8000; // 本地端口   
  11.     if(argc > 1)                       
  12.     {  
  13.         port = atoi(argv[1]);  
  14.     }  
  15.     //1.创建通信端点:套接字  
  16.     int sockfd = socket(AF_INET, SOCK_STREAM, 0);     
  17.     if(sockfd < 0)  
  18.     {  
  19.         perror("socket");  
  20.         exit(-1);  
  21.     }  
  22.       
  23.     //设置本地地址结构体  
  24.     struct sockaddr_in my_addr;  
  25.     bzero(&my_addr, sizeof(my_addr));           // 清空,保证最后8字节为0      
  26.     my_addr.sin_family = AF_INET;               // ipv4  
  27.     my_addr.sin_port   = htons(port);           // 端口  
  28.     my_addr.sin_addr.s_addr = htonl(INADDR_ANY);// ip,INADDR_ANY为通配地址其值为0  
  29.       
  30.     //2.绑定:将本地ip、端口与套接字socket相关联起来  
  31.     int err_log = bind(sockfd, (struct sockaddr*)&my_addr, sizeof(my_addr));  
  32.     if( err_log != 0)  
  33.     {  
  34.         perror("binding");  
  35.         close(sockfd);        
  36.         exit(-1);  
  37.     }  
  38.       
  39.     //3.监听,监听套接字改为被动,创建连接队列  
  40.     err_log = listen(sockfd, 10);   
  41.     if(err_log != 0)  
  42.     {  
  43.         perror("listen");  
  44.         close(sockfd);        
  45.         exit(-1);  
  46.     }     
  47.       
  48.     printf("listen client @port=%d...\n",port);  
  49.   
  50.     while(1)  
  51.     {     
  52.       
  53.         struct sockaddr_in client_addr;          
  54.         char cli_ip[INET_ADDRSTRLEN] = "";       
  55.         socklen_t cliaddr_len = sizeof(client_addr);      
  56.           
  57.         int connfd = 0;  
  58.           
  59.         //4.从完成连接队列中提取客户端连接  
  60.         connfd = accept(sockfd, (struct sockaddr*)&client_addr, &cliaddr_len);         
  61.         if(connfd < 0)  
  62.         {  
  63.             perror("accept");  
  64.             continue;  
  65.         }  
  66.   
  67.         inet_ntop(AF_INET, &client_addr.sin_addr, cli_ip, INET_ADDRSTRLEN);  
  68.         printf("----------------------------------------------\n");  
  69.         printf("client ip=%s,port=%d\n", cli_ip,ntohs(client_addr.sin_port));  
  70.           
  71.         char recv_buf[512] = "";  
  72.         while( recv(connfd, recv_buf, sizeof(recv_buf), 0) > 0 ) // 接收数据  
  73.         {  
  74.             printf("\nrecv data:\n");  
  75.             printf("%s\n",recv_buf);  
  76.         }  
  77.           
  78.         close(connfd);     //关闭已连接套接字  
  79.         printf("client closed!\n");  
  80.     }  
  81.       
  82.     close(sockfd);         //关闭监听套接字  
  83.       
  84.     return 0;  
  85. }  


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



关闭连接:close()

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


做服务器时

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


做客户端时

关闭连接就是关闭连接,不意味着其他。


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




源码下载:


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值