Linux学习之多线程编程(简单TCP服务器的创建)

言之者无罪,闻之者足以戒。 - “诗序”

四,Linux的多线程综合练习

线程:有时又称轻量级进程,程序执行的最小单位,系统独立调度和分派CPU的基本单位,它是进程中的一个实体一个进程中可以有多个线程,这些线程共享进程的所有资源,线程本身只包含一点必不可少的资源。

1,则TCP服务器创建的步骤:

socket(套接字)实质上提供了进程通信的端点,进程通信之前,双方首先必须有各自的一个端点,否则是没有办法通信的。通过套接字将IP地址和端口绑定之后,客户端就可以和服务器通信了。

当我们访问套接字时,像要访问文件一样使用文件描述符

(1),插座创造一个套接字

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

头文件的:#include <SYS / socket.h>中

第一个参数:通信域,确定通信特性,包括地址格式域描述

第二个参数:套接字类型

第三个参数:指定相应的传输协议

返回值:成功则返回套接字文件描述符,失败返回-1

参数域:通信域,确定通信特性,包括地址格式域描述

描述
AF_INET IPv4的因特网域
AF_INET6 IPv6的因特网域
AF_UNIX UNIX域
AF_UNSPEC 未指定

参数类型:套接字类型类型描

类型 描述
SOCK_DGRAM 长度固定的,无连接的不可靠报文传输
SOCK_RAW IP协议的数据报接口
SOCK_SEQPACKET 长度固定,有序,可靠的面向连接报文传递
SOCK_STREAM 有序,可靠,双向的面向连接的字节流

 参数protocol,指定相应的传输协议,也就是诸如TCP或UDP协议等等,系统针对每一个协议簇与类型提供了一个默认的协议,我们通过把协议设置为0来使用这个默认的值。

在socket程序设计中,struct sockaddr_in(或者struct sock_addr)用于记录网络地址

struct sockaddr_in
{
  short int sin_family; / *协议族* /
  unsigned short int sin_port; / *端口号* /
  struct in_addr sin_addr; / *协议特定地址* /
  unsigned char sin_zero [8]; / *填0 * /
}
typedef struct in_addr {
  union {
       struct {
           unsigned charwers1,
           shield2,
           shield3,
           shield4;
           } S_un_b;
       struct {
           unsigned short s_w1,
           s_w2;
       } S_un_w;
       unsigned long s_addr;
  } S_un;
} IN_ADDR;

