超详细c语言tcp通信接口

  • 1、可下载源码(客户端 || 服务端通信)
  • 2、说明
  • 3、接口代码
  • 4、客户端通信main_client_demo.c
  • 5、服务端通信main_server_demo.c

1、可下载源码(客户端 || 服务端通信)

tcp通信代码资源

2、说明

功能:tcp服务端与多个客户端进行通信(服务端通过多线程方式处理客户端服务),初始设置参数,解决服务端重启出现地址占用问题

编译:
make clean
make

执行:
(tcp服务端,系统中只能运行一个)
./server

(tcp客户端,可运行多个,打开另外的终端窗口执行)
./client

3、接口代码

.h头文件

/*************************************************
Function:tcp 服务端和客户端总接口,多线程实现一个服务端处理多个客户端通信服务
author:zyh
date:2020.4
**************************************************/#ifndef _TCP_CLIENT_AND_SERVER_H_
#define _TCP_CLIENT_AND_SERVER_H_int tcp_creat_socket(void);//创建socket
int tcp_client_connect(int sockfd, char *server_ip, int server_port);//tcp客户端连接服务器
int tcp_send(int sockfd,  void *sendBuf,  int len);//tcp发送消息
int tcp_blocking_rcv(int sockfd, void *recvBuf, int len);//tcp堵塞接收消息
int tcp_noblocking_rcv(int sockfd,  void *recvBuf, int len, int timeval_sec, int timeval_usec);//tcp非堵塞接收消息
void tcp_close(int sockfd);//tcp关闭socket通信//服务端多出来的部分
int tcp_server_bind_and_listen(int sockfd, char *server_ip, int server_port, int max_listen_num);//tcp服务器绑定端口、监听设置
int tcp_server_wait_connect(int sockfd);//tcp阻塞等待客户端连接
void tcp_server_creat_pthread_process_client(int *new_sockfd, void* (*callBackFun)(void*));//服务端每接收到新的客户端连接,就创建新线程提供服务,外部需要重写处理消息的回调函数,参考void *tcp_server_callBackFun_demo(void *ptr)
void *tcp_server_callBackFun_demo(void *ptr);//callBackFun:处理客户端消息的回调函数,示例#endif

.c源码

