Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接

服务端代码

#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <unistd.h>#define handle_error(cmd,result)    \if(result < 0)              \{                           \perror(cmd);            \exit(EXIT_FAILURE);     \}                           \void * thread_write(void *argv)
{unsigned char * buf_sock_write;buf_sock_write = (unsigned char *)malloc(30);int client_fd = *((int *)argv);//以ctrl+D为结束信号,结束本端的写线程while (read(STDIN_FILENO,buf_sock_write,30)){send(client_fd,buf_sock_write,30,0);memset(buf_sock_write,0,30);}printf("服务端收到关闭信号\n");//关闭WR时,对端的recv会收到一个0字节的数据包,从而结束对端读线程,不同时关闭读端是为了接收对端的关闭信号//本端关闭写端,但是仍然可以接收数据包shutdown(client_fd,SHUT_WR);free(buf_sock_write);
}void * thread_read(void *argv)
{unsigned char * buf_sock_read;buf_sock_read = (unsigned char *)malloc(30);int client_fd = *((int *)argv);//对端关闭WR之后,本端的recv会接收到一个0字节的数据包,从而结束本端的读线程while (recv(client_fd,buf_sock_read,30,0)){printf("%s",buf_sock_read);memset(buf_sock_read,0,30);}printf("客户端断开连接\n");free(buf_sock_read);
}int main(int argc, char const *argv[])
{int server_fd,tmp,client_fd;//创建服务端,客户端ip地址结构体struct sockaddr_in server,client;memset(&server,0,sizeof(server));memset(&client,0,sizeof(client));//1,创建服务端套接字并返回文件描述符,家族选择AF_INET(IPV4),使用TCP就选择SOCK_STREAM,UDP就选择SOCK_DGRAMserver_fd = socket(AF_INET,SOCK_STREAM,0);//2,设置服务端的ip和端口号server.sin_family = AF_INET;inet_pton(AF_INET,"0.0.0.0",&server.sin_addr);server.sin_port = htons(8888);//3,将套接字绑定地址,我们使用的是IPV4专属的struct sockaddr_in结构体,需要强转为专用的struct sockaddr *tmp = bind(server_fd,(struct sockaddr *)&server,sizeof(server));handle_error("bind",tmp);//4,服务端进入监听模式,随时准备接受连接tmp = listen(server_fd,128);handle_error("listen",tmp);//5,服务端接受客户端的连接,同时会获取到客户端套接字的文件描述符和ip地址等信息,通过文件描述符和客户端进行通信socklen_t client_addr_len;client_fd = accept(server_fd,(struct sockaddr *)&client,&client_addr_len);printf("客户端连接成功\n");pthread_t thread_id_write;pthread_t thread_id_read;pthread_create(&thread_id_write,NULL,thread_write,(void *)&client_fd);pthread_create(&thread_id_read,NULL,thread_read,(void *)&client_fd);pthread_join(thread_id_write,NULL);pthread_join(thread_id_read,NULL);close(server_fd);close(client_fd);return 0;
}

客户端代码

