言之者无罪,闻之者足以戒。 - “诗序”
四,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
上面的程序存在很多小问题,但是我现在也不熟练,也不能改的更好了。