文章目录

  • 基础版
  • 多进程版本
  • 多线程版本
  • UDP实现

基础版

封装的socket.h
warp_socket.h

#ifndef _WRAP__
#define _WRAP__#include <sys/socket.h>int check(int ret, char *str);int my_socket(int domain, int type, int protocol);int default_my_socket_ipv4_tcp();struct sockaddr_in* create_ipv4_for_localhost(struct sockaddr_in *srv_addr, uint16_t port);int my_bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);int my_listen(int sockfd, int backlog);int my_accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);int my_connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);ssize_t my_read(int fd, void *buf, size_t count);ssize_t my_write(int fd, const void *buf, size_t count);
int my_setsockopt(int sockfd);
#endif

warp_socket.c

#include <stdio.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#include <arpa/inet.h>
#include <errno.h>
#include <strings.h>#define  SOCKET_ERROR "socket error"
#define  CONNECT_ERROR "connect error"
#define  LISTEN_ERROR "listen error"
#define  BIND_ERROR "bind error"
#define  ACCEPT_ERROR "accept error"
#define  PORT_ERROR "port error"int check(int ret, char *str) {if (ret == -1) {perror(str);exit(EXIT_FAILURE);}return ret;
}int my_socket(int domain, int type, int protocol) {return check(socket(domain, type, protocol), SOCKET_ERROR);
}int default_my_socket_ipv4_tcp() {return my_socket(AF_INET, SOCK_STREAM, 0);
}struct sockaddr_in *create_ipv4_for_localhost(struct sockaddr_in *srv_addr, uint16_t port) {//将地址结构清零
//    memset(&srv_addr,0, sizeof(srv_addr));bzero(srv_addr, sizeof(srv_addr));srv_addr->sin_family = AF_INET;srv_addr->sin_port = htons(port);srv_addr->sin_addr.s_addr = htonl(INADDR_ANY);return srv_addr;
}int my_bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) {return check(bind(sockfd, addr, addrlen), BIND_ERROR);
}int my_listen(int sockfd, int backlog) {return check(listen(sockfd, backlog), LISTEN_ERROR);
}int my_accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) {int i;again :if ((i = accept(sockfd, addr, addrlen)) < 0) {//因为accept是个慢系统调用,发送信号后中断后,重试./**      EINTR:如果进程在一个慢系统调用(slow system call)中阻塞时,当捕获到某个信号且相应信号处理函数返回时,这个系统调用被中断,调用返回错误,设置errno为EINTR*//*** ECONNABORTED*  该错误被描述为“software caused connection abort”,即“软件引起的连接中止”。*  原因在于当服务和客户进程在完成用于 TCP 连接的“三次握手”后,*  客户 TCP 却发送了一个 RST (复位)分节,在服务进程看来,就在该连接已由 TCP 排队,*  等着服务进程调用 accept 的时候 RST 却到达了。POSIX 规定此时的 errno 值必须 ECONNABORTED。*  源自 Berkeley 的实现完全在内核中处理中止的连接,*  服务进程将永远不知道该中止的发生。服务器进程一般可以忽略该错误,直接再次调用accept。*/if ((errno == ECONNABORTED) || (errno == EINTR)) {//重试等待连接goto again;} else {return check(i, ACCEPT_ERROR);}}}int my_connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) {return check(connect(sockfd, addr, addrlen), CONNECT_ERROR);
}ssize_t my_read(int fd, void *buf, size_t count) {ssize_t i;again :if ((i = read(fd, buf, count)) < 0) {if (errno == EINTR) {goto again;} else {return -1;}}return i;
}/*** 重复读取count个数数据** @param fd* @param buf* @param count 读取指定个数,应该<=buf空间* @return*/
ssize_t my_readn(int fd, void *buf, size_t count) {ssize_t i;//记录剩余尚未读取个数ssize_t nleft = count;char *ptr = buf;while (nleft > 0) {if ((i = read(fd, buf, nleft)) < 0) {if (errno == EINTR) {i = 0;} else {return -1;}} else if (i == 0) {break;}//说明读取成功//重新计算应读个数,以及指针偏移nleft -= i;ptr += i;}//返回已读个数return count - nleft;
}ssize_t my_writen(int fd, void *buf, size_t count) {ssize_t i;ssize_t nleft = count;char *ptr = buf;while (nleft > 0) {if ((i = write(fd, buf, nleft)) <= 0) {if (i < 0 && errno == EINTR) {i = 0;} else {return -1;}}nleft -= i;ptr += i;}//返回已写个数return count - nleft;
}ssize_t my_write(int fd, const void *buf, size_t count) {ssize_t i;again :if ((i = write(fd, buf, count)) < 0) {if (errno == EINTR) {goto again;} else {return -1;}}return i;
}/**
* 设置端口复用,也就是可以重复利用同一个端口运行服务器
*/
int my_setsockopt(int sockfd) {//是否启用,0不启用int opt = 1;return check(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (void *) &opt, sizeof(opt)), PORT_ERROR);
}

客户端

#include <stdio.h>
#include <sys/socket.h>
#include <unistd.h>
#include <arpa/inet.h>
#include  "warp_socket.h"
#define  PORT 8080int main() {char buf[BUFSIZ];struct sockaddr_in serv_addr;serv_addr.sin_family = AF_INET;serv_addr.sin_port = htons(PORT);
//    inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr.s_addr);inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr);int cfd = default_my_socket_ipv4_tcp();int ret = my_connect(cfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr));while (1) {my_write(cfd, "hello\n", 6);sleep(1);int size = my_read(cfd, buf, sizeof(buf));my_write(STDOUT_FILENO, buf, size);}close(cfd);return 0;
}

