一、阻塞与非阻塞selectLinux c socket编程中,bind()用于绑定IP和端口,listen()监听连接端口,如果没有连接到来,那么accept()会一直阻塞,直到有数据进来,但是如果我们需要有多个客户端访问服务端那就麻烦了,我们想要能够一边处理accept()到的请求,同时也能等待或accept()其它请求。
目前解决阻塞的方式有多进程、多线程、fcntl设置非阻塞模式、多路同步I/O 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多客户端长连接
文章图片
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编程实例
文章图片
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下多线程如何同步
使用互斥锁、条件变量和信号量
推荐阅读
- Linux socket网络编程(一)(TCP编程实例、socket编程函数、套接字类型和网络模型)
- TCP/IP五层模型之数据包发送流程
- 什么是OSI七层模型和网络五层模型(有什么功能或作用? – 深入浅出HTTP原理)
- 计算机网络编程|计算机网络学习笔记1-网络编程基础
- 计算机网络|计算机网络---TCP/IP网络编程实验
- 计算机网络|计算机网络、网络编程笔记
- 计算机网络|计算机网络 --- 初始网络
- IPD|IPD解读--华为500强的研发第一名,除了钱还有IPD
- 计算机网络|计算机网络期末4小时速成