Linux 网络编程 tcp server 笔记

一、TCP 服务器的创建

在 Linux 上创建一个简单的 tcp 服务器步骤如下:

①创建套接字

②将套接字绑定到 IP 地址和端口号

③监听来自客户端的连接

④接受连接并创建新的套接字用于与客户端通信

⑤通过新建的套接字发送和接收数据

⑥关闭套接字

流程框图如下:

7e06856095275adf66b6fb22f68a0c3b.png

根据以上介绍可以创建tcp server的示例,分为服务器-单客户端和服务器-多客户端。

二、服务器-单客户端示例

tcp server示例代码如下:

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h> //struct sockaddr_in
#include <arpa/inet.h> //inet_addr()
#include <unistd.h>//close()




#define MY_PRINTF(argv)   do{\
                                printf("file:%s --- function:%s --- line:%d\r\n",__FILE__,__FUNCTION__,__LINE__);\
                                printf("%s\r\n",argv);\
                            }while(0);
#define MYPORT 5000
//tcp server demo
 int main(int argc,char *argv[])
{
     int sockefd;
     int sockenewfd;
     int ret;
     int enable=1;


     struct sockaddr_in my_addr;//本地地址-服务器


     struct sockaddr_in remote_addr;//远端地址-客户端
     int remote_addr_len;


     char buf[1024];


     sockefd=socket(AF_INET,SOCK_STREAM,0);// 套接字
     if(sockefd<0)
     {
         MY_PRINTF("socket err !! ");
         return -1;
     }
    if (setsockopt(sockefd, SOL_SOCKET, SO_REUSEADDR, (char *) &enable, sizeof(enable)) == -1) 
     {
        close(sockefd);
        MY_PRINTF(" setsockopt  err !! ");
        return -1;
    }
     MY_PRINTF("socket ok !! ");


     //本地地址
     my_addr.sin_family=AF_INET;    
     my_addr.sin_port=htons(MYPORT);//0:随机端口
     my_addr.sin_addr.s_addr=INADDR_ANY;//inet_addr("192.168.164.157");//INADDR_ANY:本机 ip  // inet_addr():IP 地址的字符串转换成一个无符号长整型
     bzero(my_addr.sin_zero,sizeof(my_addr.sin_zero));




     ret=bind(sockefd,(struct sockaddr*)&my_addr,sizeof(struct sockaddr));//绑定   //转换数据类型
     if(ret<0)
     {
         close(sockefd);
         MY_PRINTF("bind err !! ");
         return -1;
     }
     MY_PRINTF("bind ok !! ");


     ret=listen(sockefd,5);//监听
     if(ret<0)
     {
         close(sockefd);
         MY_PRINTF("listen err !! ");
         return -1;
     }
     MY_PRINTF("listen ok !! ");


     sockenewfd=accept(sockefd,(struct sockaddr *)&remote_addr,&remote_addr_len);//获取连接的新套接字
     if(sockenewfd<0)
     {
         close(sockefd);
         MY_PRINTF("accept err !! ");
         return -1;
     }
     MY_PRINTF("accept ok !! ");


    printf("%d\r\n",ntohs(remote_addr.sin_port));  //端口号
    printf("%s\r\n",inet_ntoa(remote_addr.sin_addr));//ip地址    //转换为字符串形式以 数字.数字.数字.数字 的格式显 示出来


     for(;;)
     {
            ret=recv(sockenewfd,buf,sizeof(buf),0);//接收
            if(ret<0)
            {
                close(sockenewfd);
                MY_PRINTF("recv err !! ");
                break;
            }
            else if(ret==0)
            {
                close(sockenewfd);
                MY_PRINTF("close !! ");
                perror("close sockenewfd");  
                break;
            }
            MY_PRINTF("recv ok !! ");
            MY_PRINTF(buf);


            ret=send(sockenewfd,buf,ret,0);//发送 等价于write(sockenewfd,buf,ret);
            if(ret<0)
            {
                MY_PRINTF("send err !! ");
            }
            MY_PRINTF("send ok !! ");
            MY_PRINTF(buf);
     }




      close(sockefd);//关闭
      perror("close sockefd");
      return0;


 }

程序运行只允许单个客户端连接通讯。测试如下:

服务端启动,等待连接,如下:

50959d4753910442cafe7bf93bfb25fa.png

客户端连接,如下:

9c044a0cc3c0414d814cab194431c141.png

服务端获取到客户端连接,如下:

