文章目录

  • 客户端通信步骤
  • 为什么客户端没有bind和listen
  • 客户端connect函数介绍
  • 局域网内客户端和服务器通信代码实例
客户端通信步骤

根据基本TCP网络通信编程模型

我们可以知道客户端的实现主要有几个步骤

  1. socket创建客户端通信的套接字文件,并指定通信的协议族和数据类型
  2. 使用connect主动向服务器发起连接请求,与服务器的accept实现三次握手建立连接。连接成功之后客户端可以通过socket返回的文件描述符进行通信 ,服务器使用accept返回的通信文件描述符进行通信
  3. 使用send/recv和服务器 发送接收数据进行通信
  4. 通信结束之后断开连接close或者shutdown
为什么客户端没有bind和listen

关于bind函数的功能,是为了绑定ip和端口号,即通信使用固定的ip和端口号进行通信。这样的要求仅仅是针对tcp服务器而言的,对于多个客户端来说,这样的bind操作是不需要的。如果指定bind也是可以通信,但是并不合理。在通信过程中只需要使用自己的ip和默认分配的端口号即可。客户端默认分配的端口范围为49152~65535

关于listen函数,对于客户端程序,它永远是主动连接请求的发起者,没有被动监听别人连接的需求。所以这里不需要将主动通信文件描述符转换为被动通信文件描述符。

客户端connect函数介绍
  • 头文件:#include <sys/socket.h>
  • 函数使用:int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
  • 函数功能: 向服服务器发起主动连接请求
  • 返回值:成功0;失败 -1,并设置errno。
  • 函数参数:
    a. sockfd socket返回的套接字文件描述符
    b. addrlen addr结构体的大小
    c. addr 用于设置想要连接的服务器的ip和端口号
    注意:如果局域网内部通信,则ip和端口号可以直接填写服务器的ip和端口号,但是如果是跨网通信,IP必须是服务器所在路由器的公网ip
    为了方便设置struct sockaddr结构体的内容,我们同样使用sockaddr_in结构体进行设置

    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(6789);
    addr.sin_addr.s_addr = inet_addr("127.0.0.1");cfd = connect(sockfd,(struct sockaddr *)&addr,sizeof(addr));
    
局域网内客户端和服务器通信代码实例

