当前位置: 首页 > article >正文

网络编程学习笔记——TCP网络编程

文章目录

    • 1、socket()函数
    • 2、bind()函数
    • 3、listen()
    • 4、accept()
    • 5、connect()
    • 6、send()/write()
    • 7、recv()/read()
    • 8、套接字的关闭
    • 9、TCP循环服务器模型
    • 10、TCP多线程服务器
    • 11、TCP多进程并发服务器

网络编程常用函数

  • socket() 创建套接字
  • bind() 绑定本机地址和端口
  • connect() 建立连接
  • listen() 设置监听端口
  • accept() 接受TCP连接
  • recv(), read(), recvfrom() 数据接收
  • send(), write(), sendto() 数据发送
  • close(), shutdown() 关闭套接字

在这里插入图片描述

1、socket()函数

int socket (int domain, int type, int protocol);

  • domain 是地址族
    • PF_INET IPv4 Internet protocols ip(7) // internet 协议
    • PF_UNIX IPv6 Internet protocols ipv6(7) // unix internal协议
    • PF_NS Local communication unix(7) // Xerox NS协议
    • PF_IMPLINK Low level packet interface packet(7)// Interface Message协议
  • type // 套接字类型
    • SOCK_STREAM // 流式套接字
    • SOCK_DGRAM // 数据报套接字
    • SOCK_RAW //原始套接字
  • protocol 参数通常置为0

地址相关的数据结构

  • 通用地址结构

    • struct sockaddr
      {    u_short  sa_family;    // 地址族, AF_xxxchar  sa_data[14];     // 14字节协议地址
      };
      
  • Internet协议地址结构

    • struct sockaddr_in
      {           u_short sin_family;		// 地址族, AF_INET,2 bytesu_short sin_port;      	// 端口,2 bytesstruct in_addr sin_addr;	// IPV4地址,4 bytes 	char sin_zero[8];			// 8 bytes unused,作为填充
      }; 
      
  • IPv4地址结构

    • // internet address  
      struct in_addr
      {in_addr_t  s_addr;            // u32 network address 
      };
      

返回值

  • RETURN VALUEOn  success,  a  file  descriptor  for  the new socket is returned.  Onerror, -1 is returned, and errno is set appropriately.
    

成功时返回文件描述符,出错时返回为-1

2、bind()函数

int bind (int sockfd, struct sockaddr* addr, int addrLen);

  • sockfd 由socket() 调用返回

  • addr 是指向 sockaddr_in 结构的指针,包含本机IP 地址和端口号(struct sockaddr的结构体变量的地址)

    • struct sockaddr_in

    • u_short sin_family // protocol family 
      u_short sin_port     // port number 
      struct in_addr  sin_addr  //IP address (32-bits)4字节
      
  • addrLen : sizeof (struct sockaddr_in)(地址长度)

地址结构的一般用法:

  • 定义一个struct sockaddr_in类型的变量并清空

    • struct sockaddr_in myaddr;
      memset(&myaddr, 0, sizeof(myaddr));
      
  • 填充地址信息

    • myaddr.sin_family = PF_INET;
      myaddr.sin_port = htons(8888); 
      myaddr.sin_addr.s_addr = inet_addr(“192.168.1.100”);
      
  • 将该变量强制转换为struct sockaddr类型在函数中使用

    • bind(listenfd, (struct sockaddr*)(&myaddr), sizeof(myaddr));
      

地址转换函数:

  • unsigned long inet_addr(char *address);

    • address是以’\0’结尾的点分IPv4字符串。该函数返回32位的地址。如果字符串包含的不是合法的IP地址,则函数返回-1。例如:

      struct in_addr addr;
      addr.s_addr = inet_addr(" 192.168.1.100 ");
      
  • char* inet_ntoa(struct in_addr address);

    • address是IPv4地址结构,函数返回一指向包含点分IP地址的静态存储区字符指针。如果错误则函数返回NULL

代码举例:

#include "net.h"int main(void)
{int fd = -1;struct sockaddr_in sin;/* 1. 创建socket fd*/if( (fd = socket(AF_INET,SOCK_STREAM, 0)) < 0) {perror("socket");exit(1);}/*2. 绑定 *//*2.1 填充struct sockaddr_in结构体变量 */bzero(&sin,sizeof(sin));sin.sin_family = AF_INET;sin.sin_port = htons(SERV_PORT); //网络字节序的端口号
#if 0sin.sin_addr.s_addr = inet_addr(SERV_IP_ADDR); 
#elseif( inet_pton(AF_INET, SERV_IP_ADDR, (void *)&sin.sin_addr) != 1) {perror("inet_pton");exit(1);}
#endif	/*2.2 绑定 */	if( bind(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0 ) {perror("bind");exit(1);}
}