IP地址通常由数字加点(192.168.0.1)的形式表示,而在结构in_addr中使用的IP地址是由32位的整数表示的,为了转换我们可以使用下面两个函数:

 int inet_aton(const char * cp,struct in_addr *
 inp char * inet_ntoa(struct in_addr in)

函数里面a代表ascii,n代表网络 .inet_aton是将abcd形式的IP转换为32位的IP,存储在inp指针里面.inet_ntoa是将32位IP转换为ABCD的格式

不同类型的CPU对变量的字节存储顺序可能不同:有的系统是高位在前,低位在后,而有的系统是低位在前,高位在后,而网络传输的数据顺序是一定要是统一的。所以当内部字节存储顺序和网络字节序(big endian)不同时,就一定要进行转换。

字节序转换,32位的整数(0x01234567)从地址为0x100开始:


htons to unsigned short类型从主机序转换到网络序
htonl把unsigned long类型从主机序转换到网络序
ntohs把unsigned short类型从网络序转换到主机序
ntohl把unsigned long类型从网络序转换到主机序

(2),结合绑定服务器的地址和端口到插座

这样做就是让客户端来发现用以连接的服务器的地址

int bind(int sockfd,const struct sockaddr * addr,socklen_t len)

头文件:#include <sys / socket.h>

第一个参数:服务器插槽

第二个参数:服务器的地址

第三个参数:地址的长度

返回值:成功返回0,失败返回-1

 参数sockfd:服务器socket
 参数addr:服务器的地址,对于因特网域,如果设置地址为INADDR_ANY,套接字可以绑定到所有的网络端口。这意味着可以收到这个系统所有网卡的数据包。一般我们在使用sockaddr_in类型的结构体代替sockaddr结构体
 参数len:addr的长度

(3),听设置允许的最大连接数

int listen(int sockfd,int backlog)

头文件:#include <sys / socket.h>

第一个参数:服务器插槽

第二个参数:用于表示服务器能接受的请求数量

返回值:成功返回0,失败返回-1

服务器调用听函数来宣告可以接受连接请求

(4),接受等待来自客户端的连接请求

int accept(int sockfd,struct sockaddr * restrict addr,socklen_t * restrict len)

头文件的:#include <SYS / socket.h>中

第一个参数:服务器插槽

第二个参数:用来存放客户端的地址,如果地址的空间足够大,系统会自动填充。

第三个参数:地址的长度

返回值:成功则返回套接字描述符,失败返回-1

一旦服务器调用了听,套接字就能接收连接请求。使用函数接受函数来接受并建立请求

 注意:
1,接受返回一个新的socket关联到客户端,它与原始的socket有相同的套接字类型和协议族。传递给接受的原始socket并没有关联客户端,它要继续保持可用状态,接收其他请求
.2,接受是一个阻塞的函数,会一直等到有客户端的请求。

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



/*
1,int socket(int domain,int type,int protocol)
2,int bind(int sockfd,const struct sockaddr *addr,socklen_t addrlen)
3,int listen(int sockfd,int backlog)
4,int accept(int sockfd,struct sockaddr *addr,socklen_t *addrlen)
5,read,write
*/

#define MAX_LISTEN 10
char buf[10];
int ad[10];
struct sockaddr_in server_ip,remote_ip;

void *thread_fun(void *arg)
{
        while(1)
        {       //持续的读取数据
                printf("read data from client:%s\n",inet_ntoa(remote_ip.sin_addr.s_addr));
                read(ad[(int)arg],buf,100);
                printf("buf is %s\n",buf);
        }
        return NULL;
}

int main()
{
        int server_len,remote_len;
        pthread_t tid[10];

        int err,sd;
        int i=0;
        //创建socket
        sd = socket(AF_INET,SOCK_STREAM,0);
        if(sd == -1)
        {
                printf("create socket failure ,errno is %d\n",errno);
                return;
        }
        //设置ip地址和端口
        server_ip.sin_family = AF_INET;
        server_ip.sin_port = htons(678);
        server_ip.sin_addr.s_addr = htonl(INADDR_ANY);
        memset(server_ip.sin_zero,0,8);

        //绑定ip地址和端口到socket
        err = bind(sd,(struct sockaddr *)(&server_ip),sizeof(struct sockaddr));
        if(err == -1)
        {
                printf("bind error , errno is %d\n",errno);
                close(sd);
                return ;
        }
        //设置服务器到最大链接数
        err = listen(sd,MAX_LISTEN);
        if(err == -1)
        {
                printf("listen error ,errno is %d\n",errno);
                close(sd);
                return;
        }
        //获取客户端ip地址的长度
        remote_len = sizeof(struct sockaddr);

        while(1)
        {
                //等待客户端到请求,如果请求来到,返回一个新到socket
                //服务器和客户端利用新的socket来通信
                ad[i] = accept(sd,(struct sockaddr *)(&remote_ip),&remote_len);
                if(ad[i] == -1)
                {
                        printf("accept error , errno is %d\n",errno);
                        close(sd);
                        return;
                }
                //抛出一个新的线程,在新线程中使用while循环,确保能不停的交换数据
                err = pthread_create(&tid[i],NULL,thread_fun,(void *)i);
                if(err != 0)
                {
                        printf("create new thread failure\n");
                        close(ad[i]);
                }
                i++;
        }
        close(sd);
        return 0;
}

这个程序需要用到一个客户端,给为了大家带来网求方便我直接把软件上传出来https://download.csdn.net/download/weixin_42994525/10737633

CSDN上传没办法免费,我百度网盘分享一下:链接:https://pan.baidu.com/s/1bdyc-SyBf2_oZ0DKOu_JSA提取码:h8yh

上面的程序存在很多小问题,但是我现在也不熟练,也不能改的更好了。

猜你喜欢

转载自blog.csdn.net/weixin_42994525/article/details/83276702