/*************************************************
Function:tcp 服务端和客户端总接口,多线程实现一个服务端处理多个客户端通信服务
author:zyh
date:2020.4
**************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>//提供IP地址转换函数
#include <sys/types.h>//数据类型定义文件
#include <sys/socket.h>//提供socket函数及数结构
#include <netinet/in.h>//定义数据结构体sockaddr_in
#include <netinet/ip.h>
#include <pthread.h>
#include <stdbool.h>/**
函数功能:tcp创建socket通信
入参:无
出参:无
返回:成功:socket通信句柄,失败:-1
**/
int tcp_creat_socket(void)
{int sockfd = 0;sockfd = socket(AF_INET, SOCK_STREAM, 0);if (0 > sockfd) {perror("socket");return -1;}//设置一下参数属性,防止服务端重启,出现地址占用问题int bReuseaddr = 1;//允许重用本地地址和端口, close socket(一般不会立即关闭而经历TIME_WAIT的过程)后想继续重用该socketif(0 > setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&bReuseaddr, sizeof(int))) {perror("setsockopt");}#if 0int bDontLinger = 0;//如果要已经处于连接状态的soket在调用closesocket后强制关闭,不经历TIME_WAIT的过程:if(0 > setsockopt(sockfd, SOL_SOCKET, SO_LINGER, (const char*)&bDontLinger, sizeof(int))) {perror("setsockopt");}
#endif#if 0// 接收缓冲区int nRecvBuf = 32*1024;//设置为32Ksetsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, (const char*)&nRecvBuf, sizeof(int));//发送缓冲区int nSendBuf = 32*1024;//设置为32Ksetsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (const char*)&nSendBuf, sizeof(int));
#endifreturn sockfd;
}/**
函数功能:tcp客户端连接服务器
入参:socket:socket通信句柄
入参:server_ip:服务器ip
入参:server_port:服务器端口(提供给客户端连接的端口)
出参:无
返回:成功:0, 失败:-1
**/
int tcp_client_connect(int sockfd, char *server_ip, int server_port)
{unsigned int server_addr = 0;struct sockaddr_in st_server_addr = {0};st_server_addr.sin_family = AF_INET;st_server_addr.sin_port = htons(server_port);//端口号,无符号短整型数值转换为网络字节序,即大端模式(big-endian)inet_pton(AF_INET, server_ip, &server_addr);//ip转换函数,主机字节序转化为网络字节序st_server_addr.sin_addr.s_addr = server_addr;if (0 > connect(sockfd, (struct sockaddr *)&st_server_addr, sizeof(st_server_addr))) {perror("connect");return -1;}return 0;
}/**
函数功能:tcp发送消息
入参:sockfd:句柄
入参:sendBuf:发送的消息内容;
入参:len:发送的消息内容长度(字节)(如果使用strlen计算长度时请注意碰到0x00会截至)
出参:无
返回:成功:实际发送的字节数,失败:-1
**/
int tcp_send(int sockfd,  void *sendBuf,  int len)
{int sendbytes = 0;if (0 > (sendbytes = send(sockfd, sendBuf, len,  MSG_DONTWAIT|MSG_NOSIGNAL))) {perror("send");return -1;}return sendbytes;
}/**
函数功能:tcp堵塞接收消息
入参:sockfd:文件操作句柄
入参:recvBuf:接收的消息缓冲区(使用前后注意清空消息缓冲区,要不然存放消息会遗留上次接收的部分数据)
入参:len:缓冲区长度
出参:无
返回:成功:实际接收的字节数(其中:如果连接已中止,返回0), 失败:-1
**/
int tcp_blocking_rcv(int sockfd, void *recvBuf, int len)
{int recvbytes = 0;if (0 > (recvbytes = recv(sockfd, recvBuf, len, 0)) ) {perror("recv");return -1;}return recvbytes;
}/**
函数功能:tcp非堵塞接收消息
入参:sockfd:句柄
入参:recvBuf:接收的消息缓冲区(使用前后注意清空消息缓冲区,要不然存放消息会遗留上次接收的部分数据)
入参:len:缓冲区长度
入参:timeval_sec:超时时间(秒)
入参:timeval_usec:超时时间(微秒)
出参:无
返回:成功:实际接收的字节数(其中:如果连接已中止,返回0), 失败:-1,  超时:-2
**/
int tcp_noblocking_rcv(int sockfd,  void *recvBuf, int len, int timeval_sec, int timeval_usec)
{fd_set readset;struct timeval timeout={0, 0};int maxfd = 0;int recvbytes = 0;int ret = 0;timeout.tv_sec = timeval_sec;timeout.tv_usec = timeval_usec;FD_ZERO(&readset);           FD_SET(sockfd, &readset);         maxfd = sockfd + 1;    ret = select(maxfd, &readset, NULL, NULL, &timeout); if (0 > ret) {return -2;} else {if (FD_ISSET(sockfd, &readset)) {if (0 > (recvbytes = recv(sockfd, recvBuf, len, MSG_DONTWAIT))) {perror("recv");return -1;}} else {return -1;}}return recvbytes;
}/**
函数功能:tcp关闭sockfd通信句柄
入参:sockfd:socket通信句柄
出参:无
返回:无
**/
void tcp_close(int sockfd)
{//close(sockfd);if (sockfd > 0) {  //sockfd等于0时不能关,防止把文件句柄0关掉,影响系统,会导致scanf()函数输入不了close(sockfd);}
}/*********************以下是服务端多出来的部分********************************************************************************************/
/**
函数功能:tcp服务器绑定端口、监听设置
入参:sockfd:socket通信句柄
入参:server_ip:服务器本地IP
入参:server_port:服务器本地端口(提供给客户端连接的端口)
入参:max_listen_num:最大监听客户端的数目
出参:无
返回:成功返回0, 失败返回-1
**/
int tcp_server_bind_and_listen(int sockfd, char *server_ip, int server_port, int max_listen_num)
{unsigned int server_addr = 0;struct sockaddr_in st_LocalAddr = {0}; //本地地址信息结构图,下面有具体的属性赋值st_LocalAddr.sin_family = AF_INET;  //该属性表示接收本机或其他机器传输st_LocalAddr.sin_port = htons(server_port); //端口号,无符号短整型数值转换为网络字节序,即大端模式(big-endian)inet_pton(AF_INET, server_ip, &server_addr);//ip转换函数,主机字节序转化为网络字节序st_LocalAddr.sin_addr.s_addr = server_addr;//绑定地址结构体和socketif(0 > bind(sockfd, (struct sockaddr *)&st_LocalAddr, sizeof(st_LocalAddr))) {perror("bind");return -1;}//开启监听 ,第二个参数是最大监听数if(0 > listen(sockfd, max_listen_num)) {perror("listen");return -1;}return 0;
}/**
函数功能:tcp阻塞等待客户端连接
入参:sockfd:socket通信句柄;
出参:无
返回:成功:与客户端连接后的新句柄,失败:-1
**/
int tcp_server_wait_connect(int sockfd)
{int new_sockfd = 0;//建立连接后的句柄struct sockaddr_in st_RemoteAddr = {0}; //对方地址信息socklen_t socklen = 0;  //在这里阻塞直到接收到连接,参数分别是socket句柄,接收到的地址信息以及大小 new_sockfd = accept(sockfd, (struct sockaddr *)&st_RemoteAddr, &socklen);if(0 > new_sockfd) {perror("accept");return -1;}return new_sockfd;
}/**
函数功能:服务端每接收到新的客户端连接,就创建新线程提供服务
入参:new_sockfd:客户端连接上服务端后产生的新socket句柄
入参:callBackFun:处理客户端消息的回调函数
出参:无
返回:无
**/
void tcp_server_creat_pthread_process_client(int *new_sockfd, void* (*callBackFun)(void*))
{pthread_t thread_id;int ret = 0;pthread_create(&thread_id, NULL, callBackFun, (void *)new_sockfd);pthread_detach(thread_id);//将线程分离, 线程结束后自动释放线程资源,后续不需要使用pthread_join()进行回收
}//callBackFun:处理客户端消息的回调函数,示例
void *tcp_server_callBackFun_demo(void *ptr)
{//int new_sockfd = (int *)ptr;//错误,不能直接使用地址,防止外部地址数值改变int new_sockfd = *(int *)ptr;printf("新建线程处理客户端服务(new_sockfd=%d)\n", new_sockfd);char recv_buff[1024] = {0};int recv_len = 0;char *str = NULL;while (1) {memset(recv_buff, 0, sizeof(recv_buff));recv_len = tcp_blocking_rcv(new_sockfd, recv_buff, sizeof(recv_buff));//堵塞接收消息if(0 > recv_len) {printf("接收客户端消息失败(new_sockfd=%d)!\n", new_sockfd);break;} else if(0 == recv_len) {printf("客户端断开连接(new_sockfd=%d)\n", new_sockfd);break;} else {printf("接收客户端消息(new_sockfd=%d):%s\n", new_sockfd, recv_buff);str = (char *)"服务端已收到";tcp_send(new_sockfd, str, strlen(str));}//usleep(1*1000);}tcp_close(new_sockfd);printf("退出线程服务(new_sockfd=%d)\n", new_sockfd);return NULL;
}