#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <unistd.h>#define handle_error(cmd,result)    \if(result < 0)              \{                           \perror(cmd);            \exit(EXIT_FAILURE);     \}                           \void * thread_write(void *argv)
{int client_fd = *((int *)argv);unsigned char * buf_sock_write = (unsigned char *)malloc(30);//客户端同样以ctrl+D为关闭信号,关闭写端while (read(STDIN_FILENO,buf_sock_write,30)){send(client_fd,buf_sock_write,30,0);memset(buf_sock_write,0,30);}printf("客户端收到关闭信号\n");//关闭写功能会向对端发送一个0字节的数据包,不同时关闭读端是为了接收对端的关闭信号//本端关闭写端,但是仍然可以接收数据包shutdown(client_fd,SHUT_WR);free(buf_sock_write);
}void * thread_read(void *argv)
{int client_fd = *((int *)argv);unsigned char * buf_sock_read = (unsigned char *)malloc(30);//对端关闭WR之后,本端的recv会接收到一个0字节的数据包,从而结束本端的读线程while (recv(client_fd,buf_sock_read,30,0)){printf("%s",buf_sock_read);memset(buf_sock_read,0,30);}printf("服务端关闭连接\n");free(buf_sock_read);
}int main(int argc, char const *argv[])
{int client_fd,tmp;struct sockaddr_in server_addr,client_addr;memset(&server_addr,0,sizeof(server_addr));memset(&client_addr,0,sizeof(client_addr));//1,创建套接字client_fd = socket(AF_INET,SOCK_STREAM,0);handle_error("socket",client_fd);//2,设置服务端的ip地址和端口号inet_pton(AF_INET,"0.0.0.0",&server_addr.sin_addr);server_addr.sin_family = AF_INET;server_addr.sin_port = htons(8888);//端口号要和服务端设置的相匹配//3,绑定套接字,客户端可以跳过本步骤,会自动分配ip地址和端口号inet_pton(AF_INET,"0.0.0.0",&client_addr.sin_addr);client_addr.sin_family = AF_INET;client_addr.sin_port = htons(6666);//要避免使用和客户端相同的端口,因为是本机通信,端口不能被多个进程同时使用tmp = bind(client_fd,(struct sockaddr *)&client_addr,sizeof(client_addr));handle_error("bind",tmp);//4,连接服务端tmp = connect(client_fd,(struct sockaddr *)&server_addr,sizeof(server_addr));handle_error("connect",tmp);printf("成功连接到服务端\n");pthread_t thread_id_write;pthread_t thread_id_read;pthread_create(&thread_id_write,NULL,thread_write,(void *)&client_fd);pthread_create(&thread_id_read,NULL,thread_read,(void *)&client_fd);pthread_join(thread_id_write,NULL);pthread_join(thread_id_read,NULL);close(client_fd);return 0;
}

过程及结果解释

        客户端和服务端代码均使用多线程,分别为读写线程,均通过终端接收数据并向对端发送数据,接收到对端的数据之后就会打印在终端上。在服务端/客户端关闭本端的写功能之后,都可以继续接收对端的数据,但是无法发送数据。

        在两个终端中分别运行服务端和客户端代码,要先运行服务端代码,不然客户端会连接不到。

         服务端发送一次hello csdn,客户端发送一次hello wrold

         关闭服务端,客户端继续发送数据

        关闭客户端 

 服务端基于多线程的支持多个客户端连接

服务端代码

#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <unistd.h>#define handle_error(cmd,result)    \if(result < 0)              \{                           \perror(cmd);            \exit(EXIT_FAILURE);     \}                           \void *thread_recv_send(void *argv)
{int client_fd; ssize_t cnt;client_fd = *((int *)argv);char *buf = (char *)malloc(100);while (cnt = recv(client_fd,buf,100,0)){handle_error("recv",cnt);cnt = send(client_fd,buf,cnt,0);handle_error("send",cnt);printf("客户端%d发来消息,内容为%s",client_fd,buf);memset(buf,0,100);}shutdown(client_fd,SHUT_WR);printf("有客户端断开连接,文件描述符为%d\n",client_fd);free(buf);close(client_fd);
}int main(int argc, char const *argv[])
{int server_fd,client_fd,tmp;struct sockaddr_in server_addr,client_addr;//创建服务端套接字server_fd = socket(AF_INET,SOCK_STREAM,0);handle_error("socket",server_fd);//设置服务端ip地址和端口号server_addr.sin_family = AF_INET;inet_pton(AF_INET,"0.0.0.0",&server_addr.sin_addr);server_addr.sin_port = htons(8888);//绑定服务端套接字tmp = bind(server_fd,(struct sockaddr *)&server_addr,sizeof(server_addr));handle_error("bind",tmp);tmp = listen(server_fd,128);handle_error("listen",tmp);while (1){socklen_t sock_len;client_fd = accept(server_fd,(struct sockaddr *)&client_addr,&sock_len);handle_error("accept",client_fd);printf("有客户端连接,ip地址为%s,端口号为%d,文件描述符为%d\n",inet_ntoa(client_addr.sin_addr),ntohs(client_addr.sin_port),client_fd);pthread_t thread_id_sock;pthread_create(&thread_id_sock,NULL,thread_recv_send,(void *)&client_fd);pthread_detach(thread_id_sock);}close(server_fd);return 0;
}