服务端

#include <stdio.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#include <arpa/inet.h>
#include  "warp_socket.h"/*** 一个文件描述符指向一个套接字,该套接字内部由内核借助2个缓冲区实现** 网络字节序*          小端存储法(pc本地存储):高位存高地址,低位存低地址.*          大端存储法(网络存储):同上相反.*          h表示host,n表示network,l表示32位整数,s表示16位短整数*          htonl:本地 => 网络(IP)      只接收32位整数,所以需要先将点分十进制形式(192.XX.XX.XX)的ip地址转成整数, INADDR_ANY获取系统有效IP地址,32整型*          htons:本地 => 网络(port)*          ntohl  网络 => 本地(IP)*          ntohs  网络 => 本地(port)** IP地址转换*          int inet_pton(int af, const char *src, void *dst);      本地(string) =>网络*          af : AF_INET / AF_INET6  ipv4 /ipv6*          src : IP地址(点分10进制形式)*          dst : 传出参数,网络字节序的ip地址**          const char *inet_ntop(int af, const void *src,char *dst, socklen_t size);  网络 => 本地(string)*/#define  PORT 8080int main() {int lfd, cfd;int ret;char buf[BUFSIZ], cli_Ip[1024];struct sockaddr_in serv_addr, clit_addr;serv_addr.sin_family = AF_INET;serv_addr.sin_port = htons(PORT);serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);/*** int socket(int domain, int type, int protocol);* domain : ip地址协议* type : 数据协议 流式协议,报式协议* protocol : 选用协议的代表协议. 流式就是tcp,报式就是udp*/lfd = default_my_socket_ipv4_tcp();/***  int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);*  struct sockaddr_in {sa_family_t sin_family;  IP地址协议in_port_t sin_port;  端口(网络字节序)struct in_addr sin_addr;  ip(网络字节序)}sockaddr_in主要弥补sockaddr的缺陷(目标地址和端口信息混在一起),占用字节大小都一样.*/my_bind(lfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr));//最多允许连接数my_listen(lfd, 128);//监听并处理连接socklen_t client_addr_len = sizeof(clit_addr);cfd = my_accept(lfd, (struct sockaddr *) &clit_addr, &client_addr_len);printf("client ip: %s , port: %d  \n", inet_ntop(AF_INET, &clit_addr.sin_addr.s_addr, cli_Ip, sizeof(cli_Ip)),ntohs(clit_addr.sin_port));while (1) {ret = my_read(cfd, buf, sizeof(buf));my_write(STDOUT_FILENO, buf, ret);for (int i = 0; i < ret; ++i) {buf[i] = toupper(buf[i]);}my_write(cfd, buf, ret);}close(cfd);close(lfd);return 0;
}

多进程版本

