socket TCP长连接和UDP网络编程实例、阻塞与非阻塞select、常见面试题

一、阻塞与非阻塞selectLinux c socket编程中,bind()用于绑定IP和端口,listen()监听连接端口,如果没有连接到来,那么accept()会一直阻塞,直到有数据进来,但是如果我们需要有多个客户端访问服务端那就麻烦了,我们想要能够一边处理accept()到的请求,同时也能等待或accept()其它请求。
目前解决阻塞的方式有多进程、多线程、fcntl设置非阻塞模式、多路同步I/O select。

socket TCP长连接和UDP网络编程实例、阻塞与非阻塞select、常见面试题

文章图片
非阻塞select的函数定义:select(int number, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout),其中:
【socket TCP长连接和UDP网络编程实例、阻塞与非阻塞select、常见面试题】number为最大的文件描述符加1;
readfds,writefds,exceptfds为描述符组,返回描述符的读、写、异常的情况。
描述符组的处理方式如下:
FD_CLR(int fd, fd_set *set); // 清除描述符组的fd位 FD_ISSET(int fd, fd_set *set); // 检查set中的fd位是否为真 FD_SET(int fd, fd_set *set); // 设置set中的fd位 FD_ZERO(fd_set *set); // 清除set的全部位。

timeval结构体的声明如下:
struct timeval{ time_t tv_sec; time_t tv_usec; }

本文主要使用多线程的方式解决阻塞,下面一起看看多线程的实现实例。
二、TCP多客户端长连接
socket TCP长连接和UDP网络编程实例、阻塞与非阻塞select、常见面试题

文章图片
1、线程相关操作函数
创建多线程的头文件为:< pthread.h> ,常见的函数有:
(1)int pthread_create(pthread_t *restrict ptid, const pthread_attr_t *restrict attr, void *(thread_run), void *restrict arg),
该函数用于创建一个线程,编译时要加上参数-lpthread,成功返回0,错误返回错误编号,其中
ptid为线程标识符;
attr为线程的属性;
thread_run实际的线程代码,一个函数指针;
arg为线程函数的参数
(2)int pthread_equal(pthread_t pt1, thread_t pt2);
比较两个线程的标识ID是否相等,返回0不相等,返回非0则相等。
(3)pthread_t pthread_self();
返回调用线程的标识ID。
2、创建socket TCP服务端
#include < stdio.h> #include < stdlib.h> #include < string.h> #include < time.h> #include < sys/types.h> #include < sys/socket.h> #include < netinet/in.h> #include < arpa/inet.h> #include < pthread.h>#define PORT 8080 #define IP "192.168.1.2" #define MAX_SOCKET 4struct html_request{ char version[16]; char protocol[16]; char method[16]; char content[256]; }; struct html_response{ char version[16]; char protocol[16]; char content[256]; }; int init_tcp_socket(char *ip, int port){ int socket_s = socket(AF_INET, SOCK_STREAM, 0); if(socket_s < 0){ perror("socket"); exit(1); } struct sockaddr_in addr; bzero(& addr, sizeof(struct sockaddr_in)); addr.sin_family = AF_INET; addr.sin_port = htons(port); addr.sin_addr.s_addr = inet_addr(ip); if(bind(socket_s, (struct sockaddr *)& addr, sizeof(struct sockaddr)) < 0){ perror("socket bind"); exit(1); } if(listen(socket_s, MAX_SOCKET) < 0){ perror("socket listen"); exit(1); } return socket_s; }void thread_run(void *arg){ int socket_n = (int)arg; int length_rst = sizeof(struct html_request); int length_rep = sizeof(struct html_response); while(1){ struct html_request request; memset(& request, 0, sizeof(length_rst)); if(recv(socket_n, & request, length_rst, 0) < 0){ perror("socket recv"); break; } int time = get_time(); printf("%dversion: %s\n", time, request.version); printf("%dprotocol: %s\n", time, request.protocol); printf("%dmethod: %s\n", time, request.method); printf("%dcontent: %s\n\n", time, request.content); struct html_response response = {"1.0", "HTTP Response", "thanks for visiting!"}; // response.version = 1.3; // response.protocol = "HTTP"; // response.content = "thanks for visiting!"; if(send(socket_n, & response, length_rep, 0) < 0){ perror("socket send"); break; } } close(socket_n); }int get_time(){ int t = time(NULL); return t; }int main(int argc, char *argv[]){ int socket_s = init_tcp_socket(IP, PORT); while(1){ struct sockaddr_in c_addr; int len = sizeof(struct sockaddr); int socket_n = accept(socket_s, (struct sockaddr *)& c_addr, & len); if(socket_n < 0){ perror("socket accept"); continue; } pthread_t id; int code = pthread_create(& id, NULL, (void*)thread_run, (void*)socket_n); if(code){ perror("thread create"); exit(1); } } return 0; }