客户端代码

#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <unistd.h>#define handle_error(cmd,result)    \if(result < 0)              \{                           \perror(cmd);            \exit(EXIT_FAILURE);     \}                           \void * thread_write(void *argv)
{int client_fd = *((int *)argv);unsigned char * buf_sock_write = (unsigned char *)malloc(30);//客户端同样以ctrl+D为关闭信号,关闭写端while (read(STDIN_FILENO,buf_sock_write,30)){send(client_fd,buf_sock_write,30,0);memset(buf_sock_write,0,30);}printf("客户端收到关闭信号\n");//关闭写功能会向对端发送一个0字节的数据包,不同时关闭读端是为了接收对端的关闭信号//本端关闭写端,但是仍然可以接收数据包shutdown(client_fd,SHUT_WR);free(buf_sock_write);
}void * thread_read(void *argv)
{int client_fd = *((int *)argv);unsigned char * buf_sock_read = (unsigned char *)malloc(30);//对端关闭WR之后,本端的recv会接收到一个0字节的数据包,从而结束本端的读线程while (recv(client_fd,buf_sock_read,30,0)){printf("%s",buf_sock_read);memset(buf_sock_read,0,30);}printf("服务端关闭连接\n");free(buf_sock_read);
}int main(int argc, char const *argv[])
{int client_fd,tmp;struct sockaddr_in server_addr,client_addr;memset(&server_addr,0,sizeof(server_addr));memset(&client_addr,0,sizeof(client_addr));//1,创建套接字client_fd = socket(AF_INET,SOCK_STREAM,0);handle_error("socket",client_fd);//2,设置服务端的ip地址和端口号inet_pton(AF_INET,"0.0.0.0",&server_addr.sin_addr);server_addr.sin_family = AF_INET;server_addr.sin_port = htons(8888);//端口号要和服务端设置的相匹配//3,绑定套接字,客户端可以跳过本步骤,会自动分配ip地址和端口号,如果固定端口号的话//多个线程无法共用一个端口,导致无法创建多连接/*inet_pton(AF_INET,"0.0.0.0",&client_addr.sin_addr);client_addr.sin_family = AF_INET;client_addr.sin_port = htons(6666);//要避免使用和客户端相同的端口,因为是本机通信,端口不能被多个进程同时使用tmp = bind(client_fd,(struct sockaddr *)&client_addr,sizeof(client_addr));handle_error("bind",tmp);*///4,连接服务端tmp = connect(client_fd,(struct sockaddr *)&server_addr,sizeof(server_addr));handle_error("connect",tmp);printf("成功连接到服务端\n");pthread_t thread_id_write;pthread_t thread_id_read;pthread_create(&thread_id_write,NULL,thread_write,(void *)&client_fd);pthread_create(&thread_id_read,NULL,thread_read,(void *)&client_fd);pthread_join(thread_id_write,NULL);pthread_join(thread_id_read,NULL);close(client_fd);return 0;
}

过程及结果解释

        客户端的代码几乎没有变化,只是取消了ip地址、端口号与套接字的绑定,因为客户端会默认设置本机的ip地址,并选择一个空闲端口使用,所以无需进行绑定。相反如果固定了端口号,因为端口号不能被多个进程共用,反而会导致无法创建多连接。

        服务端的接受连接的逻辑与之前相同,在每次接受连接之后都会创建一个新线程并传入服务端的套接字,并将线程设置为detach状态,使其在结束之后自动回收资源,不使用join,从而不会阻挡创立新连接。在新线程中维护与客户端的连接并进行数据传输,将服务端接收到的消息发送回客户端并打印到终端上。当客户端从终端中接收了ctrl+D之后,客户端退出,调用shutdown会向服务端发送挥手信号,此时recv接收到的数据长度为0,服务端也随之退出。

        打开三个终端,在终端1中运行服务端,终端2,3运行客户端

         终端2,3分别发送消息

         终端2,3退出

        可以看到此时客户端已经全部退出,服务端仍然在接收连接

        终端2,3继续运行客户端,与服务端建立连接 

 服务端基于多进程的支持客户端多连接