如果是IPV6的编程,要使用struct sockddr_in6结构体(详细情况请参考man 7 ipv6),通常更通用的方法可以通过struct sockaddr_storage来编程

3、listen()

int listen (int sockfd, int backlog);把主动套接字变成被动套接字

  • sockfd:监听连接的套接字(通过socket()函数拿到的fd)
  • backlog同时允许几路客户端和服务器进行正在连接的过程(正在三次握手)
    一般填5,测试得知,ARM最大为8
    • 指定了正在等待连接的最大队列长度,它的作用在于处理可能同时出现的几个连接请求。
    • DoS(拒绝服务)攻击即利用了这个原理,非法的连接占用了全部的连接数,造成正常的连接请求被拒绝。
  • 返回值: 0 或 -1
  • 完成listen()调用后,socket变成了监听socket(listening socket)

内核中服务器的套接字fd会维护2个链表:

  • 正在三次握手的的客户端链表(数量=2*backlog+1
  • 已经建立好连接的客户端链表(已经完成3次握手分配好了newfd)
  • 比如:listen(fd, 5); //表示系统允许11(=2*5+1)个客户端同时进行三次握手

返回值:

  • RETURN VALUEOn success, zero is returned. On error, -1 is returned, and errno is set appropriately.
    

4、accept()

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) ;(阻塞等待客户端连接请求)

  • 返回值:已建立好连接的套接字或-1

  • 头文件

    • #include <sys/types.h>
      #include <sys/socket.h> 
      
  • sockfd : 监听套接字(经过前面socket()创建并通过bind(),listen()设置过的fd)

  • addr : 对方地址(获取连接过来的客户的信息)

  • addrlen:地址长度(获取连接过来的客户的信息)

listen()accept()是TCP服务器端使用的函数

返回值

  •  RETURN VALUEOn success, these system calls return a nonnegative integer that is a descriptor for the accepted socket. On  error, -1 is returned, and errno is set appropriately.
    
  • 成功时返回已经建立好连接的新的newfd


代码举例:

创建文件net.h

#ifndef __MAKEU_NET_H__
#define __MAKEU_NET_H__#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/types.h>			/* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>			/* superset of previous */#define SERV_PORT 5001
#define SERV_IP_ADDR "192.168.7.246"
#define BACKLOG 5#define QUIT_STR "quit"#endif

创建服务器文件server.c

#include "net.h"int main (void)
{int fd = -1;struct sockaddr_in sin;/* 1. 创建socket fd */if ((fd = socket (AF_INET, SOCK_STREAM, 0)) < 0) {perror ("socket");exit (1);}/*2. 绑定 *//*2.1 填充struct sockaddr_in结构体变量 */bzero (&sin, sizeof (sin));sin.sin_family = AF_INET;sin.sin_port = htons (SERV_PORT);	//网络字节序的端口号/*优化1: 让服务器程序能绑定在任意的IP上 */
#if 1sin.sin_addr.s_addr = htonl (INADDY_ANY);
#elseif (inet_pton (AF_INET, SERV_IP_ADDR, (void *) &sin.sin_addr) != 1) {perror ("inet_pton");exit (1);}
#endif/*2.2 绑定 */if (bind (fd, (struct sockaddr *) &sin, sizeof (sin)) < 0) {perror ("bind");exit (1);}/*3. 调用listen()把主动套接字变成被动套接字 */if (listen (fd, BACKLOG) < 0) {perror ("listen");exit (1);}printf ("Server starting....OK!\n");int newfd = -1;/*4. 阻塞等待客户端连接请求 */
#if 0newfd = accept (fd, NULL, NULL);if (newfd < 0) {perror ("accept");exit (1);}
#else/*优化2:通过程序获取刚建立连接的socket的客户端的IP地址和端口号 */struct sockaddr_in cin;socklen_t addrlen = sizeof (cin);if ((newfd = accept (fd, (struct sockaddr *) &cin, &addrlen)) < 0) {perror ("accept");exit (1);}char ipv4_addr[16];if (!inet_ntop (AF_INET, (void *) &cin.sin_addr, ipv4_addr, sizeof (cin))) {perror ("inet_ntop");exit (1);}printf ("Clinet(%s:%d) is connected!\n", ipv4_addr, ntons (cin.sin_port));#endif/*5. 读写 *///..和newfd进行数据读写int ret = -1;char buf[BUFSIZ];while (1) {bzero (buf, BUFSIZ);do {ret = read (newfd, buf, BUFSIZ - 1);} while (ret < 0 && EINTR == errno);if (ret < 0) {perror ("read");exit (1);}if (!ret) {				//对方已经关闭break;}printf ("Receive data: %s\n", buf);if (!strncasecmp (buf, QUIT_STR, strlen (QUIT_STR))) {	//用户输入了quit字符printf ("Client is exiting!\n");break;}}close (newfd);close (fd);return 0;
}

