Linux网络编程基础及多线程并发案例

Posted 陈子青 :See

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux网络编程基础及多线程并发案例相关的知识,希望对你有一定的参考价值。

目录

1.ip:端口  TCP/IP协议    

2.socket 头文件  sys/socket.h       

3.字节序

4.ip地址转换函数

5.sockaddr和sockaddr_in

6.服务器端基本函数 bind listen accept

7.客户端基本函数  connect

8.send 和 recv 

8.多线程

9.通信流程

10.多线程并发样例程序 


1.ip:端口  TCP/IP协议    

 AF_INET             IPv4
 AF_INET6            IPv6

SOCK_STREAM  tcp协议

2.socket 头文件  sys/socket.h 
      

套接字对应程序猿来说就是一套网络通信的接口,使用这套接口就可以完成网络通信。
网络通信的主体主要分为两部分:客户端和服务器端

int socket(int domain, int type, int protocol);

(1)在参数表中,domain指定使用何种的地址类型,比较常用的有:

PF_INET, AF_INET: Ipv4网络协议

PF_INET6, AF_INET6: Ipv6网络协议。

(2)type参数的作用是设置通信的协议类型,可能的取值如下所示:

SOCK_STREAM: 提供面向连接的稳定数据传输,即TCP协议

(3)一般取0即可

    3.字节序


    ittle-Endian -> 主机字节序 (小端)
    Big-Endian -> 网络字节序 (大端)
    
    头文件:#include <arpa/inet.h> //包含 sys/socket.h
    uint16_htons(uint16_t hostshort) //短整形 主机字节序 --> 网络字节序
    uint32_htonl(uint32_t hostlong) //长整形  主机字节序 -->网络字节序

    uint16_ntohs(uint16_t netshort) //短整形  网络字节序 -->主机字节序
    uint32_ntohl(uint32_t netlong) //长整形  网络字节序 -->主机字节序

   

4.ip地址转换函数


    int inet_pton(int af,const char *src,void *dst) //主机ip地址 -->网络ip字节序
    注: 主机ip地址是字符串 、网络ip字节序是整形
    (1) af : IP协议 AF_INET、AF_INET6
    (2) src: 传入参数 十进制的ip地址
    (3) dst:大整形ip 存入地址

    //网络ip字节序 --> 主机ip地址
    const char* inet_ntop(int af,const void *src,char *dst,socklen_t size)
    返回值:成功返回指向 dst的指针 失败返回空指针

与此相关只能转换AF_INET ip字节序的函数

inet_addr IP地址的字符串赋值转换为in_addr类型

inet_ntoa,可以把一个in_addr类型转换为一个字符串。

5.sockaddr和sockaddr_in

sockaddr

头文件#include <sys/socket.h>中定义

sockaddr的缺点:sa_data把目标地址和端口存在一起

struct sockaddr {  
     sa_family_t sin_family;//地址族
    char sa_data[14]; //前2字节:端口 后4字节:ip地址 后8字节作为填充        
 }; 

sockaddr_in

struct sockaddr_in{

short sin_family;//(地址族)

unsigned short sin_port;/*端口号*/

struct in_addr sin_addr;/*IP 地址*/

unsigned char sin_zero[8];/*Same size as struct sockaddr没有实际意义,只是为了 跟SOCKADDR结构在内存中对齐*/

};

我的理解:网络通信api的参数默认为sockaddr,但由于不好指定端口和ip地址,一般定义sockaddr_in 来方便赋值端口和ip地址,调用api函数时强转为sockaddr类型即可

6. 服务器端基本函数 bind listen accept

int bind( int sockfd , const struct sockaddr * my_addr, socklen_t addrlen);

int listen(int fd, int backlog)

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);  

7. 客户端基本函数  connect

int connect(int sock, struct sockaddr *serv_addr, socklen_t addrlen)

8.send 和 recv 

int send( SOCKET s, const char *buf, int len, int flags );

int recv( SOCKET s, const char *buf, int len, int flags); 

8.多线程

头文件 pthread.h

创建线程 pthread_create (与windows CreateThread 用法相似)

int pthread_create(pthread_t *tidp,const pthread_attr_t *attr,void *(*start_rtn)(void*),void *arg);

 pthread_detach()与pthread_join 

 pthread_detach()即主线程与子线程分离,子线程结束后,资源自动回收。

 pthread_join()即是子线程合入主线程,主线程阻塞等待子线程结束,然后回收子线程资源。

int pthread_join(pthread_t tid);  若成功则返回0,若出错则为非零。 