服务端代码

#include <sys/socket.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <unistd.h>
#include <signal.h>#define handle_error(cmd,result)    \if(result < 0)              \{                           \perror(cmd);            \exit(EXIT_FAILURE);     \}                           \void singal_handle(int sig)
{while (waitpid(-1,NULL,WNOHANG)){}}void recv_send(int client_fd,struct sockaddr_in * client_addr)
{ssize_t cnt;char *buf = malloc(30);while (cnt = recv(client_fd,buf,30,0)){send(client_fd,buf,cnt,0);printf("客户端%d发来消息,内容为%s",ntohs(client_addr->sin_port),buf);memset(buf,0,30);}printf("客户端%d断开连接\n",client_fd);shutdown(client_fd,SHUT_WR);free(buf);
}int main(int argc, char const *argv[])
{int server_fd,client_fd,tmp;struct sockaddr_in server_addr,client_addr;server_fd = socket(AF_INET,SOCK_STREAM,0);handle_error("socket",server_fd);server_addr.sin_family = AF_INET;inet_pton(AF_INET,"0.0.0.0",&server_addr.sin_addr);server_addr.sin_port = htons(8888);tmp = bind(server_fd,(struct sockaddr *)&server_addr,sizeof(server_addr));handle_error("bind",tmp);tmp = listen(server_fd,128);handle_error("listen",tmp);//注册信号处理函数,调用waitpid回收子进程防止成为僵尸进程signal(SIGCHLD,singal_handle);while (1){socklen_t client_len;client_fd = accept(server_fd,(struct sockaddr*)&client_addr,&client_len);handle_error("accept",client_fd);printf("有客户端建立连接,ip地址为%s,端口号为%d,文件描述符为%d\n",inet_ntoa(client_addr.sin_addr),ntohs(client_addr.sin_port),client_fd);pid_t pid = fork();if(pid == 0)//子进程{//子进程不需要使用服务端描述符close(server_fd);recv_send(client_fd,&client_addr);close(client_fd);exit(EXIT_SUCCESS);}else if(pid > 0){//父进程不需要使用客户端描述符close(client_fd);}}return 0;
}

客户端代码

        客户端代码和多线程的相同。

运行结果及过程解释

        服务端通过fork创建多个进程来维护与客户端的连接,在子进程中执行数据传输,父进程阻塞等待客户端的连接。子进程在客户端断开之后调用exit退出进程,向父进程发送SIGCHLD信号,父进程通过注册信号处理函数调用waitpid来回收每一个子进程,防止出现僵尸进程。

        程序的运行结果和多线程的基本相同。开启三个终端,终端1执行服务端,2,3执行客户端并发送消息,之后关闭客户端

         和多线程不同的是,我们发现两个客户端的文件描述符是相同的,这是因为accept在父进程进行,数据传输的任务在子进程执行,父进程不需要使用客户端的文件描述符,所以已经将其关闭了,进而导致下一次返回的文件描述符相同。

UDP的数据传输

        UDP通讯也使用socket,但是接收和发送的函数与TCP不一样。由于UDP不存在握手这一步骤,所以在绑定地址之后,服务端不需要listen,客户端也不需要connect,服务端同样不需要accept。只要服务端绑定以后,就可以相互发消息了,由于没有握手过程,两端都不能确定对方是否收到消息,这也是UDP协议不如TCP协议可靠的地方。

        UDP的通讯流程相比之下就简单了很多,TCP是通过accept和connect绑定文件描述符进行通信,UDP则是通过目标地址的ip和端口号来通信,使用的是struct sockaddr结构体的信息。在接收对方的数据之后,就可以获得对方的ip地址和端口号等信息,进而向对方回传数据。

服务端代码