4、客户端通信main_client_demo.c

/*************************************************
Function:tcp 客户端进程,服务器中可运行多个
author:zyh
date:2020.4
**************************************************/
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
#include"tcp_spi.h"int sockfd = -1;//客户端socket通信句柄
int tcp_client_connectflag = 0;//客户端socket通信连接标志//开启一个接收消息的线程
void *fun_client_rcv(void *ptr)
{char recvBuf[1024] = {0};int recvBytes = 0;while (1) {if (1 == tcp_client_connectflag) {//堵塞接收memset(recvBuf, 0, sizeof(recvBuf));//清空recvBytes = tcp_blocking_rcv(sockfd, recvBuf, sizeof(recvBuf));//堵塞接收if (0 > recvBytes) {//接收失败printf("接收失败\n");tcp_client_connectflag = 0;} else if (0 == recvBytes) {//断开了连接printf("已断开连接\n");tcp_client_connectflag = 0;} else {printf("接收到消息:%s\n", recvBuf);}} else {sleep(1);}}return NULL;
}//开启一个发送消息的线程
void *fun_client_send(void *ptr)
{char msg_buf[1024] = {0};while (1) {if (1 == tcp_client_connectflag) {//连接成功printf("\n请输入要发送的消息:\n");scanf("%s", msg_buf);printf("正在发送\n");if (0 > tcp_send(sockfd, msg_buf, strlen(msg_buf))) {//如果含有0x00不能用strlenprintf("发送失败...!\n");tcp_client_connectflag = 0;} else {printf("发送成功\n");}sleep(1);} else {sleep(1);}}return NULL;
}int main(int argc, char *argv[])
{char server_ip[16] = {0};//服务器IPint server_port = 0;//服务器端口int ret = 0;pthread_t thread_client_rcv, thread_client_send;//创建一个接收消息线程ret = pthread_create(&thread_client_rcv, NULL, fun_client_rcv, NULL);if (ret < 0) {printf("creat thread_client_rcv is fail!\n");return -1;}ret = pthread_create(&thread_client_send, NULL, fun_client_send, NULL);if (ret < 0) {printf("creat fun_client_send is fail!\n");return -1;}printf("请输入服务器ip:\n");scanf("%s", server_ip);printf("请输入服务器端口:\n");scanf("%d", &server_port);while (1) {if (0 == tcp_client_connectflag) {//未连接就不断中断重连if (sockfd > 0) {  //sockfd等于0时不能关,防止把文件句柄0关掉,导致scanf()函数输入不了tcp_close(sockfd);}sockfd = tcp_creat_socket();//创建socketif (0 > sockfd) {printf("socket创建失败...!\n");sleep(2);continue;}printf("请求连接...\n");if (0 > tcp_client_connect(sockfd, server_ip, server_port)) {printf("连接失败...重连中...\n");sleep(2);continue;} else {tcp_client_connectflag = 1;printf("连接成功!\n");}  } else {sleep(1);}}tcp_close(sockfd);pthread_join(thread_client_rcv, NULL);pthread_join(thread_client_send, NULL);return 0;
}

5、服务端通信main_server_demo.c

/*************************************************
Function:tcp 服务端进程,服务器中运行只一个
author:zyh
date:2020.4
**************************************************/
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
#include"tcp_spi.h"void *tcp_server_callBackFun(void *ptr)
{//int new_sockfd = (int *)ptr;//错误,不能直接使用地址,防止外部地址数值改变int new_sockfd = *(int *)ptr;printf("开启线程服务处理客户端(new_sockfd=%d)\n", new_sockfd);char recv_buff[1024] = {0};int recv_len = 0;char *str = NULL;while (1) {memset(recv_buff, 0, sizeof(recv_buff));recv_len = tcp_blocking_rcv(new_sockfd, recv_buff, sizeof(recv_buff));//堵塞接收消息if(0 > recv_len) {printf("接收客户端消息失败(new_sockfd=%d)!\n", new_sockfd);break;} else if(0 == recv_len) {printf("客户端断开连接(new_sockfd=%d)\n", new_sockfd);break;} else {printf("收到客户端消息(new_sockfd=%d):%s\n", new_sockfd, recv_buff);str = (char *)"服务端已收到";tcp_send(new_sockfd, str, strlen(str));}//usleep(1*1000);}tcp_close(new_sockfd);printf("退出线程服务(new_sockfd=%d)\n", new_sockfd);return NULL;
}int main(int argc, char *argv[])
{int ret = 0;int sockfd = -1;sockfd = tcp_creat_socket();//创建socketif (0 > sockfd) {printf("socket创建失败...!\n");return -1;}int port = 2022;char *ip = (char *)"127.0.0.1";ret = tcp_server_bind_and_listen(sockfd, ip, port, 1024);if (0 > ret) {printf("bind_and_listen失败...!\n");tcp_close(sockfd);return -1;}printf("服务端ip=localHost, 端口=%d\n", port);int new_sockfd = -1;while (1) {if (0 > (new_sockfd = tcp_server_wait_connect(sockfd))) {//堵塞直到客户端连接printf("等待连接失败...!\n");continue;} else {printf("\n有客户端连接成功! new_sockfd=%d\n", new_sockfd);tcp_server_creat_pthread_process_client(&new_sockfd, tcp_server_callBackFun);//服务端每接收到新的客户端连接,就创建新线程提供服务}}tcp_close(sockfd);return 0;
}

超详细c语言简化tcp通信接口(多线程实现一个服务端处理多个客户端服务)相关推荐

  1. python实现淘宝客服自动回复语_Python+Socket实现基于TCP协议的客户与服务端中文自动回复聊天功能示例...

    本文实例讲述了Python+Socket实现基于TCP协议的客户与服务端中文自动回复聊天功能.分享给大家供大家参考,具体如下: [吐槽] 网上的代码害死人,看着都写的言之凿凿,可运行就是有问题. 有些 ...

  2. java 网络编程(二) tcp传输实现客户端和服务端进行信息交流

    1.使用Tcp从一台电脑往另一台电脑上发送文本数据 客户端: import java.io.*; import java.net.*; /**** 客户端,* 通过查阅socket对象,发现在该对象建 ...

  3. 渡一教育_Java每日一练:建立Statement的作用是什么、前端Console.log( Boolean(‘‘))输出的是什么、如果希望1监听TCP端口为9000,服务端应该怎样创建socket

    系列文章目录 文章目录 系列文章目录 题目1java部分建立Statement的作用是什么(答案在最后公布) 题目1 - 答案 ==解析== ==答案== 题目2 前端 js部分 ==答案== 题目3 ...

  4. 基于TCP/IP协议的Java服务端与Android客户端的Socket通信及数据交互

    基于TCP/IP协议的Java服务端与Android客户端的Socket通信及数据交互 一.前言 1.Java服务端程序代码的项目名为TcpSocketServerOfJava,包名为com.exam ...

  5. TCP聊天文件服务器v2.2 - 服务端客户端套接字解决分包/粘包问题 - SocketQueue继承以及减少冗余

    TCP聊天+传输文件服务器服务器套接字v2.2 整个图当封面吧 所有版本记录: v1.0 : TCP聊天服务器套接字|PyQt5+socket(TCP端口映射+端口放行)+logging+Thread ...

  6. OSI七层、TCP/IP五层、UDP、TCP的socket编程(服务端及客户端)、字节序转换、多进程以及多线程服务端的实现

    1.网络以覆盖范围划分:局域网/城域网/广域网   互联网/因特网   以太网/令牌环网--组网方式 2.在网络中必须能够为一表示每一台主机,才能实现点到点的精确通信            IP地址: ...

  7. 超详细的建站流程,如何建立一个网站

    好了,现在言归正传.建一个网站,大概分为以下几个步骤: 1. 申请域名 例如http://www.xxx.com,http://www.xxx.cn等等这样的域名,后面别人访问你的网站都是通过这个域名 ...

  8. http协议与https协议+UDP协议和TCP协议+WebSocket协议下服务端主动去发送信息+对称加密与非对称加密+get和post请求方式区别详解+浏览器内核以及jsj解析引擎

    TCP和UDP协议是TCP/IP协议的核心. 在TCP/IP网络体系结构中,TCP(传输控制协议,Transport Control Protocol).UDP(用户数据报协议,User Data P ...

  9. 【学习笔记】在windows下进行基于TCP的本地客户端和服务端socket通信

    文章目录 socket介绍 java中使用socket 基于tcp的socket通信 使用ServerSocket类创建一个web服务器:(java) windows下的基于tcp的socket编程( ...

最新文章

  1. R语言数据类型:Logical、Numeric、Integer、Complex、Character、Vectors、Lists、Matrices、Arrays、Factors、DataFrames
  2. 虚幻4皮肤材质_虚幻周报20200721 | CJ就要开始啦~
  3. Tomcat中出现“RFC 7230 and RFC 3986“错误的解决方法
  4. Andriod的Http请求获取Cookie信息并同步保存,使第二次不用登录也可查看个人信息...
  5. python csdn博客_利用Python抓取CSDN博客
  6. html基础知识点列表
  7. linux内存分配器类型,内核早期内存分配器:memblock
  8. phpcms2008里的if判断
  9. simpledateformat格式_大厂都是怎么用Java8代替SimpleDateFormat?
  10. 配置使用Powershell管理Azure Stack
  11. 创建数独小游戏uniapp/vue
  12. css3新单位vw、vh、vmin、vmax的使用介绍
  13. 更强大、更灵活、更全面丨一文搞懂DolphinDB窗口计算
  14. SpringMVC入门
  15. 【收藏】JavaWeb项目详解:水果库存后台管理系统(servlet+thymeleaf+mysql)
  16. python 期末考
  17. 极智AI | 昇腾 CANN ATC 模型转换
  18. Java 将网络图片URL 转为file文件
  19. taobao 登录功能代码 淘宝
  20. 武器装备作战效能评估系统软件常用方法

热门文章

  1. 2、Pandas练习
  2. Atmega328P熔丝设置错误修复
  3. 个人理财小助手 —— 简介
  4. Django实现adminx后台关闭顶部的搜索栏
  5. 【IoT】创业:内容运营 - 戴上写作的六顶思考帽
  6. php练手项目20个,2022最新版 附源码
  7. 中小型微服务系统 硬件设备如何部署,QPS大概多少
  8. MAC os, Google drive 登录故障排除 (There was a problem signing in)
  9. 【并行编程】双笙子佯谬 - 高性能并行编程与优化 - 视频教程目录
  10. html5 style设置字体,初识HTML(5)+CSS(3)-2020升级版 - font-style:设置字体样式,3种