linux网络编程-多线程实现TCP并发服务器
客户端跟服务端通信流程
服务端流程步骤
socket函数创建监听套接字lfd;
bind函数将监听套接字绑定ip和端口;
listen函数将服务器设置为被动监听状态,同时创建一条未完成连接队列(没走完tcp三次握手流程的连接),和一条已完成连接队列(已完成tcp三次握手的连接);
accept函数循环的从已完成连接的队列中提取连接,并返回一个新的套接字cfd跟客户端进行通信;
pthread_create函数创建一个子线程,跟客户端进行通信;
子线程:read函数循环的从r缓冲区读取客户端发送的数据,write函数将要发送给客户端的数据写入w缓冲区;
close函数关闭套接字;
客户端流程步骤
socket函数创建套接字;
connect函数连接服务器;
write函数将要发送给服务端的数据写入w缓冲区,read函数从r缓冲区读取服务器发送给客户端的数据;
close函数关闭套接字;
相关函数
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
功能:创建一个用于网络通信的套接字文件描述符参数:domain:协议族(AF_INET:ipv4,AF_INET6:ipv6,等等)type:套接字类型(SOCK_DGRAM:udp,SOCK_STREAM:tcp,等等)protocol:用于制定某个协议的特定类型,即type类型中的某个类型,通常不用管它,设置为0返回值:成功则返回socket套接字描述符, 失败返回-1,并设置errno
#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen);
功能:将sockfd绑定ip和端口参数:sockfd:套接字my_addr:存放有协议,ip,端口的结构体信息addrlen:my_addr结构体大小返回值:成功返回0,失败返回-1,并设置errno
#include <sys/socket.h>
int listen(int s, int backlog);功能:让服务器处于被动监听状态,同时创建了一条未完成三次握手的连接队列和一条已经完成三次握 手的连接队列参数:s:套接字backlog:支持未完成连接和已完成连接之和的最大值,一般设置128返回值:成功返回0,失败返回-1,并设置errno
#include <sys/types.h>
#include <sys/socket.h>
int accept(int s, struct sockaddr *addr, socklen_t *addrlen);功能:从已完成连接队列中提取客户端连接参数:s:套接字addr:存放成功连接的客户端的ip,端口等信息结构体addrlen:存放addr结构体大小的变量地址返回值:成功则返回一个非负整数标识这个连接套接字,是否返回-1
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);功能:从文件描述符 fd 中读取 count 字节的数据并放入从 buf 开始的缓冲区中参数:fd:文件描述符buf:缓冲区count:读count字节返回值:成功时返回读取到的字节数,失败返回-1,并设置errno
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);功能:向文件描述符fd所引用的文件中写入从buf开始的缓冲区中count字节的数据参数:fd:文件描述符buf:缓冲区count:写count字节返回值:功时返回所写入的字节数,失败返回-1,并设置errno
#include <unistd.h>
int close(int fd);功能:关闭 一个文件描述符参数:fd:要关闭的文件描述符返回值:成功返回0,失败返回-1,并设置errno
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);功能:连接服务器参数:sockfd:套接字addr:服务器的ip,端口等结构体信息addrlen:addr结构体大小返回值:成功返回0,失败返回-1,并设置errno
#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);功能:创建子线程参数:thread:线程idattr:线程属性,对属性没要求,默认的话,传NULL即可start_routine:线程回调函数arg:需要传进线程回调函数的参数返回值:成功返回0,失败返回错误码
#include <pthread.h>
int pthread_detach(pthread_t thread);功能:设置线程为分离态,线程退出时自己回收资源参数:thread:线程id返回值:成功返回0,失败返回错误码
服务端代码
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/socket.h>
#include <string.h>
#include <errno.h>#define BUF_SIZE 256typedef struct _clientinfo
{int fd;struct sockaddr_in client_addr;
}clientinfo;void sys_err(const char *str, const int exitno)
{perror(str);exit(exitno);
}void sys_errex(const char *str, const int exitno, const int err)
{fprintf(stderr, "%s:%s\n", str, strerror(err));exit(exitno);
}void *t_client(void *arg)
{clientinfo *client = (clientinfo *)arg;char buf[BUF_SIZE] = "";int size;char ip[INET_ADDRSTRLEN] = "";printf("client: %s, port = %d connect success\n", inet_ntop(AF_INET, &client->client_addr.sin_addr.s_addr, ip, sizeof(ip)), ntohs(client->client_addr.sin_port));while(1){size = read(client->fd, buf, sizeof(buf));if (size < 0){if (EINTR == errno) //被信号中断不能退出continue;sys_err("read error", 1);}else if (size == 0) //客户端退出{printf("client: %s, port = %d exit\n", ip, ntohs(client->client_addr.sin_port));break;}printf("client: %s, port = %d say: %s\n", ip, ntohs(client->client_addr.sin_port), buf);while(write(client->fd, buf, size) == -1){if (EINTR == errno) //被信号中断不能退出continue;sys_err("write error", 1);}memset(buf, 0, sizeof(buf));}close(client->fd);free(client);printf("thread %lu exit success\n", pthread_self());
}int main(int argc, char *argv[])
{if (argc < 2){printf("%s port\n", argv[0]);exit(1);}//创建监听socketint lfd;int cfd;int ret;int optval = 1;struct sockaddr_in server_addr;struct sockaddr_in client_addr;socklen_t len = sizeof(client_addr);pthread_t tid;lfd= socket(AF_INET, SOCK_STREAM, 0);if (lfd < 0)sys_err("socket error", 1);//设置端口复用,解决 address already user 问题ret = setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, (const void *)&optval, sizeof(optval));if (ret < 0)sys_err("setsockopt error", 1);//绑定ip, 端口server_addr.sin_family = AF_INET;server_addr.sin_port = htons((unsigned short)atoi(argv[1]));server_addr.sin_addr.s_addr = 0; //0表示将本机所有ip都绑定上ret = bind(lfd, (struct sockaddr *)&server_addr, sizeof(server_addr));if (ret < 0)sys_err("bind error", 1);//监听ret = listen(lfd, 128); //指定未完成连接队列的最大长度为128if (ret < 0)sys_err("listen error", 1);printf("server start success\n");while(1){cfd = accept(lfd, (struct sockaddr *)&client_addr, &len);if (cfd < 0){if (ECONNABORTED == errno || EINTR == errno) //软件引起的连接中止或被信号中断,不能退出 continue;sys_err("accept error", 1);}clientinfo *p = malloc(sizeof(clientinfo));if (!p)fprintf(stderr, "malloc error\n");p->fd = cfd;p->client_addr = client_addr;ret = pthread_create(&tid, NULL, t_client, (void *)p);if (0 != ret)sys_errex("pthread_create error", 1, ret);ret = pthread_detach(tid); //设置为分离态,线程结束时自己回收资源if (0 != ret)sys_errex("pthread_detach error", 1, ret);}close(lfd); return 0;
}
客户端代码
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>#define BUF_SIZE 256int main(int argc, char *argv[])
{if (argc < 3){printf("%s sever_ip server_port\n", argv[0]);exit(1);}//创建流式套接字int fd = socket(AF_INET, SOCK_STREAM, 0);if (fd < 0){perror("socket");exit(1);}//连接服务器struct sockaddr_in addr;addr.sin_family = AF_INET;addr.sin_port = htons((unsigned short)atoi(argv[2]));addr.sin_addr.s_addr = inet_addr(argv[1]);int ret = connect(fd, (struct sockaddr *)&addr, sizeof(addr));if (ret < 0){perror("connect");exit(1);}printf("connect server success\n");//读写数据char buf_w[BUF_SIZE] = "";char buf_r[BUF_SIZE] = "";ssize_t size_r = 0;while(1){fgets(buf_w, sizeof(buf_w), stdin);buf_w[strlen(buf_w) - 1] = 0;write(fd, buf_w, strlen(buf_w));size_r = read(fd, buf_r, sizeof(buf_r));if (size_r == 0) //服务器断开break;elseprintf("%s\n", buf_r);memset(buf_w, 0, sizeof(buf_w));memset(buf_r, 0, sizeof(buf_r));}//关闭套接字close(fd);return 0;
}
结果
服务器启动,同时连上3个客户端
此时,ps -eLf查看LWP,服务端有4条线程(1条监听线程,3条与连接上的客户端通信的线程)
3个客户端分别发一条消息给服务器,服务器接收到,并发回到客户端
3个客户端都退出
此时,ps -eLf查看LWP,服务器只剩1条监听线程
ending
linux网络编程-多线程实现TCP并发服务器相关推荐
- Linux网络编程服务器模型选择之并发服务器(上)
转载:http://www.cnblogs.com/lizhenghn/p/3617666.html 与循环服务器的串行处理不同,并发服务器对服务请求并发处理.循环服务器只能够一个一个的处理客户端的请 ...
- 第4章 Linux网络编程 22.多进程实现并发服务器、多线程实现并发服务器
目录 多线程实现并发服务器 多进程实现并发服务器 多线程实现并发服务器 server_thread.c #include <stdio.h> #include <arpa/inet. ...
- Linux网络编程服务器模型选择之并发服务器(下)
转载:http://www.cnblogs.com/lizhenghn/p/3618986.html 前面两篇文章(参见)分别介绍了循环服务器和简单的并发服务器网络模型,我们已经知道循环服务器模型效率 ...
- Linux网络编程基础<多进程并发服务器>
一.应用场景 最简单的socket示列代码只能一个客户端连接一个服务器,并不支持多个客户端对服务器的连接,为了能让多个客户端进行连接所以需要多进程或者多线程处理 二.思路解析 服务器端的程序是俩个套接 ...
- 客户和服务器之间响应的序列,网络编程-第五讲-TCP客户-服务器程序例子.pdf-原创力文档...
网络编程 第五讲TCP客户-服务器程序例子 多进程并发服务器基本架构 pid_t pid; int listenfd, connfd; listenfd = Socket( ... ); /* fil ...
- Linux网络编程---详解TCP
Linux网络编程---详解TCP的三次握手和四次挥手_shanghx_123的博客-CSDN博客_tcp的协议数据单元被称为 TCP协议详解(TCP报文.三次握手.四次挥手.TIME_WAIT状态. ...
- 27.Linux网络编程socket变成 tcp 高并发 线程池 udp
好,咱们开始上课了,从今天开始咱们连续讲 8 天的,网络编程这个还是在linux环境下去讲,咱们先看一下咱们这 8 天都讲什么东西,跟大家一块来梳理一下,你先有个大概的印象,这些你也不要记,那么网络编 ...
- 29.Linux网络编程熟练掌握 TCP 状态张换图熟练堂握端口复用的方法了解半关闭的概念和实现方式了解多路10 转接模型熟练掌握 select 函数的使用熟练使用 fdset 相关函数的使用能够编写
把昨天的 第二天的内容说一下,复习一下,第二天 讲的东西不算多,但是有两个作业题来写一写, 大致浏览一下,三次握手 四次挥手的过程,大家有没有画一下? 能画出来吗?同学们,大家注意 这个写代码的时候其 ...
- Linux网络编程服务器模型选择之循环服务器
转载:http://www.cnblogs.com/lizhenghn/p/3617608.html 在网络程序里面,通常都是一个服务器处理多个客户机,为了出个多个客户机的请求,服务器端的程序有不同的 ...
最新文章
- python获取excel某一列-Python 读取excel指定的列
- 微软全球执行副总裁沈向洋离职;马云回应双十一数据作假;GitHub 官方终于出 App 了! | 极客头条...
- Android 最火高速开发框架AndroidAnnotations使用具体解释
- python有限元传热求解_二维稳态热传导基本方程的有限元求解(2)
- php app 签名错误,微信小程序基于PHP微信支付“签名错误”填坑
- SharedCache-共享缓存概况和初步运用实例(1)
- 【期权机理与python实践】
- 25位IT史上最具影响力人物
- 3秒测试:组建一个网络,需要几个硬件设备搞定?
- python在多继承中子类调用某个父类的同名方法之 super讲解(mro机制)***
- 2015上半年手机GPU排行榜
- 中国天气网天气api接口 天气预报调用方法 2020
- 关于java播放wav格式音乐问题及代码解释
- HanLP极致简繁转换详细讲解 1
- 理解快速离散傅里叶变换算法(FFT)
- 合同诈骗案立案标准是怎样的
- 利用Excel数据爬虫
- Redis bitmap数据格式优化 (RoaringBitmap)
- java解析pdf文档(前面写了docx的解析所以也思考写一份pdf的解析)--PdfReader底层分析
- C语言输出长方柱的体积,求3个长方柱的体积和表面积