#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>#define handle_error(cmd,result)    \if(result < 0)              \{                           \perror(cmd);            \exit(EXIT_FAILURE);     \}                           \int main(int argc, char const *argv[])
{//UDP通讯过程中只涉及一个套接字就是本端套接字int sock_fd,tmp;char buf[30];struct sockaddr_in server_addr,client_addr;socklen_t client_len = sizeof(client_addr);sock_fd = socket(AF_INET,SOCK_DGRAM,0);handle_error("sock",sock_fd);server_addr.sin_family = AF_INET;inet_pton(AF_INET,"0.0.0.0",&server_addr.sin_addr);server_addr.sin_port = htons(8888);tmp = bind(sock_fd,(struct sockaddr *)&server_addr,sizeof(server_addr));handle_error("bind",tmp);ssize_t cnt = 0;while (cnt = recvfrom(sock_fd,buf,30,0,(struct sockaddr *)&client_addr,&client_len)){printf("%s",buf);sendto(sock_fd,buf,30,0,(struct sockaddr *)&client_addr,client_len);memset(buf,0,30);}printf("客户端退出\n");return 0;
}

客户端代码

#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>#define handle_error(cmd,result)    \if(result < 0)              \{                           \perror(cmd);            \exit(EXIT_FAILURE);     \}                           \int main(int argc, char const *argv[])
{int sock_fd,tmp;sock_fd = socket(AF_INET,SOCK_DGRAM,0);handle_error("socket",sock_fd);struct sockaddr_in server_addr;socklen_t server_len;server_addr.sin_family = AF_INET;server_addr.sin_port = htons(8888);inet_pton(AF_INET,"0.0.0.0",&server_addr.sin_addr);char buf[30];while (read(STDIN_FILENO,buf,30)){sendto(sock_fd,buf,30,0,(struct sockaddr *)&server_addr,sizeof(server_addr));memset(buf,0,30);recvfrom(sock_fd,buf,30,0,(struct sockaddr *)&server_addr,&server_len);printf("%s",buf);memset(buf,0,30);}sendto(sock_fd,"",0,0,(struct sockaddr *)&server_addr,sizeof(server_addr));return 0;
}

        客户端必须先向服务端发送消息,之后服务端才可以获取到客户端的ip地址和端口号。运行结果如下

套接字用于进程间通信

        Socket编程原本是为了网络服务的,后来逐渐发展成一种进程间通信的方式:Unix Domain Socket IPC。它允许在同一台主机上运行的进程之间进行高效的数据传输,无需经过网络协议栈,因此具有低延迟和高性能的特点。通过文件系统中的特殊文件(通常是一个套接字文件),进程可以通过套接字(socket)来进行通信,实现双向的数据传输。

        和网络通信不同的是,UNIX通信域使用的结构体是struct sockaddr_un,结构体的sun_family固定使用AF_UNIX,第二个成员变为了套接字的地址,也就是声明一个使用本地文件的socket,其他进程可以绑定该文件完成进程间通信。在使用完成之后,服务端需要调用unlink清除该套接字文件,否则下次进行绑定时会提示该地址已经被使用。除此之外,服务端和客户端的程序编写流程和TCP并无区别。

        服务端代码

