600字范文,内容丰富有趣,生活中的好帮手!
600字范文 > linux网络编程-多线程实现TCP并发服务器

linux网络编程-多线程实现TCP并发服务器

时间:2019-10-01 11:40:18

相关推荐

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😃

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。