一、多线程的基本概念、优势
1、线程:有时又称轻量级进程,程序执行的最小单位,系统独立调度和分派cpu的基本单位,它是进程中的一个实体。一个进程中可以有
多个线程,这些线程共享进程的所有资源,线程本身只包含一点必不可少的资源。
2、同步、异步、并发、并行
3、优势:
1)、在多处理器中开发程序的并行性
2)、在等待慢速IO操作时,程序可以执行其他操作,提高并发性
3)、模块化的编程,能更清晰的表达程序中独立事件的关系,结构清晰
4)、占用较少的系统资源
4、线程创造,获取ID,生命周期
5、线程控制:终止、连接、取消、发送信号、清除操作
6、线程同步:互斥量、读写锁、条件变量
7、线程高级控制:一次性初始化、线程属性、同步属性、私有数据、安全的fork
二、TCP服务器创建的步骤
1、创建一个socket
■Socket(套接字)实质上提供了进程通信的端点.进程通信之前,双方首先必须各自的一个端点,否则是没有办法通信的。通过socket将IP地址
和端口绑定之后,客户端就可以和服务器通信了
■当我们访问套接字时,要像访问文件一样使用文件描述符。
■创造一个套接字可以使用socket()函数
2、绑定IP地址和端口信息到socket,使用函数bind()
1)、IP地址
在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 char s_b1,
s_b2,
s_b3,
s_b4;
} 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)的形式表示,而在struct in_addr中使用的IP地址是由32位的整数表示的,为了转换我们可以使用下面
两个函数:
int inet_aton(const char *cp,struct in_addr *inp)
char *inet_ntoa(struct in_addr in)
函数里面a 代表ascii ,n 代表network。inet_aton是将a.b.c.d形式的IP转换为32位的IP,存储在inp指针里面。inet_ntoa是将32位IP转换为
a.b.c.d的格式。字节序转换
不同类型的CPU 对变量的字节存储顺序可能不同:有的系统是高位在前,低位在后,而有的系统是低位在前,高位在后,而网络传输的数据
顺序是一定要是统一的。所以当内部字节存储顺序和网络字节序( big endian )不同时,就一定要进行转换。
字节序转换,32bit的整数(0x01234567)从地址0x100开始:
htons 把unsigned short类型从主机序转换到网络序
htonl 把unsigned long类型从主机序转换到网络序
ntohs 把unsigned short类型从网络序转换到主机序
ntohl 把unsigned long类型从网络序转换到主机序
2)、bind函数
绑定服务器的地址和端口到socket,这样做就是让客户端来发现用以连接的服务器的地址
#include
int bind(int sockfd, const struct sockaddr *addr, socklen_t len);
返回值:成功返回0,失败返回-1
参数sockfd: 服务器socket
参数addr: 服务器的地址,对于因特网域,如果设置地址为INADDR_ANY,套接字可以绑定到所有的网络端口。这意味着可以收到
这个系统所有网卡的数据包。
一般我们在使用sockaddr_in类型的结构体代替sockaddr结构体
1、线程:有时又称轻量级进程,程序执行的最小单位,系统独立调度和分派cpu的基本单位,它是进程中的一个实体。一个进程中可以有
多个线程,这些线程共享进程的所有资源,线程本身只包含一点必不可少的资源。
2、同步、异步、并发、并行
3、优势:
1)、在多处理器中开发程序的并行性
2)、在等待慢速IO操作时,程序可以执行其他操作,提高并发性
3)、模块化的编程,能更清晰的表达程序中独立事件的关系,结构清晰
4)、占用较少的系统资源
4、线程创造,获取ID,生命周期
5、线程控制:终止、连接、取消、发送信号、清除操作
6、线程同步:互斥量、读写锁、条件变量
7、线程高级控制:一次性初始化、线程属性、同步属性、私有数据、安全的fork
二、TCP服务器创建的步骤
1、创建一个socket
■Socket(套接字)实质上提供了进程通信的端点.进程通信之前,双方首先必须各自的一个端点,否则是没有办法通信的。通过socket将IP地址
和端口绑定之后,客户端就可以和服务器通信了
■当我们访问套接字时,要像访问文件一样使用文件描述符。
■创造一个套接字可以使用socket()函数
1)、IP地址
在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 char s_b1,
s_b2,
s_b3,
s_b4;
} 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)的形式表示,而在struct in_addr中使用的IP地址是由32位的整数表示的,为了转换我们可以使用下面
两个函数:
int inet_aton(const char *cp,struct in_addr *inp)
char *inet_ntoa(struct in_addr in)
函数里面a 代表ascii ,n 代表network。inet_aton是将a.b.c.d形式的IP转换为32位的IP,存储在inp指针里面。inet_ntoa是将32位IP转换为
a.b.c.d的格式。字节序转换
不同类型的CPU 对变量的字节存储顺序可能不同:有的系统是高位在前,低位在后,而有的系统是低位在前,高位在后,而网络传输的数据
顺序是一定要是统一的。所以当内部字节存储顺序和网络字节序( big endian )不同时,就一定要进行转换。
字节序转换,32bit的整数(0x01234567)从地址0x100开始:
htons 把unsigned short类型从主机序转换到网络序
htonl 把unsigned long类型从主机序转换到网络序
ntohs 把unsigned short类型从网络序转换到主机序
ntohl 把unsigned long类型从网络序转换到主机序
绑定服务器的地址和端口到socket,这样做就是让客户端来发现用以连接的服务器的地址
#include
int bind(int sockfd, const struct sockaddr *addr, socklen_t len);
返回值:成功返回0,失败返回-1
参数sockfd: 服务器socket
参数addr: 服务器的地址,对于因特网域,如果设置地址为INADDR_ANY,套接字可以绑定到所有的网络端口。这意味着可以收到
这个系统所有网卡的数据包。
一般我们在使用sockaddr_in类型的结构体代替sockaddr结构体
3、
设置允许的最大连接数,使用函数listen()
服务器调用listen函数来宣告可以接受连接请求
#include
int listen(int sockfd, int backlog);
返回值: 成功返回0,失败返回-1
参数backlog:用于表示服务器能接受的请求数量
4、 等待来自客户端的连接请求,使用函数accept()
一旦服务器调用了listen,套接字就能接收连接请求。使用函数accept函数来接受并建立请求
#include
int accept(int sockfd, struct sockaddr *restrict addr, socklen_t *restrict len);
参数sockfd:服务器socket
参数addr: 用来存放客户端的地址,如果addr的空间足够大,系统会自动填充。
参数len: addr的长度
如果不关心客户端ip,那么addr和len可以设为NULL
返回值:成功则返回套接字描述符,失败返回-1
注意:
1)、accept返回一个新的socket关联到客户端,它与原始的socket有相同的套接字类型和协议族传递给accept的原始socket并没有关联
客户端,它要继续保持可用状态 ,接收其他请求。
2)、accept是一个阻塞的函数,会一直等到有客户端的请求
5、 收发数据,用函数recv()、send()/sendto()或者read()、write()
6、
关闭网络连接,close()
三、源代码
服务器调用listen函数来宣告可以接受连接请求
#include
int listen(int sockfd, int backlog);
返回值: 成功返回0,失败返回-1
参数backlog:用于表示服务器能接受的请求数量
4、 等待来自客户端的连接请求,使用函数accept()
一旦服务器调用了listen,套接字就能接收连接请求。使用函数accept函数来接受并建立请求
#include
int accept(int sockfd, struct sockaddr *restrict addr, socklen_t *restrict len);
参数sockfd:服务器socket
参数addr: 用来存放客户端的地址,如果addr的空间足够大,系统会自动填充。
参数len: addr的长度
如果不关心客户端ip,那么addr和len可以设为NULL
返回值:成功则返回套接字描述符,失败返回-1
注意:
1)、accept返回一个新的socket关联到客户端,它与原始的socket有相同的套接字类型和协议族传递给accept的原始socket并没有关联
客户端,它要继续保持可用状态 ,接收其他请求。
2)、accept是一个阻塞的函数,会一直等到有客户端的请求
5、 收发数据,用函数recv()、send()/sendto()或者read()、write()
三、源代码
点击(此处)折叠或打开
/*DATA: 2015-4-20
*AUTHOR: WJ
*DESCRIPTION: 多线程TCP服务器
* 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
*/
#include "apue.h"
#define MAX_LISTEN 10
char buf[100];
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;
sd = socket(AF_INET, SOCK_STREAM, 0);
if(sd == -1)
{
printf("create socket failed, errno is %d\n", errno);
return;
}
server_ip.sin_family = AF_INET;
server_ip.sin_port = htons(5678);
server_ip.sin_addr.s_addr = htonl(INADDR_ANY);
memset(server_ip.sin_zero, 0, 8);
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;
}
remote_len = sizeof(struct sockaddr);
while(1)
{
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;
}
err = pthread_create(&tid[i], NULL, thread_fun, (void *)i);
if(err != 0)
{
printf("create new thread failed\n");
close(ad[i]);
}
i++;
}
close(sd);
return 0;
}
*AUTHOR: WJ
*DESCRIPTION: 多线程TCP服务器
* 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
*/
#include "apue.h"
#define MAX_LISTEN 10
char buf[100];
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;
sd = socket(AF_INET, SOCK_STREAM, 0);
if(sd == -1)
{
printf("create socket failed, errno is %d\n", errno);
return;
}
server_ip.sin_family = AF_INET;
server_ip.sin_port = htons(5678);
server_ip.sin_addr.s_addr = htonl(INADDR_ANY);
memset(server_ip.sin_zero, 0, 8);
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;
}
remote_len = sizeof(struct sockaddr);
while(1)
{
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;
}
err = pthread_create(&tid[i], NULL, thread_fun, (void *)i);
if(err != 0)
{
printf("create new thread failed\n");
close(ad[i]);
}
i++;
}
close(sd);
return 0;
}