#include <stdlib.h>
#include <stdio.h>
#include <stddef.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>#define handle_error(cmd,result)    \if(result < 0)              \{                           \perror(cmd);            \exit(EXIT_FAILURE);     \}                           \void *thread_recv_send(void *argv)
{int client_fd; ssize_t cnt;client_fd = *((int *)argv);char *buf = (char *)malloc(100);while (cnt = recv(client_fd,buf,100,0)){handle_error("recv",cnt);cnt = send(client_fd,buf,cnt,0);handle_error("send",cnt);printf("客户端%d发来消息,内容为%s",client_fd,buf);memset(buf,0,100);}shutdown(client_fd,SHUT_WR);printf("有客户端断开连接,文件描述符为%d\n",client_fd);free(buf);close(client_fd);
}int main(int argc, char const *argv[])
{int server_fd,client_fd,tmp;struct sockaddr_un server_addr,client_addr;//创建套接字server_fd = socket(AF_UNIX,SOCK_STREAM,0);handle_error("socket",server_fd);//设置地址server_addr.sun_family = AF_UNIX;strcpy(server_addr.sun_path,"test.socket");//绑定套接字tmp = bind(server_fd,(struct sockaddr*)&server_addr,sizeof(server_addr));handle_error("bind",tmp);tmp = listen(server_fd,128);handle_error("listen",tmp);while (1){socklen_t client_len = sizeof(client_addr);client_fd = accept(server_fd,(struct sockaddr*)&client_addr,&client_len);handle_error("accept",client_fd);printf("有客户端连接,文件描述符为%d\n",client_fd);pthread_t thread_id_sock;pthread_create(&thread_id_sock,NULL,thread_recv_send,(void *)&client_fd);pthread_detach(thread_id_sock);}unlink("test.socket");return 0;
}

客户端代码

#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/un.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>#define handle_error(cmd,result)    \if(result < 0)              \{                           \perror(cmd);            \exit(EXIT_FAILURE);     \}                           \void * thread_write(void *argv)
{int client_fd = *((int *)argv);unsigned char * buf_sock_write = (unsigned char *)malloc(30);//客户端同样以ctrl+D为关闭信号,关闭写端while (read(STDIN_FILENO,buf_sock_write,30)){send(client_fd,buf_sock_write,30,0);memset(buf_sock_write,0,30);}printf("客户端收到关闭信号\n");//关闭写功能会向对端发送一个0字节的数据包,不同时关闭读端是为了接收对端的关闭信号//本端关闭写端,但是仍然可以接收数据包shutdown(client_fd,SHUT_WR);free(buf_sock_write);
}void * thread_read(void *argv)
{int client_fd = *((int *)argv);unsigned char * buf_sock_read = (unsigned char *)malloc(30);//对端关闭WR之后,本端的recv会接收到一个0字节的数据包,从而结束本端的读线程while (recv(client_fd,buf_sock_read,30,0)){printf("%s",buf_sock_read);memset(buf_sock_read,0,30);}printf("服务端关闭连接\n");free(buf_sock_read);
}int main(int argc, char const *argv[])
{int client_fd,tmp;struct sockaddr_un server_addr,client_addr;memset(&server_addr,0,sizeof(server_addr));memset(&client_addr,0,sizeof(client_addr));//创建套接字client_fd = socket(AF_UNIX,SOCK_STREAM,0);handle_error("socket",client_fd);//设置服务端的地址server_addr.sun_family = AF_UNIX;strcpy(server_addr.sun_path,"test.socket");//连接服务端tmp = connect(client_fd,(struct sockaddr *)&server_addr,sizeof(server_addr));handle_error("connect",tmp);printf("成功连接到服务端\n");pthread_t thread_id_write;pthread_t thread_id_read;pthread_create(&thread_id_write,NULL,thread_write,(void *)&client_fd);pthread_create(&thread_id_read,NULL,thread_read,(void *)&client_fd);pthread_join(thread_id_write,NULL);pthread_join(thread_id_read,NULL);close(client_fd);return 0;
}

过程解释及运行结果

        服务端仍然采用多线程方式支持多个连接,接收客户端数据之后打印在终端并传回客户端。

         总结一下,套接字用于进程间通信时,类似于TCP协议下的编程顺序,但是套接字地址结构体变为了struct sockaddr_un 其中的family写为AF_UNIX,第二个成员改为了路径,客户端服务端的路径要一致。创建套接字的第二个参数还是sock_stream和TCP一样。服务端的编程顺序是初始化地址,给地址赋值,创建套接字,绑定套接字,进入监听模式,接受连接(会返回客户端的套接字和地址),发送/接收数据,unlink释放套接字。客户端的编程顺序是初始化地址,给地址赋值,创建套接字,连接客户端,发送/接收数据。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/news/908849.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python SQLModel 简介