499390968ee107bb269ebe68b9b98bce.png

客户端发送数据,并接收到服务端返回,如下:

fe6c0bd29696559425a8169907429e7e.png

服务端显示:

bddbe154b449fa7b1c1eb62af765d2fc.png

三、服务器-多客户端

这里介绍两种方法,方法一:使用多线程;方法二:使用select方法。

①方法一多线程,测试代码如下:

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h> //struct sockaddr_in
#include <arpa/inet.h> //inet_addr()
#include <unistd.h>//close()




#define MY_PRINTF(argv)   do{\
                                printf("file:%s --- function:%s --- line:%d\r\n",__FILE__,__FUNCTION__,__LINE__);\
                                printf("%s\r\n",argv);\
                            }while(0);
#define MYPORT 5000
//tcp server demo




void *myfun_thread(void *socketInfo)
{
    char buf[1024];
     int ret;
    int sockenewfd=(int)socketInfo;


     for(;;)
     {
            ret=recv(sockenewfd,buf,sizeof(buf),0);//接收
            if(ret<0)
            {
                  close(sockenewfd);
                MY_PRINTF("recv err !! ");
                perror("close sockenewfd");  
                break;
            }
            else if(ret==0)
            {
                close(sockenewfd);
                MY_PRINTF("close !! ");
                perror("close sockenewfd");  
                break;
            }
            MY_PRINTF("recv ok !! ");
            MY_PRINTF(buf);


            ret=send(sockenewfd,buf,ret,0);//发送 等价于write(sockenewfd,buf,ret);
            if(ret<0)
            {
                MY_PRINTF("send err !! ");
            }
            MY_PRINTF("send ok !! ");
            MY_PRINTF(buf);
     }
     pthread_exit(NULL);


}


 int main(int argc,char *argv[])
{
     int sockefd;
     int sockenewfd;
     int ret;
     int enable=1;
     pthread_t  threadRx;
     struct sockaddr_in my_addr;//本地地址-服务器


     struct sockaddr_in remote_addr;//远端地址-客户端
     int remote_addr_len;


     


     sockefd=socket(AF_INET,SOCK_STREAM,0);// 套接字
     if(sockefd<0)
     {
         MY_PRINTF("socket err !! ");
         return -1;
     }
     if (setsockopt(sockefd, SOL_SOCKET, SO_REUSEADDR, (char *) &enable, sizeof(enable)) == -1) 
     {
        close(sockefd);
        MY_PRINTF(" setsockopt  err !! ");
        return -1;
    }
     MY_PRINTF("socket ok !! ");


     //本地地址
     my_addr.sin_family=AF_INET;    
     my_addr.sin_port=htons(MYPORT);//0:随机端口
     my_addr.sin_addr.s_addr=INADDR_ANY;//inet_addr("192.168.164.157");//INADDR_ANY:本机 ip  // inet_addr():IP 地址的字符串转换成一个无符号长整型
     bzero(my_addr.sin_zero,sizeof(my_addr.sin_zero));


    
     ret=bind(sockefd,(struct sockaddr*)&my_addr,sizeof(struct sockaddr));//绑定   //转换数据类型
     if(ret<0)
     {
         close(sockefd);
         MY_PRINTF("bind err !! ");
         return -1;
     }
     MY_PRINTF("bind ok !! ");


     ret=listen(sockefd,5);//监听
     if(ret<0)
     {
         close(sockefd);
         MY_PRINTF("listen err !! ");
         return -1;
     }
     MY_PRINTF("listen ok !! ");


     while(1)
     {
                sockenewfd=accept(sockefd,(struct sockaddr *)&remote_addr,&remote_addr_len);//获取连接的新套接字
                if(sockenewfd<0)
                {
                    MY_PRINTF("accept err !! ");
                    break;
                }
                MY_PRINTF("accept ok !! ");


                printf("%d\r\n",ntohs(remote_addr.sin_port));  //端口号
                printf("%s\r\n",inet_ntoa(remote_addr.sin_addr));//ip地址    //转换为字符串形式以 数字.数字.数字.数字 的格式显 示出来
                pthread_create(&threadRx, NULL, myfun_thread, (void *)sockenewfd);
                sleep(1);
     }
     close(sockefd);//关闭
    perror("close sockefd");  
     return 0;


 }

测试结果:

服务端:

2eedde70a977fc5d02db9ac76f735503.png

客户端:

5cf7c32b0cfbd080d17623fc87289453.png

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值