5、connect()

int connect(int sockfd, struct sockaddr *serv_addr, int addrlen);(客户端的连接函数)

  • 返回值:0 或 -1

  • 头文件:

    • #include <sys/types.h>
      #include <sys/socket.h> 
      
  • sockfd : socket返回的文件描述符(通过socket()函数拿到的fd)

  • serv_addr : 服务器端的地址信息(struct sockaddr的结构体变量的地址)

  • addrlen : serv_addr的长度(地址长度)

connect()是客户端使用的系统调用。

返回值:

  • RETURN VALUEIf the connection or binding succeeds, zero is returned.  On error, -1 is returned, and errno is set appropriately.
    

代码举例:创建文件client1.c

#include "net.h"int main (void)
{int fd = -1;struct sockaddr_in sin;/* 1. 创建socket fd */if ((fd = socket (AF_INET, SOCK_STREAM, 0)) < 0) {perror ("socket");exit (1);}/*2.连接服务器 *//*2.1 填充struct sockaddr_in结构体变量 */bzero (&sin, sizeof (sin));sin.sin_family = AF_INET;sin.sin_port = htons (SERV_PORT);	//网络字节序的端口号
#if 0sin.sin_addr.s_addr = inet_addr (SERV_IP_ADDR);
#elseif (inet_pton (AF_INET, SERV_IP_ADDR, (void *) &sin.sin_addr) != 1) {perror ("inet_pton");exit (1);}
#endifif (connect (fd, (struct sockaddr *) &sin, sizeof (sin)) < 0) {perror ("connect");exit (1);}printf ("Client staring...OK!\n");/*3. 读写数据 */char buf[BUFSIZ];int ret = -1;while (1) {bzero (buf, BUFSIZ);if (fgets (buf, BUFSIZ - 1, stdin) == NULL) {continue;}do {ret = write (fd, buf, strlen (buf));} while (ret < 0 && EINTR == errno);if (!strncasecmp (buf, QUIT_STR, strlen (QUIT_STR))) {	//用户输入了quit字符printf ("Client is exiting!\n");break;}}/*4.关闭套接字 */close (fd);
}

6、send()/write()

ssize_t send(int socket, const void *buffer, size_t length, int flags);

  • 返回值:
    • 成功:实际发送的字节数
    • 失败:-1, 并设置errno
  • 头文件:
    • #include <sys/socket.h>
  • buffer : 发送缓冲区首地址
  • length : 发送的字节数
  • flags : 发送方式(通常为0)

ssize_t write(int fd, const void *buf, size_t count);

  • send()比write多一个参数flags:
    • flags:
      • 一般填写0,此时与write()作用一样
      • MSG_DONTWAIT:Enables nonblocking operation(非阻塞标志)
      • MSG_OOB:用于发送TCP类型的带外数据(out-of-band)

read()和write()经常会代替recv()和send(),通常情况下,看程序员的偏好

使用read()/write()和recv()/send()时最好统一

7、recv()/read()

ssize_t recv(int socket, const void *buffer, size_t length, int flags);

  • 返回值:
    • 成功:实际接收的字节数
    • 失败:-1, 并设置errno
  • 头文件:
    • #include <sys/socket.h>
  • buffer : 发送缓冲区首地址
  • length : 发送的字节数
  • flags : 接收方式(通常为0)

ssize_t read(int fd, voide *buf, size_t count);

flags:

  • 一般填写0,此时和read()作用相同

特殊标志:

  • MSG_DONTWAIT:Enables nonblocking operation(非阻塞标志)
  • MSG_OOB:用于发送TCP类型的带外数据(out-of-band)
  • MSG_PEEK:

8、套接字的关闭

int close(int sockfd);

  • 关闭双向通讯

int shutdown(int sockfd, int howto);

  • TCP连接是双向的(是可读写的),当我们使用close时,会把读写通道都关闭,有时侯我们希望只关闭一个方向,这个时候我们可以使用shutdown。
  • 针对不同的howto,系统回采取不同的关闭方式。

shutdown()的howto参数

  • howto = 0
    • 关闭读通道,但是可以继续往套接字写数据。
  • howto = 1
    • 和上面相反,关闭写通道。只能从套接字读取数据。
  • howto = 2
    • 关闭读写通道,和close()一样

9、TCP循环服务器模型