销量过万TEEIS德国护膝夏天用薄款 优惠券冠生园 百花蜂蜜428g 挤压瓶纯蜂蜜巨奇严选 鞋子除臭剂360ml 多芬身体磨砂膏280g健70%-75%酒精消毒棉片湿巾1418cm 80片/袋3袋大包清洁食品用消毒 优惠券AIMORNY52朵红玫瑰永生香皂花同城配送非鲜花七夕情人节生日礼物送女友 热卖妙洁棉…

【Post-process】【VBA】ETABS VBA FrameObj.GetNameList and write to EXCEL

ETABS API实战:导出框架元素数据到Excel 在结构工程师的日常工作中,经常需要从ETABS模型中提取框架元素信息进行后续分析。手动复制粘贴不仅耗时,还容易出错。今天我们来用简单的VBA代码实现自动化导出。 🎯 我们要实现什么? 一键点击,就能将ETABS中所有框架元素的基…

springboot根据部署服务器生成机器码+加密生成到期时间授权码设置项目在服务器的到期时间

生成机器码 首先需要在后端写个获取window或linux的机器码&#xff0c;根据CPU序列号和硬盘序列号(Windows)&#xff0c;拼接得到 /*** 操作系统的工具类*/ public class OSUtils {/*** 获取window or linux机器码** return*/public static String getOSNumber() {Map<Str…

Thumb-2指令集及其与STM32的关系

Thumb-2指令集及其与STM32的关系&#xff1a; 1. Thumb-2指令集是什么&#xff1f; 本质&#xff1a;Thumb-2是ARM公司设计的混合指令集架构&#xff0c;首次在ARMv7架构中引入&#xff08;如Cortex-M3/M4/M7&#xff09;。 核心创新&#xff1a; 融合了传统 32位ARM指令&…

Haption 力反馈遥操作机器人:6 自由度 + 低延迟响应,解锁精准远程操控体验

Haption自2001年成立以来&#xff0c;始终专注于力反馈设备与定制化解决方案的设计、研发及销售。作为工业级力反馈技术的先行者&#xff0c;其核心产品以高精度交互与可靠性著称&#xff0c;已与达索系统、空客、Orano 等行业头部企业达成深度合作&#xff0c;业务覆盖工程仿真…

C# ExcelWorksheet 贴图

C# ExcelWorksheet 贴图 在C#中,如果你想在Excel工作表中插入图片(例如,在ExcelWorksheet中贴图),你可以使用ClosedXML或EPPlus这样的库来操作Excel文件。下面我将分别介绍如何使用这两个库来实现这一功能。 使用ClosedXML 首先,确保你已经安装了ClosedXML包。你可以通…

Android15默认授权浮窗权限

我们经常有那种需求&#xff0c;客户需要定制的apk集成在ROM中&#xff0c;并且默认授予其【显示在其他应用的上层】权限&#xff0c;也就是我们常说的浮窗权限&#xff0c;那么我们就可以通过以下方法在wms、ams等系统服务的systemReady()方法中调用即可实现预置应用默认授权浮…

莫兰迪高级灰总结计划简约商务通用PPT模版

莫兰迪高级灰总结计划简约商务通用PPT模版&#xff0c;莫兰迪调色板清新简约工作汇报PPT模版&#xff0c;莫兰迪时尚风极简设计PPT模版&#xff0c;大学生毕业论文答辩PPT模版&#xff0c;莫兰迪配色总结计划简约商务通用PPT模版&#xff0c;莫兰迪商务汇报PPT模版&#xff0c;…

无人机EN 18031欧盟网络安全认证详细解读

EN 18031 是欧盟针对无线电设备发布的网络安全标准&#xff0c;于 2024 年 8 月正式发布&#xff0c;2025 年 1 月 30 日被列入《无线电设备指令》&#xff08;RED&#xff09;协调标准清单&#xff0c;并于 2025 年 8 月 1 日起强制执行。以下是对无人机 EN 18031 欧盟网络安全…

Linux离线(zip方式)安装docker