#include <stdio.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#include <arpa/inet.h>
#include <signal.h>
#include <wait.h>
#include  "warp_socket.h"/*** 多进程* @return*/
#define PORT 8080void killP() {while (waitpid(0, NULL, WNOHANG) > 0);return;
}int main() {int lfd, cfd;int ret;char buf[BUFSIZ];pid_t pid;struct sockaddr_in srv_addr, cli_addr;create_ipv4_for_localhost(&srv_addr, PORT);lfd = default_my_socket_ipv4_tcp();my_bind(lfd, (struct sockaddr *) &srv_addr, sizeof(srv_addr));my_listen(lfd, 128);socklen_t cli_addr_len = sizeof(cli_addr);while (1) {cfd = my_accept(lfd, (struct sockaddr *) &cli_addr, &cli_addr_len);pid = fork();if (pid < 0) {exit(EXIT_FAILURE);} else if (pid == 0) {//关闭主进程accept描述符close(lfd);break;} else {//关闭子进程描述符close(cfd);//回收子进程struct sigaction act;act.sa_handler = killP;sigemptyset(&act.sa_mask);act.sa_flags = 0;ret = sigaction(SIGCHLD, &act, NULL);if (ret != 0) {perror("error");exit(EXIT_FAILURE);}continue;}}if (pid == 0) {while (1) {ret = my_read(cfd, buf, sizeof(buf));printf("%d \n", ret);//读到文件末尾或者客户端主动断开,则直接返回if (ret <= 0) {close(cfd);perror("error");exit(EXIT_FAILURE);}my_write(STDOUT_FILENO, buf, ret);for (int i = 0; i < ret; i++) {buf[i] = toupper(buf[i]);}my_write(cfd, buf, ret);}}return 0;
}

多线程版本

#include <stdio.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#include <arpa/inet.h>
#include <signal.h>
#include <wait.h>
#include <pthread.h>
#include  "warp_socket.h"/*** 多线程* @return*/
#define PORT 8080
struct s_info {struct sockaddr_in cliaddr;int fd;
};void *work_fun(void *s_info) {struct s_info *s = (struct s_info *) s_info;int ret;char buf[BUFSIZ];char str[INET_ADDRSTRLEN];while (1) {ret = my_read(s->fd, buf, BUFSIZ);printf("%d \n", ret);//读到文件末尾或者客户端主动断开,则直接返回if (ret <= 0) {break;}printf("received from %s at PORT %d \n", inet_ntop(AF_INET, &(*s).cliaddr.sin_addr, str, INET_ADDRSTRLEN),ntohs((*s).cliaddr.sin_port));printf("received from %s at PORT %d \n", inet_ntop(AF_INET, &(s->cliaddr.sin_addr), str, INET_ADDRSTRLEN),ntohs(s->cliaddr.sin_port));my_write(STDOUT_FILENO, buf, ret);for (int i = 0; i < ret; i++) {buf[i] = toupper(buf[i]);}my_write(s->fd, buf, ret);}close(s->fd);return (void *) 0;
}int main() {int lfd, cfd;int i = 0;struct sockaddr_in srv_addr, cli_addr;socklen_t cli_addr_len = sizeof(cli_addr);struct s_info t[256];create_ipv4_for_localhost(&srv_addr, PORT);lfd = default_my_socket_ipv4_tcp();my_bind(lfd, (struct sockaddr *) &srv_addr, sizeof(srv_addr));my_listen(lfd, 128);pthread_t tid;printf("wait for Accept... ");while (1) {cfd = my_accept(lfd, (struct sockaddr *) &cli_addr, &cli_addr_len);t[i].cliaddr = cli_addr;t[i].fd = cfd;pthread_create(&tid, NULL, work_fun, (void *) &t[i]);//线程分离,防止僵尸线程产生.pthread_detach(tid);i = (++i) % 256;}return 0;
}

UDP实现

client.c

#include <stdio.h>
#include <sys/socket.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <strings.h>#define  PORT 8080int main() {char buf[BUFSIZ];int cfd;int n;struct sockaddr_in serv_addr;socklen_t serv_addr_len = sizeof(serv_addr);bzero(&serv_addr, sizeof(serv_addr));serv_addr.sin_family = AF_INET;serv_addr.sin_port = htons(PORT);inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr);cfd = socket(AF_INET, SOCK_DGRAM, 0);while (1) {//相当于connect+writen = sendto(cfd, "hello\n", 6, 0, (struct sockaddr *) &serv_addr, serv_addr_len);if (n == -1) {perror("error");}n = recvfrom(cfd, buf, BUFSIZ, 0, NULL, 0);  //不需要服务器的信息write(STDOUT_FILENO, buf, n);if (n == -1) {perror("error");}sleep(1);}close(cfd);return 0;
}

server.c

#include <stdio.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#include <arpa/inet.h>
#include <strings.h>/*** upd 通信* @return*/
#define PORT 8080
int main() {int lfd;int n;char buf[BUFSIZ], cli_Ip[1024];struct sockaddr_in serv_addr, clit_addr;socklen_t client_addr_len = sizeof(clit_addr);lfd=socket(AF_INET, SOCK_DGRAM, 0);bzero(&serv_addr, sizeof(serv_addr));serv_addr.sin_family = AF_INET;serv_addr.sin_port = htons(PORT);serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);bind(lfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr));printf("acepting...");while (1){//相当于accpet+writen = recvfrom(lfd, buf,BUFSIZ,0,(struct sockaddr *)&clit_addr,&client_addr_len);if (n==-1){perror("error");}write(STDOUT_FILENO, buf, n);printf("client ip: %s , port: %d  \n", inet_ntop(AF_INET, &clit_addr.sin_addr.s_addr, cli_Ip, sizeof(cli_Ip)),ntohs(clit_addr.sin_port));for (int i = 0; i < n; ++i) {buf[i] = toupper(buf[i]);}n=sendto(lfd, buf, n,0,(struct sockaddr *)&clit_addr, sizeof(clit_addr));if (n==-1){perror("error");}}close(lfd);return 0;
}

C采用多线程和多进程实现TCP/UDP网络连接相关推荐

  1. C# TCP/UDP网络通讯调试助手(含源码)

    C# TCP/UDP网络通讯调试助手 1.客户端界面 1.客户端界面 源码下载地址:https://download.csdn.net/download/kingleitao/11927885

  2. TCP/UDP端口连接测试

    TCP/UDP端口连接测试 TCP端口连接测试 测试主机与目标端口的网络是否通畅 telnet ip port UDP端口连接测试 1.测试目标端口是否正常(前提是目标主机上启动了服务端口,且本机与服 ...

  3. 【Java】网络编程——TCP/UDP网络对讲机

    目录 前言 OSI概述 传输层 端口 Socket InetAddress类 UDP协议间的通信 UDP协议相关类 UDP 发送端 UDP 接收端 + 发送端 UDP 聊天 + 全局广播 UDP发送大 ...

  4. TCP/UDP(网络编程)

    TCP/UDP(大作业) 文章目录 TCP/UDP(大作业) Java 网络编程 1.1.描述TCP协议和UDP协议,并说出他们的区别与联系: 基于客户机-服务器模式的应用场景 2.什么是客户机-服务 ...

  5. iptraf:一个实用的TCP/UDP网络监控工具

    iptraf是一个基于ncurses的IP局域网监控器,用来生成包括TCP信息.UDP计数.ICMP和OSPF信息.以太网负载信息.节点状态信息.IP校验和错误等等统计数据. 它基于ncurses的用 ...

  6. 详解TCP/UDP网络协议

    ​​​​​​​ 目录 TCP 和 UDP 有哪些区别? UDP介绍 UDP的特点 UDP应用场景 TCP介绍 TCP头部结构 TCP特点 TCP 的三次握手 TCP 四次挥手 流量控制 拥塞控制 ​​ ...

  7. Java的TCP/UDP网络编程+多线程实现服务器端与客户端间的通信

    写在前面: Java为网络编程提供了丰富的库,我们能通过调用Socket套接字的方法实现服务器与客户端的双通信. 注意点: 需要注意的是端口的对应,端口可以理解为窗户,服务器只能通过某个端口(窗户)与 ...

  8. 2.6-使用C#Winform开发Tcp/Udp网络小助手(含步骤和源码)

    一.界面设计 在做软件之前使用"软件界面设计工具"先设计并绘制出软件界面,方便在写代码之前确认"功能需求"以及"交互流程". 这里使用的软件 ...

  9. TCP/UDP网络的通信

    一.基础通信 1.TCP通信架构 2.UDP通信 总结 一.基础通信 1.TCP通信架构 对于服务器,其通信流程一般有如下步骤: 1.调用socket函数创建socket,这一步会创建一个文件描述符f ...

最新文章

  1. 在MATLAB中实现对MySQL数据库的操作
  2. 【今晚7点半】:华为云在数字人领域的技术探索与实践
  3. MFC中CString类字符串与长整型、浮点型、字符数组char数据之间的相互转换
  4. python打印二进制内容_在python中打印出c类型的二进制表示
  5. redis 集群_Redis集群管理
  6. 2.4 sklearn中的metrics.roc_auc_score评价指标
  7. Halcon教程四:一个小技巧
  8. 英文顺序:第一~第N
  9. 运筹学实验_指派问题
  10. qgjsfagafgpjqip
  11. 微信小程序开发的基础学习
  12. Hadoop,master和slave简单的分布式搭建
  13. java核心技术卷一_java核心技术卷一
  14. 信号完整性(概念一览表)
  15. 九九八十一难|R一步安装TSA包
  16. 推荐一个Windows10下的搜索软件:Everything
  17. shell查mysql_通过shell检查mysql主机和数据库,生成html报表的脚本
  18. Altium Designer 19.1.18 - 更改铺铜的透明度
  19. wnmp mysql_WNMP环境搭建(win10+Ndinx1.9.15+MySQL5.7.12+PHP5.6.21)
  20. javaweb JSP JAVAJSP 新闻发布系统源码(JSP新闻管理系统)JSP新闻系统

热门文章

  1. linux线程同步与异步的区别,怎样理解阻塞非阻塞与同步异步的区别?
  2. 贪心算法|三角形的最大周长
  3. 2017计蒜客第二场(AB)
  4. yolov5的学习和总结
  5. 公司要收我的毕业证书,这合法吗?
  6. chrome的network查看资源下载时间并优化(瀑布流图)
  7. 基于flowable的upp(统一流程平台)运行性能优化(2)
  8. java中readLine()方法为什么有的行读不到?
  9. 阿富汗-天灾与人祸的荒野[天声人语2009年8月25日(火)]
  10. DTH11 温湿度传感器