socket(...);
bind(...);
listen(...);
while(1)
{accept(...);process(...);close(...);
}

TCP服务器一般很少用

10、TCP多线程服务器

socket(...);
bind(...);
listen(...);
while(1)
{accpet(...);if((pthread_create(...))!==-1)   {process(...);close(...);exit(...);   }   close(...);
}

多线程服务器是对多进程的服务器的改进

代码举例:net.h

#ifndef __MAKEU_NET_H__
#define __MAKEU_NET_H__#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/types.h>			/* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>			/* superset of previous */#define SERV_PORT 5001
#define SERV_IP_ADDR "192.168.7.246"
#define BACKLOG 5#define QUIT_STR "quit"#endif

server.c

#include <pthread.h>
#include "net.h"void cli_data_handle (void *arg);int main (void)
{int fd = -1;struct sockaddr_in sin;/* 1. 创建socket fd */if ((fd = socket (AF_INET, SOCK_STREAM, 0)) < 0) {perror ("socket");exit (1);}/*优化4: 允许绑定地址快速重用 */int b_reuse = 1;setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &b_reuse, sizeof (int));/*2. 绑定 *//*2.1 填充struct sockaddr_in结构体变量 */bzero (&sin, sizeof (sin));sin.sin_family = AF_INET;sin.sin_port = htons (SERV_PORT);	//网络字节序的端口号/*优化1: 让服务器程序能绑定在任意的IP上 */
#if 1sin.sin_addr.s_addr = htonl (INADDR_ANY);
#elseif (inet_pton (AF_INET, SERV_IP_ADDR, (void *) &sin.sin_addr) != 1) {perror ("inet_pton");exit (1);}
#endif/*2.2 绑定 */if (bind (fd, (struct sockaddr *) &sin, sizeof (sin)) < 0) {perror ("bind");exit (1);}/*3. 调用listen()把主动套接字变成被动套接字 */if (listen (fd, BACKLOG) < 0) {perror ("listen");exit (1);}printf ("Server starting....OK!\n");int newfd = -1;/*4. 阻塞等待客户端连接请求 *//* 优化: 用多进程/多线程处理已经建立号连接的客户端数据 */pthread_t tid;struct sockaddr_in cin;socklen_t addrlen = sizeof (cin);while (1) {if ((newfd = accept (fd, (struct sockaddr *) &cin, &addrlen)) < 0) {perror ("accept");exit (1);}char ipv4_addr[16];if (!inet_ntop (AF_INET, (void *) &cin.sin_addr, ipv4_addr, sizeof (cin))) {perror ("inet_ntop");exit (1);}printf ("Clinet(%s:%d) is connected!\n", ipv4_addr, htons (cin.sin_port));pthread_create (&tid, NULL, (void *) cli_data_handle, (void *) &newfd);}close (fd);return 0;
}void cli_data_handle (void *arg)
{int newfd = *(int *) arg;printf ("handler thread: newfd =%d\n", newfd);//..和newfd进行数据读写int ret = -1;char buf[BUFSIZ];while (1) {bzero (buf, BUFSIZ);do {ret = read (newfd, buf, BUFSIZ - 1);} while (ret < 0 && EINTR == errno);if (ret < 0) {perror ("read");exit (1);}if (!ret) {				//对方已经关闭break;}printf ("Receive data: %s\n", buf);if (!strncasecmp (buf, QUIT_STR, strlen (QUIT_STR))) {	//用户输入了quit字符printf ("Client(fd=%d) is exiting!\n", newfd);break;}}close (newfd);}

client.c


/*./client serv_ip serv_port */
#include "net.h"void usage (char *s)
{printf ("\n%s serv_ip serv_port", s);printf ("\n\t serv_ip: server ip address");printf ("\n\t serv_port: server port(>5000)\n\n");
}int main (int argc, char **argv)
{int fd = -1;int port = -1;struct sockaddr_in sin;if (argc != 3) {usage (argv[0]);exit (1);}/* 1. 创建socket fd */if ((fd = socket (AF_INET, SOCK_STREAM, 0)) < 0) {perror ("socket");exit (1);}port = atoi (argv[2]);if (port < 5000) {usage (argv[0]);exit (1);}/*2.连接服务器 *//*2.1 填充struct sockaddr_in结构体变量 */bzero (&sin, sizeof (sin));sin.sin_family = AF_INET;sin.sin_port = htons (port);	//网络字节序的端口号
#if 0sin.sin_addr.s_addr = inet_addr (SERV_IP_ADDR);
#elseif (inet_pton (AF_INET, argv[1], (void *) &sin.sin_addr) != 1) {perror ("inet_pton");exit (1);}
#endifif (connect (fd, (struct sockaddr *) &sin, sizeof (sin)) < 0) {perror ("connect");exit (1);}printf ("Client staring...OK!\n");/*3. 读写数据 */char buf[BUFSIZ];int ret = -1;while (1) {bzero (buf, BUFSIZ);if (fgets (buf, BUFSIZ - 1, stdin) == NULL) {continue;}do {ret = write (fd, buf, strlen (buf));} while (ret < 0 && EINTR == errno);if (!strncasecmp (buf, QUIT_STR, strlen (QUIT_STR))) {	//用户输入了quit字符printf ("Client is exiting!\n");break;}}/*4.关闭套接字 */close (fd);
}

11、TCP多进程并发服务器

socket(...);
bind(...);
listen(...);
while(1){accpet(...);if(fork(...) == 0){process(...);close(...);exit(...);}close(...);
}

TCP并发服务器的思想是每一个客户机的请求并不由服务器直接处理,而是由服务器创建一个子进程来处理。

代码举例:创建文件net.h

#ifndef __MAKEU_NET_H__
#define __MAKEU_NET_H__#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/types.h>			/* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>			/* superset of previous */#define SERV_PORT 5001
#define SERV_IP_ADDR "192.168.7.246"
#define BACKLOG 5#define QUIT_STR "quit"#endif

server.c

#include <pthread.h>
#include <signal.h>
#include "net.h"void cli_data_handle (void *arg);void sig_child_handle(int signo)
{if(SIGCHLD == signo) {waitpid(-1, NULL,  WNOHANG);}
}
int main (void)
{int fd = -1;struct sockaddr_in sin;signal(SIGCHLD, sig_child_handle);	/* 1. 创建socket fd */if ((fd = socket (AF_INET, SOCK_STREAM, 0)) < 0) {perror ("socket");exit (1);}/*优化4: 允许绑定地址快速重用 */int b_reuse = 1;setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &b_reuse, sizeof (int));/*2. 绑定 *//*2.1 填充struct sockaddr_in结构体变量 */bzero (&sin, sizeof (sin));sin.sin_family = AF_INET;sin.sin_port = htons (SERV_PORT);	//网络字节序的端口号/*优化1: 让服务器程序能绑定在任意的IP上 */
#if 1sin.sin_addr.s_addr = htonl (INADDR_ANY);
#elseif (inet_pton (AF_INET, SERV_IP_ADDR, (void *) &sin.sin_addr) != 1) {perror ("inet_pton");exit (1);}
#endif/*2.2 绑定 */if (bind (fd, (struct sockaddr *) &sin, sizeof (sin)) < 0) {perror ("bind");exit (1);}/*3. 调用listen()把主动套接字变成被动套接字 */if (listen (fd, BACKLOG) < 0) {perror ("listen");exit (1);}printf ("Server starting....OK!\n");int newfd = -1;/*4. 阻塞等待客户端连接请求 */struct sockaddr_in cin;socklen_t addrlen = sizeof (cin);while(1) {pid_t pid = -1;if ((newfd = accept (fd, (struct sockaddr *) &cin, &addrlen)) < 0) {perror ("accept");break;}/*创建一个子进程用于处理已建立连接的客户的交互数据*/if((pid = fork()) < 0) {perror("fork");break;}if(0 == pid) {  //子进程中close(fd);char ipv4_addr[16];if (!inet_ntop (AF_INET, (void *) &cin.sin_addr, ipv4_addr, sizeof (cin))) {perror ("inet_ntop");exit (1);}printf ("Clinet(%s:%d) is connected!\n", ipv4_addr, ntohs(cin.sin_port));	cli_data_handle(&newfd);		return 0;	} else { //实际上此处 pid >0, 父进程中 close(newfd);}}		close (fd);return 0;
}void cli_data_handle (void *arg)
{int newfd = *(int *) arg;printf ("Child handling process: newfd =%d\n", newfd);//..和newfd进行数据读写int ret = -1;char buf[BUFSIZ];while (1) {bzero (buf, BUFSIZ);do {ret = read (newfd, buf, BUFSIZ - 1);} while (ret < 0 && EINTR == errno);if (ret < 0) {perror ("read");exit (1);}if (!ret) {				//对方已经关闭break;}printf ("Receive data: %s\n", buf);if (!strncasecmp (buf, QUIT_STR, strlen (QUIT_STR))) {	//用户输入了quit字符printf ("Client(fd=%d) is exiting!\n", newfd);break;}}close (newfd);}

client.c


/*./client serv_ip serv_port */
#include "net.h"void usage (char *s)
{printf ("\n%s serv_ip serv_port", s);printf ("\n\t serv_ip: server ip address");printf ("\n\t serv_port: server port(>5000)\n\n");
}int main (int argc, char **argv)
{int fd = -1;int port = -1;struct sockaddr_in sin;if (argc != 3) {usage (argv[0]);exit (1);}/* 1. 创建socket fd */if ((fd = socket (AF_INET, SOCK_STREAM, 0)) < 0) {perror ("socket");exit (1);}port = atoi (argv[2]);if (port < 5000) {usage (argv[0]);exit (1);}/*2.连接服务器 *//*2.1 填充struct sockaddr_in结构体变量 */bzero (&sin, sizeof (sin));sin.sin_family = AF_INET;sin.sin_port = htons (port);	//网络字节序的端口号
#if 0sin.sin_addr.s_addr = inet_addr (SERV_IP_ADDR);
#elseif (inet_pton (AF_INET, argv[1], (void *) &sin.sin_addr) != 1) {perror ("inet_pton");exit (1);}
#endifif (connect (fd, (struct sockaddr *) &sin, sizeof (sin)) < 0) {perror ("connect");exit (1);}printf ("Client staring...OK!\n");/*3. 读写数据 */char buf[BUFSIZ];int ret = -1;while (1) {bzero (buf, BUFSIZ);if (fgets (buf, BUFSIZ - 1, stdin) == NULL) {continue;}do {ret = write (fd, buf, strlen (buf));} while (ret < 0 && EINTR == errno);if (!strncasecmp (buf, QUIT_STR, strlen (QUIT_STR))) {	//用户输入了quit字符printf ("Client is exiting!\n");break;}}/*4.关闭套接字 */close (fd);
}

相关文章:

网络编程学习笔记——TCP网络编程

文章目录 1、socket()函数2、bind()函数3、listen()4、accept()5、connect()6、send()/write()7、recv()/read()8、套接字的关闭9、TCP循环服务器模型10、TCP多线程服务器11、TCP多进程并发服务器 网络编程常用函数 socket() 创建套接字bind() 绑定本机地址和端口connect() …...

Vue+element-ui,实现表格渲染缩略图,鼠标悬浮缩略图放大,点击缩略图播放视频(一)

Vueelement-ui&#xff0c;实现表格渲染缩略图&#xff0c;鼠标悬浮缩略图放大&#xff0c;点击缩略图播放视频 前言整体代码预览图具体分析基础结构主要标签作用videoel-popover 前言 如标题&#xff0c;需要实现这样的业务 此处文章所实现的&#xff0c;是静态视频资源。 注…...

day13 leetcode-hot100-22(链表1)

160. 相交链表 - 力扣&#xff08;LeetCode&#xff09; 1.哈希集合HashSet 思路 &#xff08;1&#xff09;将A链的所有数据存储到HashSet中。 &#xff08;2&#xff09;遍历B链&#xff0c;找到是否在A中存在。 具体代码 /*** Definition for singly-linked list.* pu…...

【Oracle】DQL语言

个人主页&#xff1a;Guiat 归属专栏&#xff1a;Oracle 文章目录 1. DQL概述1.1 什么是DQL&#xff1f;1.2 DQL的核心功能 2. SELECT语句基础2.1 基本语法结构2.2 最简单的查询2.3 DISTINCT去重 3. WHERE条件筛选3.1 基本条件运算符3.2 逻辑运算符组合3.3 高级条件筛选 4. 排序…...

HUAWEI华为MateBook D 14 2021款i5,i7集显非触屏(NBD-WXX9,NbD-WFH9)原装出厂Win10系统

适用型号&#xff1a;NbD-WFH9、NbD-WFE9A、NbD-WDH9B、NbD-WFE9、 链接&#xff1a;https://pan.baidu.com/s/1qTCbaQQa8xqLR-4Ooe3ytg?pwdvr7t 提取码&#xff1a;vr7t 华为原厂WIN系统自带所有驱动、出厂主题壁纸、系统属性联机支持标志、系统属性专属LOGO标志、Office…...

【STIP】安全Transformer推理协议

Secure Transformer Inference Protocol 论文地址&#xff1a;https://arxiv.org/abs/2312.00025 摘要 模型参数和用户数据的安全性对于基于 Transformer 的服务&#xff08;例如 ChatGPT&#xff09;至关重要。虽然最近在安全两方协议方面取得的进步成功地解决了服务 Transf…...

leetcode hot100刷题日记——27.对称二叉树

方法一&#xff1a;递归法 class Solution { public:bool check(TreeNode *left,TreeNode *right){//左子树和右子树的节点同时是空的是对称的if(leftnullptr&&rightnullptr){return true;}if(leftnullptr||rightnullptr){return false;}//检查左右子树的值相不相等&a…...

高考加油(Python+HTML)

前言 询问DeepSeek根据自己所学到的知识来生成多个可执行的代码&#xff0c;为高考学子加油。最开始生成的都会有点小问题&#xff0c;还是需要自己调试一遍&#xff0c;下面就是完整的代码&#xff0c;当然了最后几天也不会有多少人看&#xff0c;都在专心的备考。 Python励…...

贪心算法应用:Ford-Fulkerson最大流问题详解

Java中的贪心算法应用:Ford-Fulkerson最大流问题详解 1. 最大流问题概述 最大流问题(Maximum Flow Problem)是图论中的一个经典问题,旨在找到一个从源节点(source)到汇节点(sink)的最大流量。Ford-Fulkerson方法是解决最大流问题的经典算法之一,它属于贪心算法的范畴…...

UE5 Niagara 如何让四元数进行旋转

Axis Angle中&#xff0c;X,Y,Z分别为旋转的轴向&#xff0c;W为旋转的角度&#xff0c;在这里旋转角度不需要除以2&#xff0c;因为里面已经除了&#xff0c;再将计算好的四元数与要进行旋转的四元数进行相乘&#xff0c;结果就是按照原来的角度绕着某一轴向旋转了某一角度...

从“黑箱”到透明化:MES如何重构生产执行全流程?

引言 在传统制造企业中&#xff0c;生产执行环节常面临“计划混乱、进度难控、异常频发、数据滞后”的困境。人工派工效率低下、物料错配频发、质量追溯困难等问题&#xff0c;直接导致交付延期、成本攀升、客户流失。深蓝易网MES系统以全流程数字化管理为核心&#xff0c;通过…...

探索Linux互斥:线程安全与资源共享

个人主页&#xff1a;chian-ocean 文章专栏-Linux 前言&#xff1a; 互斥是并发编程中避免竞争条件和保护共享资源的核心技术。通过使用锁或信号量等机制&#xff0c;能够确保多线程或多进程环境下对共享资源的安全访问&#xff0c;避免数据不一致、死锁等问题。 竞争条件 竞…...

JWT安全:假密钥.【签名随便写实现越权绕过.】

JWT安全&#xff1a;假密钥【签名随便写实现越权绕过.】 JSON Web 令牌 (JWT)是一种在系统之间发送加密签名 JSON 数据的标准化格式。理论上&#xff0c;它们可以包含任何类型的数据&#xff0c;但最常用于在身份验证、会话处理和访问控制机制中发送有关用户的信息(“声明”)。…...

Python爬虫实战:抓取百度15天天气预报数据

&#x1f310; 编程基础第一期《9-30》–使用python中的第三方模块requests&#xff0c;和三个内置模块(re、json、pprint)&#xff0c;实现百度地图的近15天天气信息抓取 记得安装 pip install requests&#x1f4d1; 项目介绍 网络爬虫是Python最受欢迎的应用场景之一&…...

RV1126 + FFPEG多路码流项目

代码主体思路&#xff1a; 一.VI,VENC,RGA模块初始化 1.先创建一个自定义公共结构体&#xff0c;用于方便管理各个模块 rkmedia_config_public.h //文件名字#ifndef _RV1126_PUBLIC_H #define _RV1126_PUBLIC_H#include <assert.h> #include <fcntl.h> #include …...

NodeJS 基于 Koa, 开发一个读取文件,并返回给客户端文件下载,以及读取文件形成列表和文件删除的代码演示

前言 在上一篇文章 《Nodejs 实现 Mysql 数据库的全量备份的代码演示》 中&#xff0c;我们演示了如何将用户的 Mysql 数据库进行备份的代码。但是&#xff0c;这个备份&#xff0c;只是备份在了服务器上了。 而我们用户的真实需求&#xff0c;是需要将备份文件下载到本地进行…...

为什么在我的Flask里面有两个路由,但是在网页里有一个却不能正确访问到智能体

1. /zhoushibo 能访问&#xff0c;/chat 直接浏览器访问报 Method Not Allowed 原因&#xff1a; /zhoushibo 路由是你用 app.route(/zhoushibo) 定义的&#xff0c;返回的是一个HTML网页&#xff0c;浏览器访问没问题。 /chat 路由你用的是 app.route(/chat, methods[POST])…...

哈工大计算机系统2024大作业——Hello的程序人生

计算机系统 大作业 题 目 程序人生-Hello’s P2P 专 业 人工智能 学   号 2022112040 班 级 2203601 学 生 郄东昕 指 导 教 师 吴锐 计算机科学与技术学院…...

2025年软件测试面试八股文(含答案+文档)

&#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 Part1 1、你的测试职业发展是什么&#xff1f; 测试经验越多&#xff0c;测试能力越高。所以我的职业发展是需要时间积累的&#xff0c;一步步向着高级测试工程师…...

【仿生系统】qwen的仿生机器人解决方案

收到完整需求后&#xff0c;我将从系统架构设计、算法实现路径、训练方法三个维度为您构建完整的仿生机器人解决方案。以下是分阶段实施方案&#xff1a; 一、核心系统架构设计&#xff08;模块化可进化架构&#xff09; 1. 多模态感知引擎 - 视觉子系统&#xff1a;YOLOv8SAM…...

Flutter3.22适配运行鸿蒙系统问题记录

Flutter3.22适配运行鸿蒙系统问题记录 一&#xff1a;适配条件适配过程问题记录&#xff08;1&#xff09;环境配置问题&#xff08;2&#xff09;Concurrent modification during iteration: Instance(length:2) of_GrowableList 报错&#xff08;3&#xff09;三方插件寻找替…...

秋招Day10 - JVM - 内存管理

JVM组织架构主要有三个部分&#xff1a;类加载器、运行时数据区和字节码执行引擎 类加载器&#xff1a;负责从文件系统、网络或其他来源加载class文件&#xff0c;将class文件中的二进制数据加载到内存中运行时数据区&#xff1a;运行时的数据存放的区域&#xff0c;分为方法区…...

Spring Boot 3.5.0中文文档上线

Spring Boot 3.5.0 中文文档翻译完成&#xff0c;需要的可收藏 传送门&#xff1a;Spring Boot 3.5.0 中文文档...

Redisson学习专栏(一):快速入门及核心API实践

文章目录 前言一、Redisson简介1.1 什么是Redisson&#xff1f;1.2 解决了什么问题&#xff1f; 二、快速入门2.1 环境准备 2.2 基础配置三、核心API解析3.1 分布式锁&#xff08;RLock&#xff09;3.2 分布式集合3.2.1 RMap&#xff08;分布式Map&#xff09;3.2.2 RList&…...

Pandas学习入门一

1.什么是Pandas? Pandas是一个强大的分析结构化数据的工具集&#xff0c;基于NumPy构建&#xff0c;提供了高级数据结构和数据操作工具&#xff0c;它是使Python成为强大而高效的数据分析环境的重要因素之一。 一个强大的分析和操作大型结构化数据集所需的工具集基础是NumPy…...

基于Piecewise Jerk Speed Optimizer的速度规划算法(附ROS C++/Python仿真)

目录 1 时空解耦运动规划2 PJSO速度规划原理2.1 优化变量2.2 代价函数2.3 约束条件2.4 二次规划形式 3 算法仿真3.1 ROS C仿真3.2 Python仿真 1 时空解耦运动规划 在自主移动系统的运动规划体系中&#xff0c;时空解耦的递进式架构因其高效性与工程可实现性被广泛采用。这一架…...

关于 JavaScript 版本、TypeScript、Vue 的区别说明, PHP 开发者入门 Vue 的具体方案

以下是关于 JavaScript 版本、TypeScript、Vue 的区别说明&#xff0c;以及 PHP 开发者入门 Vue 的具体方案&#xff1a; 一、JavaScript 版本演进 JavaScript 的核心版本以 ECMAScript 规范&#xff08;ES&#xff09; 命名&#xff1a; 版本发布时间关键特性ES52009严格模式…...

中断和信号详解

三种中断 中断分为三种&#xff1a;硬件中断、异常中断、软中断 硬件中断 设备向中断控制器发送中断请求&#xff0c;中断控制器生成对应中断号&#xff0c;然后通过中断引脚向cpu发送高电平&#xff0c;cpu收到请求后不会立即处理&#xff0c;cpu会处理完当前指令&#xff…...

STM32八股【10】-----stm32启动流程

启动流程 1.上电复位 2.系统初始化 3.跳转到 main 函数 启动入口&#xff1a; cpu被清空&#xff0c;程序从0x00000000开始运行0x00000000存放的是reset_handler的入口地址0x00000000的实际位置会变&#xff0c;根据不同的启动模式决定启动模式分为&#xff1a; flash启动&a…...

游戏引擎学习第312天:跨实体手动排序

运行游戏并评估当前状况 目前排序功能基本已经正常&#xff0c;能够实现特定的排序要求&#xff0c;针对单一区域、单个房间的场景&#xff0c;效果基本符合预期。 不过还有一些细节需要调试。现在有些对象的缩放比例不对&#xff0c;导致它们看起来有些怪异&#xff0c;需要…...