目录 基础信息操作系统信息docker信息 安装实例安装步骤示例 遇到的问题问题1&#xff1a;修改默认工作路径启动失败问题2 找不到对应组 基础信息 操作系统信息 OS版本&#xff1a;CentOS 7 64位 内核版本&#xff1a;3.10.0 相关命令&#xff1a; uname -rcat /etc/os-rele…

Go 语言中switch case条件分支语句

1. 基本语法 package main import "fmt" func main() {var extname ".css"switch extname {case ".html":fmt.Println("text/html")case ".css":fmt.Println("text/css") // text/csscase ".js":fmt.…

FFmpeg:Windows系统小白安装及其使用

一、安装 1.访问官网 Download FFmpeg 2.点击版本目录 3.选择版本点击安装 注意这里选择的是【release buids】&#xff0c;注意左上角标题 例如我安装在目录 F:\FFmpeg 4.解压 5.添加环境变量 把你解压后的bin目录&#xff08;即exe所在文件夹&#xff09;加入系统变量…

LLM基础2_语言模型如何文本编码

基于GitHub项目&#xff1a;https://github.com/datawhalechina/llms-from-scratch-cn 字节对编码(BPE) 上一篇博文说到 为什么GPT模型不需要[PAD]和[UNK]&#xff1f; GPT使用更先进的字节对编码(BPE)&#xff0c;总能将词语拆分成已知子词 为什么需要BPE&#xff1f; 简…

监控升级:可视化如何让每一个细节 “说话”

你有没有遇到过这样的情况&#xff1f; 监控画面里明明有“异常”&#xff0c;但值班人员愣是没发现&#xff1b; 报警响起却不知道具体发生了什么&#xff0c;只能靠猜、靠翻录像&#xff1b; 出了事回看录像&#xff0c;才发现线索早就在眼前&#xff0c;只是没人注意到………

单片机bootloader(APP的自我复制)

文章目录 Bootloader 中 APP 的自我复制与启动机制解析一、为什么要进行自我复制?二、程序整体结构概述三、汇编启动代码分析重点解释:四、C 语言部分分析核心功能:五、start\_app 函数:手动启动指定 APP六、总结七、适用场景Bootloader 中 APP 的自我复制与启动机制解析 …

浏览器工作原理11 [#] this:从JavaScript执行上下文视角讲this

引用 《浏览器工作原理与实践》 在上篇文章中&#xff0c;我们讲了词法作用域、作用域链以及闭包&#xff0c;并在最后思考题中留了下面这样一段代码 var bar {myName:"time.geekbang.com",printName: function () {console.log(myName)} } function foo() {le…

【C语言】-递归

1、递归概念 递归&#xff08;Recursion&#xff09;是编程中一种重要的解决问题的方法&#xff0c;其核心思想是函数通过调用自身来解决规模更小的子问题&#xff0c;直到达到最小的、可以直接解决的基准情形&#xff08;Base Case&#xff09;。 核心&#xff1a;自己调用…

12.5Swing控件3Jpanel JOptionPane

JPanel JPanel是一个轻量级容器组件&#xff0c;用于组织和管理其他 GUI 组件。它继承自JComponent类&#xff0c;属于javax.swing包&#xff0c;可以容纳按钮、文本框、标签等控件 JPanel 默认使用的布局管理器是 FlowLayout&#xff0c;也可以嵌套其他面板&#xff0c;以便…

MIPI信号为什么不能进行长距离传输

1.关于MIPI信号传输 MIPI信号是不适合长距离传输的。 2.MIPI的信号摆幅小&#xff0c;抗干扰能力比较弱 MIPI信号的差分摆幅比较小&#xff0c;通常只有100mV~200mV,远远低于LVDS的350mV的摆幅 小摆幅信号在长线缆上传输的时候更容易被噪声淹没&#xff0c;信噪比下降&#xf…

Qt的学习(二)

1. 创建Hello Word 两种方式&#xff0c;实现helloworld&#xff1a; 1.通过图形化的方式&#xff0c;在界面上创建出一个控件&#xff0c;显示helloworld 2.通过纯代码的方式&#xff0c;通过编写代码&#xff0c;在界面上创建控件&#xff0c; 显示hello world&#xff1b; …