Linux下编写TCP服务器调用的函数顺序为:socket -> bind -> listen -> accept -> recv/send

socket

  参见:http://c.biancheng.net/view/2131.html

  socket函数成功返回文件描述符,失败返回-1

bind

  参见:http://c.biancheng.net/view/2344.html

  需要注意的是,bind函数的第二个参数类型为struct sockaddr *,但用的时候经常传入struct sockaddr_in *类型的参数,具体原因参考网页中有说。并且如果使用的结构是struct sockaddr_in,那么需要包含netinet/in.h。

  struct sockaddr_in结构体中的成员变量参考网页中也有说,需要注意其中的sin_port成员表示端口,需要用htons函数转换,htons函数说明如下:https://blog.csdn.net/zhuguorong11/article/details/52300680

  sin_addr.s_addr也需要用inet_addr函数转换,需要注意的是,给sin_addr.s_addr赋值为0表示绑定本机IP,这也是常用的写法。

  第三个参数类型为socklen_t,直接传入sizeof(struct sockaddr_in)即可。

  返回值:成功返回0,失败返回-1。

  如果程序在acctpt之后非正常退出(ctrl+c),下次bind可能会失败,需要等待一段时间恢复。

listen和accept

  参见:http://c.biancheng.net/view/2345.html

  listen函数成功返回0,失败返回-1

  而accept成功返回一个文件描述符,失败返回-1,这个文件描述符就是可以用recv和send函数通信的文件描述符

  accept函数默认会阻塞,直到有客户端来连接。

  accept函数的第2个参数是输出参数,用来得到客户端的地址和端口

  第三个参数是输入参数,指定的是第二个参数占用的空间大小,即sizeof(struct sockaddr_in)。

  特别注意第三个参数是输入参数而不是输出参数,我在一次测试就遇到了这个问题,我在想如果第三个参数如果是输入,那为什么要传指针类型,因此我将第三个参数指向的值设为0,结果accept运行返回OK了,并且打印第三个参数的值也变成了16。但是打印客户端的IP地址却变成了0.0.0.0,端口号也是0。因此在accept的时候传入的第三个参数指向的值应为sizeof(struct sockaddr_in)。

  accept执行成功后可以用inet_ntoa函数将IP地址以字符串的方式取出, inet_ntoa的函数原型为:char *inet_ntoa(struct in_addr in);

  有一点要特别注意,如果使用了inet_ntoa函数,那么就必须包含arpa/inet.h头文件,否则当你用一个char *类型的变量去接收inet_ntoa函数的返回值时会报一个很奇怪的警告,意思就是inet_ntoa函数返回的是一个int型,但是我用man手册反反复复看了不下5遍,inet_ntoa函数返回的就是char *类型,最后网上找资料发现是必须要包含arpa/inet.h头文件。不过有趣的是,如果我不包含arpa/inet.h头文件,而就用一个int类型的变量来接收inet_ntoa的返回值,结果编译还不会报任何警告和错误,运行程序时还真的能得到一个int类型的值。

  用ntohs取出端口号,ntohs的函数原型为uint16_t ntohs(uint16_t netshort);

recv和send

  recv用于接收数据,函数原型为:ssize_t recv(int sockfd, void *buf, size_t len, int flags);

  sockfd:套接字描述符,注意该描述符不是socket函数返回的描述符,而是accept函数返回的描述符。

  buf:用于存放数据的缓冲区
  len:希望接收的最大字节个数

  flags:一般设置为0,具体描述在man手册中可以看到

  send用于发送数据,函数原型为:ssize_t send(int sockfd, const void *buf, size_t len, int flags);

  sockfd:套接字描述符,注意该描述符不是socket函数返回的描述符,而是accept函数返回的描述符。

  buf:用于存放数据的缓冲区
  len:希望发送的最大字节个数

  flags:一般设置为0,具体描述在man手册中可以看到

  如果客户端断开连接,那么recv函数将不阻塞,返回值为0,可以通过这个方法判断客户端是否断开连接。

  接收数据可以用read函数代替,发送数据也可以用send函数代替。

例程:

