套接字编程-UDP、TCP套接字编程,多线程、多进程套接字编程

    技术2024-03-30  103

    struct sockaddr_in { short int sin_family; /*地址族:AF_INET*/ unsigned short int sin_port; /*端口号*/ struct in_addr sin_addr; /*IP地址*/ unsigned char sin_zero[8]; /*零数据(bzero或memset设置)*/ }; //头文件#include <netinet/in.h>中定义。 struct sockaddr { unsigned short sa_family;/*地址族:AF_INET*/ char sa_data[14];/*协议地址:IP地址和端口号*/ }; //头文件#include <sys/socket.h>中定义

    主机字节序变为网络字节序 • uint16_t htons(uint16_t hostshort) • uint32_t htonl(uint32_t hostlong) • 网络字节序变为主机字节序 • uint16_t ntohs(uint16_t netshort) • uint32_t ntohl(uint16_t netlong)

    INADDR_ANY 转换过来就是0.0.0.0,泛指本机的意思

    简单UDP循环服务器socket编程基本步骤

    简单TCP循环服务器socket编程基本步骤

    UDP编程函数解析

    socket()

    创建套接字函数<sys/socket.h>

    格式: int socket(intdomain,inttype,int protocol)

    功能:根据给定的参数创建一个套接字描述子,成功返回一个 套接字描述符,不成功返回-1。

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

    参数: domain指示网络协议族,PF_INET或PF_INET6标识TCP/IP协议 type指示协议类型,如SOCK_STREAM(TCP)SOCK_DGRAM(UDP)和SOCK_RAW protocol指示特定的协议类型,type为SOCK_STREAM和SOCK_DGRAM时 protocol设置为0,type为SOCK_RAW时可设置不同的值(IPPROTO_ICMP、 IPPROTO_IGMP、 IPPROTO_RAW等)

    bind()

    绑定套接字函数 <sys/socket.h>

    格式: int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)功能:绑定IP地址和端口号到套接字sockfd。成功返回0,否则返回-1。参数: sockfd:指示套接字描述符; addr:指示本地的套接字地址; addrlen:指示套接字地址长度;

    accept()

    接受请求套接字函数<sys/socket.h>

    格式: int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)功能:接收连接请求,成功返回一个非负的整数作为连接套接字描述符; 否则返回-1。使用该函数后,系统从完成队列中取出socket,为每个连接 请求创建一个新的连接套接字,服务器只对新的连接使用该套接字,原来 的监听套接字接受其他的连接请求。参数: sockfd:指示套接字描述符; addr:获得对方的套接字地址(作为返回结果); addrlen:指示套接字地址长度(作为返回结果) ;

    recv()

    接收数据套接字函数

    格式: ssize_t recv(int sockfd, void *buf, size_t len, int flags)功能:接收指定套接字的数据。成功返回接收的字节数,否则返回-1。 tcp协议收到FIN数据,返回0;参数: sockfd指示套接字描述符; buf用于存放接收数据的缓冲区; len指示接收缓冲区的长度; flags指示接收数据的属性; flags是传输控制标志,其值定义如下: 0:常规操作,如同read()函数; MSG_PEEK:只查看数据而不读出数据, 后续读操作仍然能读该数据; MSG_OOB:忽略常规数据,而只读带 外数据; MSG_WAITALL:recv函数只有在将接收 缓冲区填满后才返回。

    recvfrom()

    格式: ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen)功能:接收指定套接字的数据。成功返回接收的字节数,否则返回-1。 tcp协议收到FIN数据,返回0;参数: sockfd指示套接字描述符; buf:用于存放接收数据的缓冲区; len:指示接收缓冲区的长度; flags:指示接收数据的属性,同recv(); src_addr和addrlen:分别指示接收数据的来源地址及其地址长度。

    send()

    发送数据套接字函数

    格式: int send(int sockfd, const void *buf, size_t len, int flags)功能:通过已经连接的套接字发送数据。成功返回发送的字符数, 否则返回-1。参数: sockfd:指示套接字描述符; buf:要发送的数据缓冲区; len:指示要发送数据的长度; flags:指示发送数据的属性,通常设置为0。其它典型的 MSG_OOB指示带外数据发送; flags是传输控制标志,其值定义如下: 0:常规操作,如同write()函数; MSG_OOB:发送带外数据; MSG_DONTROUTE:忽略底层路 由协议,直接发送。

    sendto()

    格式: int sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen)功能:向指定的目的地址发送数据。成功返回发送的字符数,否则 返回-1。参数: sockfd:指示套接字描述符; buf:要发送的数据缓冲区; len:指示要发送数据的长度; flags:指示发送数据的属性,典型的MSG_OOB指示带外数据发送; dest_addr和addrlen:分别指示目的地址和目的地址的长度。

    close()

    关闭套接字函数 <unistd.h>

    格式: int close(int sockfd)功能:关闭网络套接字。成功返回0,否则返回-1。参数: sockfd指示套接字描述符;

    close函数缺省功能是将套接字做上“已关闭”标记,并立即返回到进程。这 个套接字不能再为该进程所用。 • 正常情况下,close将引发向TCP的四次挥手终止序列,但在终止前将发送已 排队的数据; • 如果套接字描述符访问计数在调用close后大于0(在多个进程共享同一个套接 字的情况下),则不会引发TCP终止序列(即不会发送FIN分节);

    UNIX中UDP编程实例

    服务器端

    #include <stdio.h> //标准输入输出 #include <stdlib.h> //标准库 #include <string.h> //字符数组相关函数 #include <sys/types.h> //基本系统数据类型 #include <sys/socket.h> //提供socket函数及数据结构 #include <netinet/in.h> //定义数据结构sockaddr_in #include <unistd.h> //系统调用接口 #include <arpa/inet.h> //提供IP地址转换函数 #define BUFFER_LENGTH 1024 int main(int argc, char*argv[]) { struct sockaddr_in server_addr, client_addr; //定义套接字地址信息结构体变量 int sockfd; //套接字描述符 socklen_t client_addr_len = sizeof(struct sockaddr); //struct sockaddr为套接字地址形式变量,socklen_t表示套接字地址长度 char buf[BUFFER_LENGTH]; //定义接收缓存buf,长度为BUFFER_LENGTH int n; if (argc!= 3){ //检查输入参数的数量 //如果输入参数不为3则标准输出正确的该函数的使用方法 fputs("usage: ./udpserver serverIP serverPort\n", stderr); exit(1); } sockfd= socket(AF_INET, SOCK_DGRAM, 0); //创建UDP套接字 if (sockfd== -1){ //判断套接字是否创建成功,-1则失败 perror("created udp socket"); //perror(s)输出上一个函数的错误原因,s会在输出中优先输出 exit(1); } bzero(&server_addr, sizeof(server_addr)); //套接字地址信息结构体变量初始化清0 server_addr.sin_family = AF_INET; //指定网络协议类型为IP协议 server_addr.sin_port = htons(atoi(argv[2])); //设置端口号(端口号在调用函数时的第三个参数给出)。atoi是把字符串转换成整型数的一个函数 if (inet_aton(argv[1], &server_addr.sin_addr) == 0){ //inet_aton将将一个字符串IP地址转换为一个32位的网络序列IP地址,并赋值为第二个变量。成功则返回非0 perror("inet_aton"); exit(1); } if (bind(sockfd, (struct sockaddr *)&server_addr,sizeof(server_addr)) == -1){ //将地址信息绑定到套接字 perror("call to bind"); exit(1); } while(1){//无限循环,收发数据 //作用:清空buf变量存储空间 //函数解释:void *memset(void *s, int ch, size_t n); //将s的前n个内容用ch替代 memset(buf, 0, BUFFER_LENGTH); n = recvfrom(sockfd, buf, BUFFER_LENGTH, 0, (struct sockaddr *)&client_addr, &client_addr_len); //以阻塞I/O的方式从网络接收数据 //所接收数据的源地址存放在 // client_addr套接字地址信息结构体变量 if (n == -1){ perror(“fail to receive”); exit(1); } else{ buf[n] = '\0'; printf("%s\n", buf); gets(buf); n = sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr*)&client_addr, sizeof(client_addr)); //以阻塞I/O方式向网络发送数据 //发送数据的目的地,由client_addr指定 if (n == -1){ perror("fail to reply"); exit(1); } } } if (close(sockfd) == -1){ //关闭套接字 perror("fail to close"); exit(1); } puts("UDP Server is closed!\n"); return EXIT_SUCCESS; }

    客户端

    #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <unistd.h> #include <arpa/inet.h> #define BUFFER_LENGTH 1024 int main(int argc, char *argv[]) { struct sockaddr_in server_addr; int sockfd; socklen_t server_addr_len = sizeof(struct sockaddr); char buf[BUFFER_LENGTH]; int n; if (argc!= 3){ fputs("usage: ./udpclient serverIP serverPort\n", stderr); exit(1); } sockfd = socket(AF_INET, SOCK_DGRAM, 0); if (sockfd== -1){ perror("created udp socket...\n"); exit(1); } bzero(&server_addr, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(atoi(argv[2])); if (inet_aton(argv[1], &server_addr.sin_addr) == 0){ perror("inet_aton"); exit(1); } memset(buf, 0, BUFFER_LENGTH); gets(buf); //从标准输入写入内容 n = sendto(sockfd, buf, strlen(buf), 0, (structsockaddr *)&server_addr, sizeof(server_addr)); if (n == -1){ perror("fail to send"); exit(1); } memset(buf, 0, BUFFER_LENGTH); n = recvfrom(sockfd, buf, BUFFER_LENGTH, 0, (struct sockaddr *)&server_addr, &server_addr_len); if (n == -1){ perror("fail to receive"); exit(1); } else{ buf[n] = '\0'; printf("Server: %s\n", buf); } if (close(sockfd) == -1){ perror("fail to close"); exit(1); } puts("UDP Client is closed!\n"); return EXIT_SUCCESS; }

    多进程方式套接字编程

    fork()

    每有一个客户端发来链接请求,利用fork()形式创建子进程,该子进程来处理这个客户端

    关于fork()函数的内容可以参见该文章https://blog.csdn.net/qq_43589143/article/details/106717493

    代码实例

    int main(void) { int listenfd, connfd;//listenfd为父进程的套接字,connfd为每次fork生成的子进程的套接字 pid_t pid; int BACKLOG = 5; //监听队列长度 if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { perror(“Create socket failed.); exit(1); } bind(listenfd,); listen(listenfd, BACKLOG); while(1) { if ((connfd= accept(sockfd, NULL, NULL)) == -1) { perror(“Accept error.); exit(1); } //父进程 if ((pid = fork()) >0) { //首先要关闭子进程的套接字 close(connfd); …… conntinue; } //子进程 else if (pid== 0) { //首先要关闭父进程的套接字 close(listenfd); …… close(connfd); exit(0); } else { //创建进程出错 perror(“Create child process failed.); exit(1); } } }

    fork()的问题

    Fork是“昂贵的”-费时费资源;父子进程间通信比较麻烦;并发进程的数量是有限的;

    多线程方式套接字编程

    使用<pthread.h> 库函数 具体使用参见该文章https://blog.csdn.net/qq_43589143/article/details/106699210

    编程实例

    #define PORT 1234 #define BACKLOG 2 //定义监听队列长度为2 #define MAXDATASIZE 1000 void process_cli(int connectfd, sockaddr_in client); void *start_routine(void *arg); struct ARG { //构造传递进线程的参数结构 int connfd; //连接套接字 struct sockaddr_in client; //客户端地址 }; int main(void) { int listenfd, connectfd; pthread_t tid; struct ARG *arg; struct sockaddr_in server, client; int sin_size; /* Create TCP Socket *//* Bind server address to listenfd. *//* Listen on listenfd */ … sin_size = sizeof(struct sockaddr_in); while(1) { if ((connectfd= accept(listenfd, (structsockaddr*)&client, &sin_size)) == -1) /* handle error */ arg= new ARG; arg-> connfd= connectfd; memcpy((void *)&arg -> client, &client, sizeof(client)); if (pthread_create(&tid, NULL, start_routine, (void *)arg) /* handle erroe*/ } close(listendfd); } void process_cli(int connectfd, sockaddr_in client) {} void *start_routine(void *arg) { struct ARG *info; info = (ARG *)arg; process_cli(info -> connfd, info -> client); delete arg; pthread_exit(NULL); }
    Processed: 0.013, SQL: 9