int pthread_join(pthread_t tid);  若成功则返回0,若出错则为非零。

    9.通信流程

  

10.多线程并发样例程序 

服务器端

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<arpa/inet.h>
#include<pthread.h>

void * working(void *arg);

struct SockInfo
{
   struct sockaddr_in addr;
   int fd;
};

struct SockInfo infos[512];

int main()
{
        //1.创建监听的套接字
        int fd = socket(AF_INET,SOCK_STREAM,0);
        if(fd==-1)
        {
                perror("socket");
                return -1;
        }
        //2.连接本地的ip 端口
        struct sockaddr_in saddr;
        saddr.sin_family =AF_INET;
        saddr.sin_port = htons(9999);
        saddr.sin_addr.s_addr = INADDR_ANY;
        int ret = bind(fd,(struct sockaddr*) &saddr,sizeof(saddr));
        if(ret==-1)
        {
                perror("bind");
                return -1;
        }

        //3.设置监听
        ret = listen(fd,128);
        if(ret==-1)
        {
                perror("listen");
                return -1;
        }

        //初始化客户端数组
        int max = sizeof(infos)/sizeof(infos[0]);
        for(int i=0; i<max;i++)
        {
                bzero(&infos[i],sizeof(infos[i]));
                infos[i].fd=-1;
        }

        //4.阻塞并等待客户端的连接
        while(1)
        {
             struct SockInfo * pinfo;
             int addrlen =sizeof(sockaddr_in);
             for(int i=0; i<max;i++)
             {
                     if(infos[i].fd==-1)
                     {
                              pinfo=&infos[i];
                              break;
                     }
             }
             int cfd= accept(fd,(struct sockaddr*) &pinfo->addr,&addrlen);
             if(cfd==-1)
             {
                     perror("accept");
                     continue;
             }
             pinfo->fd=cfd;
             //创建子线程
             pthread_t tid;
             pthread_create(&tid,NULL,working,pinfo);
             phread_detach(tid);

        }
        close(fd);
        return 0;
}
void working(void *arg)
{

        //5.连接成功打印客户端信息
        struct SockInfo *pinfo = (struct SockInfo*) arg;
        char ip[32];
        printf("客户端ip: %s, 端口: %d\\n",
                inet_ntop(AF_INET,&pinfo->addr.sin_addr.s_addr,ip,sizeof(ip)),
                ntohs(&pinfo->addr.sin_port));

        while(1)
        {
                //接受数据
                char buf[1024];
                int len =recv(pinfo->fd,buf,sizeof(buf),0);
                if(len>0)
                {
                        printf("client say: %s",buf);
                        send(pinfo->fd,buf,sizeof(buf),0);
                }
                else if(len==0)
                {
                        printf("clinet closed!\\n");
                        break;
                }else
                {
                        perror("recv");
                        break;
                }

        }
        //关闭socket
        close(pinfo->fd);
        return NULL;
}
                                                

客户端

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

int main()
{ 
        //1.创建通信的套接字
        int fd = socket(AF_INET,SOCK_STREAM,0);
        if(fd==-1)
        {
                perror("socket");
                return -1;
        }
        //2.连接服务器的ip 端口
        struct sockaddr_in saddr;
        saddr.sin_family =AF_INET;
        saddr.sin_port = htons(9999);
        inet_pton(AF_INET,"121.41.88.239",&saddr.sin_addr.s_addr);
        int ret = connect(fd,(struct sockaddr*) &saddr,sizeof(saddr));
        if(ret==-1)
        {
                perror("connect");
                return -1;
        }

        int number=0;
        while(1)
        {
                //接受数据
                char buff[1024];
                sprintf(buff,"hello %d\\n ",number++);
                send(fd,buff,strlen(buff)+1,0);

                memset(buff,0,sizeof buff);
                int len=recv(fd,buff,sizeof(buff),0);
                if(len>0)
                {
                        printf("server say: %s\\n",buff);
                }else if(len==0)
                {
                        printf("server closed!\\n");
                        break;
                }else
                {
                        perror("recv");
                        break;
                }

                sleep(1);

        }

        //关闭socket
        close(fd);
        return 0;
}

以上是关于Linux网络编程基础及多线程并发案例的主要内容,如果未能解决你的问题,请参考以下文章

Linux下的TCP/IP编程----进程及多进程服务端

(转载)Step by Step:Linux C多线程编程入门(基本API及多线程的同步与互斥)

Java并发编程(02):线程核心机制,基础概念扩展

Java并发编程基础(入门篇)

Java并发编程与高并发解决方案 视频教程

Linux学习资料整理