tcp_server.c

  1  /**
2 * filename: tcp_server.c
3 * author: Suzkfly
4 * date: 2021-01-22
5 * platform: Ubuntu
6 * 配合windows的网络调试工具使用:
7 * 1、先保证windows与Ubuntu在同一网段且互相能ping通;
8 * 2、在windows下打开网络调试助手,选择协议类型为TCP Client,远程主机地址为
9 * Ubuntu的IP地址,远程主机端口为Ubuntu例程中写的端口,接收设置和发送设
10 * 置都选择ASCLL。
11 * 3、运行Ubuntu下的TCP服务器程序;
12 * 4、网络调试助手上点击“连接”。
13 * 5、连接成功后在网络调试助手上发送数据,在Ubuntu下的终端上能看到,
14 * 在Ubuntu下的终端上输入字符串按回车发送,在windows上的网络调试助手上也
15 * 能看到。
16 */
17 #include <stdio.h>
18 #include <sys/types.h>
19 #include <sys/socket.h>
20 #include <string.h>
21 #include <netinet/in.h>
22 #include <arpa/inet.h>
23 #include <errno.h>
24
25 //#define IP_ADDR "127.0.0.1" /* IP地址 */
26 #define PORT 10000 /* 端口号 */
27
28 int main(int argc, const char *argv[])
29 {
30 int sock_fd = 0, confd = 0;
31 struct sockaddr_in serv_addr; /* 服务器IP(本机IP) */
32 struct sockaddr_in client_addr; /* 客户端IP(连接者IP) */
33 socklen_t addr_len = sizeof(struct sockaddr_in);
34 int ret = 0; /* 用于接收函数返回值 */
35 int pid = 0;
36 char buf[128] = { 0 }; /* 用于存放数据的缓冲区 */
37 int len = 0; /* 发送和接收数据的长度 */
38
39 /* 创建TCP套接字 */
40 sock_fd = socket(AF_INET, SOCK_STREAM, 0);
41
42 /* 将套接字与IP和端口绑定 */
43 memset(&serv_addr, 0, sizeof(struct sockaddr_in));
44 serv_addr.sin_family = AF_INET;
45 //serv_addr.sin_addr.s_addr = inet_addr(IP_ADDR); /* 绑定IP */
46 serv_addr.sin_addr.s_addr = 0; /* 绑定0就是绑定自己 */
47 serv_addr.sin_port = htons(PORT); /* 端口号 */
48 ret = bind(sock_fd, (struct sockaddr*)&serv_addr, sizeof(struct sockaddr_in));
49 if (ret == 0) {
50 printf("bind ok\n");
51 } else {
52 printf("bind failed\n");
53 close(sock_fd);
54 return 0;
55 }
56
57 /* 让套接字进入被动监听状态 */
58 ret = listen(sock_fd, 10);
59 if (ret == 0) {
60 printf("listen ok\n");
61 } else {
62 printf("listen failed\n");
63 close(sock_fd);
64 return 0;
65 }
66
67 re_connect:
68
69 /* 接收客户端请求(阻塞) */
70 memset(&client_addr, 0, sizeof(client_addr));
71 printf("accept...\n");
72 confd = accept(sock_fd, (struct sockaddr *)&client_addr, &addr_len);
73 if (confd > 0) {
74 printf("accept ok\n");
75 } else {
76 printf("accept failed\n");
77 close(sock_fd);
78 return 0;
79 }
80
81 /* 打印客户端信息 */
82 printf("addr_len = %d\n", addr_len);
83 printf("Client IP: %s\n", inet_ntoa(client_addr.sin_addr)); /* IP地址 */
84 printf("Client Port:%d\n", ntohs(client_addr.sin_port)); /* 端口号 */
85
86 pid = fork();
87
88 if (pid > 0) { /* 接收数据 */
89 while (1) {
90 memset(buf, 0, sizeof(buf));
91 len = recv(confd, buf, sizeof(buf), 0);
92 //len = read(confd, buf, sizeof(buf));
93 if (len == 0) { /* 如果recv返回0,则表示远端断开连接 */
94 goto re_connect;
95 }
96 printf("len = %d\n", len);
97 printf("data: %s\n", buf);
98 }
99 } else if (pid == 0) {
100 while (1) { /* 发送数据 */
101 memset(buf, 0, sizeof(buf));
102 scanf("%s", buf);
103 len = send(confd, buf, strlen(buf), 0);
104 //len = write(confd, buf, strlen(buf));
105 }
106 }
107 }

该测试程序有一个BUG,如果客户端断开连接,那么recv返回0,则会跳转到代码第67行重新连接,如果连接成功,则在第86行又会调用fork函数,创建出一个新的进程。在测试时发现,如果客户端断开连接并重新连接后的一端时间内,在服务器终端中输入的数据在网络调试助手中收不到。

网络调试助手设置如下:

注意:

例程中如果使用windows的网络调试工具,如果在绑定的时候指定回环网卡,则在windows上的网络调试助手连接不上。

  