一下代码是在本机测试,大家可以测试局域网内以及不同网段的通信,不过需要注意IP地址的填写,跨网段通信中ip的填写需要保证是服务器所在路由器的公网ip。
server.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <errno.h>
#include <pthread.h>
#include <signal.h>#define IP "192.168.102.174"
#define PORT 7000typedef struct data {char name[30];unsigned int num;
}Data;
void print_err(char *str, int line, int err_no) {printf("%d, %s :%s\n",line,str,strerror(err_no));_exit(-1);
}
int cfd = -1;//子线程,处理数据的接收
void *receive(void *pth_arg) {int ret = 0;Data stu_data = {0};while(1) {bzero(&stu_data, sizeof(stu_data));ret = recv(cfd, &stu_data, sizeof(stu_data),0);   if (-1 == ret) {print_err("recv failed",__LINE__,errno);}else if (ret > 0)printf("student number = %d student name = %s \n",ntohl(stu_data.num),stu_data.name);}
}//接收SIGINT信号,从而关闭通信描述符。
void sig_fun(int signo) {if (signo == SIGINT) {//close(cfd);shutdown(cfd,SHUT_RDWR);_exit(0);}
}int main()
{int skfd = -1, ret = -1;skfd = socket(AF_INET, SOCK_STREAM, 0);if ( -1 == skfd) {print_err("socket failed",__LINE__,errno);}struct sockaddr_in addr;addr.sin_family = AF_INET; //设置tcp协议族addr.sin_port = htons(PORT); //设置端口号addr.sin_addr.s_addr = inet_addr(IP); //设置ip地址ret = bind(skfd, (struct sockaddr*)&addr, sizeof(addr));if ( -1 == ret) {print_err("bind failed",__LINE__,errno);}ret = listen(skfd, 3);if ( -1 == ret ) {print_err("listen failed", __LINE__, errno);}/*被动监听客户端发起的tcp连接请求,三次握手后连接建立成功*/struct sockaddr_in caddr = {0};int csize = sizeof(caddr);cfd = accept(skfd, (struct sockaddr*)&caddr, &csize);if (-1 == cfd) {print_err("accept failed", __LINE__, errno);}//打印连接后接收到的客户端发送过来的ip和端口号printf("cport = %d, caddr = %s\n", ntohs(caddr.sin_port),inet_ntoa(caddr.sin_addr));创建子线程用于接收数据pthread_t id;ret = pthread_create(&id,NULL,receive,NULL);if (-1 == ret) print_err("pthread_create failed", __LINE__, errno);//用于处理信号函数,处理SIGINT的信号signal(SIGINT,sig_fun);Data std_data = {0};while (1) {bzero(&std_data, sizeof(std_data));printf("stu name:\n");scanf("%s",std_data.name);//对于int型的需要将主机端序转换为网络端序,这里转成long型。printf("stu num:\n");scanf("%d",&std_data.num);std_data.num = htonl(std_data.num);将数据std_data强制类型转换后发送ret = send(cfd, (void *)&std_data,sizeof(std_data),0);if ( -1 == ret) {print_err("accept failed", __LINE__, errno);}  }return 0;
}

client.c
客户端的代码和服务器端主要差异在连接上,客户端只需要初始化一下socket即可主动发起连接请求,服务端在初始化socket之后需要绑定ip和端口号,同时将主动通信的文件描述符转为被动通信的文件描述符。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <errno.h>
#include <pthread.h>
#include <signal.h>#define IP "192.168.102.174"
#define PORT 7000typedef struct data {char name[30];unsigned int num;
}Data;
void print_err(char *str, int line, int err_no) {printf("%d, %s :%s\n",line,str,strerror(err_no));_exit(-1);
}
int skfd = -1;
void *receive(void *pth_arg) {int ret = 0;Data stu_data = {0};while(1) {bzero(&stu_data, sizeof(stu_data));ret = recv(skfd, &stu_data, sizeof(stu_data),0);  if (-1 == ret) {print_err("recv failed",__LINE__,errno);}else if (ret > 0)printf("student number = %d student name = %s \n",ntohl(stu_data.num),stu_data.name);}
}void sig_fun(int signo) {if (signo == SIGINT) {//close(cfd);shutdown(skfd,SHUT_RDWR);_exit(0);}
}int main()
{int ret = -1;skfd = socket(AF_INET, SOCK_STREAM, 0);if ( -1 == skfd) {print_err("socket failed",__LINE__,errno);}/*这里需要填写服务器的端口号和IP地址,此时客户端会自己分配端口号和自己的IP发送给服务器*/struct sockaddr_in addr;addr.sin_family = AF_INET; //设置tcp协议族addr.sin_port = htons(PORT); //设置服务器端口号addr.sin_addr.s_addr = inet_addr(IP); //设置服务器ip地址,如果是跨网,则需要填写服务器所在路由器的公网ipret = connect(skfd,(struct sockaddr *)&addr,sizeof(addr));if ( -1 == ret ) {print_err("connect failed", __LINE__, errno);}pthread_t id;pthread_create(&id,NULL,receive,NULL);Data std_data = {0};while (1) {bzero(&std_data, sizeof(std_data));printf("stu name:\n");scanf("%s",std_data.name);printf("stu num:\n");scanf("%d",&std_data.num);std_data.num = htonl(std_data.num);ret = send(skfd, (void *)&std_data,sizeof(std_data),0);if ( -1 == ret) {print_err("send failed", __LINE__, errno);}  }return 0;
}

编译gcc server.c -o server -pthread
gcc client.c -o client -pthread
运行如下:

C语言网络编程:TCP客户端实现相关推荐

  1. java基础—网络编程——TCP客户端与服务端交互

    import java.net.InetAddress; import java.net.Socket; import java.net.UnknownHostException; import ja ...

  2. python网络编程---TCP客户端

    0x01 环境 python2. pycharm 0x02 程序 # -*- coding:UTF-8 -*- import sockettarget_host='www.baidu.com' tar ...

  3. C语言网络编程:多路IO select实现多客户端

    文章目录 阻塞式的服务器程序 多线程服务器程序 非阻塞式服务器程序 基于事件响应的服务器程序 事件响应服务器程序的实现`select` 阻塞式的服务器程序 我们接触过最多的最基础的网络通信模型为TCP ...

  4. Qt:Qt实现Winsock网络编程—TCP服务端和客户端通信(多线程)

    Qt实现Winsock网络编程-TCP服务端和客户端通信(多线程) 前言 感觉Winsock网络编程的api其实和Linux下网络编程的api非常像,其实和其他编程语言的网络编程都差不太多.博主用Qt ...

  5. java socket ip_JAVA 网络编程 TCP/IP、Socket 和协议设计

    [JAVA 网络编程 TCP/IP.Socket 和协议设计] TCP/IP 协议简介 IP 首先我们看 IP(Internet Protocol)协议.IP 协议提供了主机和主机间的通信. 为了完成 ...

  6. C语言网络编程函数与相关结构汇总

    持续更新中- 服务器和客户端的一般流程 服务器端:socket()-->bind( )-->listen()-->accept()-->read()/write()---> ...

  7. C语言网络编程(3)— 通过DNS连接到百度

    C语言网络编程(3)- 通过DNS连接到百度 一.gethostbyname()函数 我们现在认知一台计算机主机通常采用直观可读的名字.例如百度我们会记住 www.baidu.com 而不是他的IP地 ...

  8. WinSock API网络编程——TCP/IP协议详解

     WinSock API网络编程--TCP/IP协议(http://www.impcas.ac.cn/usr/lujun/browse.asp?id=winsock_tcp)            ...

  9. 网络编程-TCP/IP协议栈-TCP协议

    TCP协议 TCP协议作用 TCP协议位于协议栈的传输层.当应用层向TCP层发送用于网间传输的,用8字节表示的数据流,TCP则吧数据流分割成适当长度的报文段,最大传输段大小(MSS)通常受到改计算机连 ...

  10. qt android 网络编程实例,QT网络编程Tcp下C/S架构的即时通信实例

    先写一个客户端,实现简单的,能加入聊天,以及加入服务器的界面. #ifndef TCPCLIENT_H #define TCPCLIENT_H #include #include #include # ...

最新文章

  1. R语言使用ggplot2包使用geom_violin函数绘制分组小提琴图(自定义分组的次序)实战
  2. 大数据的逆袭:传统数据库市场的变革
  3. hashmap移除元素_Java HashMap 如何正确遍历并删除元素的方法小结
  4. Git - 修改用户名
  5. Redis管道(Pipeline)详解
  6. 整数的分类处理c语言switch,整数的分类处理
  7. Centos 7 更改系统语言为中文
  8. 博思得标签打印机驱动_惠普LaserJet 5200n驱动-惠普HP LaserJet 5200n打印机驱动下载 v61.074.561.43官方版...
  9. python结构体数组传出接收c动态库_使用Python向C语言的链接库传递数组、结构体、指针类型的数据...
  10. 蓝牙学习笔记(一)——蓝牙相关概念和术语整理(小白版)
  11. BZOJ2795: [Poi2012]A Horrible Poem
  12. matlab晶格图,科学网-MATLAB软件绘制一维双原子晶格的格波色散曲线-李金磊的博文...
  13. 咪咕音乐HTML代码,咪咕音乐播放器如何更换皮肤和底纹技术分享
  14. 边境的悍匪—机器学习实战:第六章 决策树
  15. ORGE之GUI Demo
  16. RAID磁盘阵列是什么
  17. Apache ServiceComb社区常见问题解答问答精选(第一期)
  18. pvifa怎么用计算机算,年金现值系数表【完整版】
  19. 华为p9 html尺寸,华为P9 Plus的屏幕尺寸是多少
  20. 我看2008—IT之最

热门文章

  1. HTML页面加载和解析流程详细介绍
  2. 计算机科学NIP,NIP自然语言处理主要应用在哪些领域呢?
  3. java十个整数相反顺序_编写程序,对输入的一个整数,按相反顺序输出该数。例如,输入为 3578, 输出为 8753。...
  4. mybatis参数有list和实体类_Mybatis的几种传参方式,你了解吗?
  5. upgrade lnmpa php.sh,LNMP 状态管理命令说明及Nginx、MySQL/MariaDB、PHP升级教程
  6. 嵌入式linux应用程序升级,基于嵌入式Linux平台的应用升级机制的研究与设计
  7. Python,OpenCV图像金字塔cv2.pyrUp(), cv2.pyrDown()
  8. 力扣(LeetCode)刷题,简单题(第3期)
  9. ROS系统——部署OpenVINO版Nanodet超轻量目标检测器
  10. Linux那些事儿之我是Sysfs(2)linux设备底层模型