3、创建socket TCP客户端
#include < stdio.h> #include < stdlib.h> #include < string.h> #include < time.h> #include < sys/types.h> #include < sys/socket.h> #include < netinet/in.h> #include < arpa/inet.h>#define SERVER_PORT 8080 #define SERVER_IP "192.168.1.2"struct html_request{ char version[16]; char protocol[16]; char method[16]; char content[256]; }; struct html_response{ char version[16]; char protocol[16]; char content[256]; }; int init_tcp_socket(char *ip, int port){ int socket_s = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in addr; bzero(& addr, sizeof(struct sockaddr_in)); addr.sin_family = AF_INET; addr.sin_port = htons(port); addr.sin_addr.s_addr = inet_addr(ip); connect(socket_s, (struct sockaddr *)& addr, sizeof(struct sockaddr)); return socket_s; }void talk(int socket_s){ int length_rst = sizeof(struct html_request); int length_rep = sizeof(struct html_response); while(1){ char message[256]; memset(message, 0 ,sizeof(message)); read(0, message, sizeof(message)); struct html_request request = {"1.7", "HTTP Request", "GET", ""}; bzero(request.content, 256); strcpy(request.content, message); if(send(socket_s, & request, length_rst, 0) < 0){ perror("socket send"); return; }struct html_response response; if(recv(socket_s, & response, length_rep, 0) < 0){ perror("socket recv"); return; } int time = get_time(); printf("%d version: %s\n", time, response.version); printf("%d protocol: %s\n", time, response.protocol); printf("%d content: %s\n\n", time, response.content); } close(socket_s); }int get_time(){ int t = time(NULL); return t; }int main(int argc, char *argv[]){ int socket_s = init_tcp_socket(SERVER_IP, SERVER_PORT); talk(socket_s); return 0; }

三、UDP编程实例
socket TCP长连接和UDP网络编程实例、阻塞与非阻塞select、常见面试题

文章图片
1、创建UDP服务端
#include < stdio.h> #include < stdlib.h> #include < string.h> #include < time.h> #include < sys/types.h> #include < sys/socket.h> #include < netinet/in.h> #include < arpa/inet.h> #include < pthread.h>#define PORT 8080 #define IP "192.168.1.2" #define MAX_SOCKET 4int main(int argc, char *argv[]){ int socket_s = socket(AF_INET, SOCK_DGRAM, 0); if(socket_s < 0){ perror("socket"); exit(1); } struct sockaddr_in addr, c_addr; int len = sizeof(struct sockaddr_in); bzero(& addr, len); bzero(& c_addr, len); addr.sin_family = AF_INET; addr.sin_port = htons(PORT); addr.sin_addr.s_addr = inet_addr(IP); if(bind(socket_s, (struct sockaddr *)& addr, len) < 0){ perror("socket bind"); exit(1); }char buffer[256]; while(1){ bzero(buffer, 256); if(recvfrom(socket_s, & buffer, sizeof(buffer), 0, (struct sockaddr *)& c_addr, & len) < 0){ perror("socket recvfrom"); continue; } printf("message: %s\n", buffer); char response[256] = "server response."; if(sendto(socket_s, & response, sizeof(response), 0, (struct sockaddr *)& c_addr, sizeof(struct sockaddr)) < 0){ perror("socket sendto"); continue; } } close(socket_s); return 0; }

2、创建UDP客户端
#include < stdio.h> #include < stdlib.h> #include < string.h> #include < time.h> #include < sys/types.h> #include < sys/socket.h> #include < netinet/in.h> #include < arpa/inet.h>#define PORT 8080 #define IP "192.168.1.2"int main(int argc, char *argv[]){ int socket_s = socket(AF_INET, SOCK_DGRAM, 0); if(socket_s < 0){ perror("socket"); exit(1); } struct sockaddr_in addr; int len = sizeof(struct sockaddr_in); bzero(& addr, len); addr.sin_family = AF_INET; addr.sin_port = htons(PORT); addr.sin_addr.s_addr = inet_addr(IP); while(1){ char message[256]; read(0, message, sizeof(message)); if(sendto(socket_s, & message, sizeof(message), 0, (struct sockaddr *)& addr, len) < 0){ perror("socket sendto"); continue; } char buffer[256]; if(recvfrom(socket_s, & buffer, sizeof(buffer), 0, (struct sockaddr *)& addr, & len) < 0){ perror("socket recvfrom"); continue; } printf("response: %s\n", buffer); } close(socket_s); return 0; }

四、Linux网络编程常见面试题1、线程和进程的区别
线程属于进程,进程属于应用,线程基于栈编程,进程可单独编程。
2、TCP和UDP的区别
TCP面向连接,建立连接器首先进行TCP三次握手,以确保连接的可靠性,不会丢失数据或乱序,提供重发机制。
3、linux下多线程如何同步
使用互斥锁、条件变量和信号量

    推荐阅读