TCP服务器程序的更多相关文章

  1. TCP客户端程序

    TCP客户端程序的函数调用顺序为:socket -> connect -> send/recv socket.send和recv函数在TCP服务器程序中已经说过了,这里就不赘述了. con ...

  2. UNIX网络编程---TCP客户/服务器程序示例(五)

    一.概述 客户从标准输入读入一行文本,并写给服务器 服务器从网络输入读入这行文本,并回射给客户 客户从网络输入读入这行回射文本,并显示在标准输出上 二.TCP回射服务器程序:main函数 这里给了函数 ...

  3. 在向服务器发送请求时发生传输级错误。 (provider: TCP 提供程序, error: 0 - 远程主机强迫关闭了一个现有的连接。)

    用VS2005+SQLSERVER2008开发C/S的程序,程序上线运行一段时间之后发现在某些功能偶尔出现如下的错误: 在向服务器发送请求时发生传输级错误. (provider: TCP 提供程序, ...

  4. UNIX网络编程——使用select函数的TCP和UDP回射服务器程序

    服务器程序: #include <sys/wait.h> #include <string.h> #include <string.h> #include < ...

  5. 【Unix网络编程】 chapter5 TCP客户,服务器程序实例

    chapter5 5.1 概述 5.2 TCP回射服务器程序:main函数 int main(int argc, char **argv) { int listenfd,connfd; pid_t c ...

  6. UNIX网络编程 第5章 TCP客户/服务器程序示例

    UNIX网络编程 第5章 TCP客户/服务器程序示例

  7. TCP客户/服务器程序实例——回射服务器

    目录 客户/服务器程序源码 POSIX信号处理 POSIX信号语义 处理SIGCHLD信号 处理僵死进程 处理被中断的系统调用 wait和waitpid函数 wait和waitpid函数的区别 网络编 ...

  8. System.Data.SqlClient.SqlException: 在向服务器发送请求时发生传输级错误。 (provider: TCP 提供程序, error: 0 - 远程主机强迫关闭了一个现有的连接。) .

    今天使用sql server 2008 R2管理器,进行SQL查询时,频率非常高的报错: System.Data.SqlClient.SqlException: 在向服务器发送请求时发生传输级错误. ...

  9. 第四章 基本TCP套接字编程 第五章 TCP客户/服务器程序实例

    TCP客户与服务器进程之间发生的重大事件时间表 TCP服务器 socket() --- bind() --- listen() --- accept() --- read() --- write -- ...

随机推荐

  1. 第 13 章 StringTable详解

    目录 第 13 章 StringTable 1.String 的基本特性 1.1.String 概述 1.2.String 的基本特征 1.3.String 的底层结构 2.String 的内存分配 ...

  2. 5、Mycat安全

    1.权限配置 [1].user标签 目前 Mycat 对于中间件的连接控制并没有做太复杂的控制,目前只做了中间件逻辑库级别的读写权限控制.是通过 server.xml 的 user 标签进行配置. # ...

  3. 精尽Spring MVC源码分析 - LocaleResolver 组件

    该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...

  4. VS中RDLC提示类型不一致

    错误"基类包括字段"XXXXXXX",但其类型(Microsoft.Reporting.WebForms.ReportViewer)与控件(Microsoft.Repor ...

  5. 服务器挂掉导致需要对k8s新增节点

    [导读]今日是周六,本想午休起来之后写篇有关kubernetes service的文章,没想到两台虚拟机接连挂掉,所以又重新创建了两台虚拟机.新建时又一直黑屏,所以只能克隆. 由于虚拟机是新建的,所以 ...

  6. 私有Maven仓库安装和使用

    安装 这里选择安装的是windows平台,linux安装类似,下载可能要梯子 官网下载页 https://help.sonatype.com/repomanager2/download 下载链接 ht ...

  7. 支付业务优化else if 代码

    背景 最近在做项目的时候,需要接入支付.由于接入第三方支付而且还不知止一家,需要接入很多家.比如说支付宝.微信.富友支付等.每家支付都一个回调.现如今的代码,根据不同的第三方支付一大堆else if判 ...

  8. python +pycharm+selenium 环境搭建

    一:首先安装python:   https://www.python.org/downloads/: 下载完后一步步的点击安装,验证是否安装成功:打开win+r 打开cmd命令,输入python -V ...

  9. 总结JAVA语言的十大特性

    JAVA语言的十大特性 1.简单 Java语言的语法简单明了,容易掌握从,而且Java语言是纯面向对象的语言. Java语言的语法规则和C++类似,从某种意义上来讲,Java原因是由C语言和C++语言 ...

  10. Java学习日报10.1

    学习内容一 ********************************** 代码 **********************************public class EnumTest ...