linux的tcp服务端、客户端例子

本文详细介绍了TCP编程中服务器端和客户端的基本步骤,并通过示例代码展示了如何实现多用户连接的处理,包括信号处理、读写操作的中断处理及客户端的交互过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

TCP编程的服务器端一般步骤是:


1、创建一个socket,用函数socket()
2、设置socket属性,用函数setsockopt(); * 可选
3、绑定IP地址、端口等信息到socket上,用函数bind();
4、开启监听,用函数listen()
5、接收客户端上来的连接,用函数accept()
6、收发数据,用函数send()recv(),或者read()write();
7、关闭网络连接;
8、关闭监听;

TCP编程的客户端一般步骤是:

1、创建一个socket,用函数socket()
2、设置socket属性,用函数setsockopt();* 可选
3、绑定IP地址、端口等信息到socket上,用函数bind();* 可选
4、设置要连接的对方的IP地址和端口等属性;
5、连接服务器,用函数connect()
6、收发数据,用函数send()recv(),或者read()write();
7、关闭网络连接;

 

Tcp的多用户连接:

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <stdlib.h>
#include <signal.h>

#define MAXLINE 800
int port = 8000;

int main(void)
{
    struct sockaddr_in sin;
    struct sockaddr_in pin;
    int listen_fd;
    int conn_fd;
    int sock_fd;
    int nready;
    int maxi;
    int max;
    int client[FD_SETSIZE];
    int address_size = sizeof(pin);
    char buf[MAXLINE];
    char str[INET_ADDRSTRLEN];
    int i;
    int len;
    int n;
    int ret;

    bzero(&sin, sizeof(sin));
    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = INADDR_ANY;
    sin.sin_port = htons(port);

    signal(SIGPIPE, SIG_IGN);
    signal(SIGCHLD, SIG_IGN);

    listen_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (-== listen_fd)
    {
        perror("call to socket");
        exit(1);
    }
    = bind(listen_fd, (struct sockaddr *)&sin, sizeof(sin));
    if (-== n)
    {
        perror("call to bind");
        exit(1);
    }
    = listen(listen_fd, 20);
    if (-== n)
    {
        perror("call to listen");
        exit(1);
    }
    printf("Accepting connections...\n");

    while(1)
    {
        conn_fd = accept(listen_fd, (struct sockaddr *)&pin, &address_size);

        = fork();
        if (-== n){
            perror("call to fork");
            exit(1);
        }

        else if (== n)
        {
            close(listen_fd);
            while(1)
            {
                memset(buf,'\0',MAXLINE);
readagain:
                ret = read(conn_fd,buf,MAXLINE);
                printf("I read %d Byte!\n",ret);

                if (-== ret){
                    if (errno == EINTR){
                        goto readagain;
                    }else{
                        perror("call to read");
                        exit(1);
                    }
                } else if (== ret){
                    printf("the other side has been closed.\n");
                    break;
                }


                printf("you ip is %s at port %d:%s\n",
                     inet_ntop(AF_INET, &pin.sin_addr,str,sizeof(str)),
                     ntohs(pin.sin_port),buf);

                len = strlen(buf);
                for (= 0; i < len; i++)
                {
                    buf[i] = toupper(buf[i]);
                }

writeagain:
                ret = write(conn_fd, buf, len+1);
                printf("I write %d Byte!\n",ret);

                if (-== ret){
                    if (errno == EINTR){
                        goto writeagain;
                    } else {
                        perror("call to write!");
                        break;
                    }
                }
            }
// end while


            ret = close(conn_fd);
            if (-== ret){
                perror("call close");
                return -1;
            }
            exit(0);
        }
    }
    return 0;
}

 
    若读操作和写操作还没有开始,就被信号中断,此时对read和write的调用也返回-1,并且errno置为EINTR.对于这种情况,应该再次调用read和write。因为socket此时仍然是完好的。一个处理方法为:
     readagain:
         n = read(socket_fd, buf ,MAXLEN);
         if (-1 == ret){
            if (errno == EINTR){
               goto readagain;
            }else{
               perror("call to read");
               exit(1);
            }
         } else if (0 == ret){
              printf("the other side has been closed.\n");
              break;
         }
   此外,如果read的返回为0,表明已经读到文件末尾,对于网络socket,则表明已经主动关闭了连接。
对于close,最好还是检查一下。因为close是可能是不成功的。如write不返回往往不代表确实写成功(为提高性能的"后写"技术导致的),如果写出错,调用close时会返回一个错误,如果不检查这个值,就会漏掉这个错误。(摘自akae培训教材)
 
TCP的客户端:
 

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>

#define MAXLINE 80
int port = 8000;

int main(int argc, char *argv[])
{
  struct sockaddr_in pin;
  int sock_fd;
  char buf[MAXLINE];
  char str[MAXLINE];
  int n;


  signal(SIGPIPE,SIG_IGN);

  bzero(&pin, sizeof(pin));
  pin.sin_family = AF_INET;
  inet_pton(AF_INET, "192.168.2.222", &pin.sin_addr);
  pin.sin_port = htons(port);
  
  sock_fd = socket(AF_INET, SOCK_STREAM, 0);
  n=connect(sock_fd, (void *)&pin, sizeof(pin));
  if (-== n)
  {
     perror("call connect");
     exit(1);
  }
  while(NULL != fgets(str,MAXLINE, stdin))
  {
    write(sock_fd, str, strlen(str)+1);
    n=read(sock_fd, buf, MAXLINE);
    if (== n)
      printf("the othere side has been closed.\n");
    else
      printf("receive from server:%s\n",buf);
  }
  close(sock_fd);
  return 0;
}

<think>好的,我现在需要解释TCP客户端服务端分别是什么。首先,我得回顾一下TCP的基础知识,确保自己理解正确。TCP是传输控制协议,属于传输层,提供可靠的、面向连接的通信。客户端服务端是网络通信中的两个主要角色,但具体怎么区分它们呢? 用户可能已经知道一些网络概念,但需要更清晰的解释。我应该从基本定义入手,先分别说明客户端服务端各自的定义和功能,然后再比较它们的区别。同时,用户可能想知道它们在实际应用中的例子,这样有助于理解。 首先,服务端通常是指等待连接的一方,比如网站服务器。客户端则是发起连接请求的一方,比如浏览器。但需要更详细地解释它们的职责和特点。比如服务端需要一直运行,监听特定的端口,而客户端则是按需发起连接。 接下来,要解释TCP的三次握手过程,因为这是建立连接的关键步骤。服务端在监听状态,客户端发送SYN,服务端回应SYN-ACK,客户端再发送ACK。这个过程对于理解两者的角色很重要。 还要提到数据传输的过程,客户端服务端如何通过建立的连接发送和接收数据。以及连接终止时的四次挥手,确保双方都知道连接已经关闭,避免资源泄漏。 另外,需要举一些实际的例子,比如HTTP中的客户端是浏览器,服务端是Web服务器;邮件客户端和SMTP服务器等。这些例子能帮助用户将概念与实际应用联系起来。 可能还需要比较客户端服务端的不同之处,比如启动顺序、端口监听、资源消耗等。服务端通常需要更稳定的资源和配置,而客户端则更灵活。 需要注意的是,用户可能对某些术语不太熟悉,比如端口、监听、SYN等,所以解释时要尽量简明,避免过于技术化。同时,结构要清晰,分点说明,让用户容易理解。 现在检查是否有遗漏的部分。比如,TCP是面向连接的,所以客户端服务端的连接是点对点的,每个客户端连接都会在服务端有一个对应的socket处理。服务端通常使用多线程或异步处理来应对多个客户端同时连接的情况,这也是一个值得提到的点。 总结一下,回答的结构应该是:定义客户端服务端,各自的作用,通信流程(三次握手、数据传输、四次挥手),实际例子,以及两者的区别。这样用户就能全面理解它们的概念和运作方式了。 在写作过程中,要注意使用中文,避免专业术语堆砌,必要时用比喻或类比帮助理解。例如,把服务端比作商店,客户端比作顾客,顾客发起请求,商店响应并提供服务。 最后,确保信息准确,比如TCP的特点,三次握手和四次挥手的正确步骤,以及客户端服务端在其中的角色。如果有不确定的地方,需要查阅资料确认,比如服务端调用listen()和accept()函数的具体过程。 可能还需要提到一些常见的错误认识,比如认为服务端只能是物理服务器,实际上服务端也可以是任何运行服务端程序的设备,比如个人电脑在运行本地开发服务器时就是服务端。 总之,回答需要全面、清晰、结构合理,帮助用户建立正确的概念模型,并能够应用到实际场景中。</think>以下是TCP客户端服务端的详细对比解析: --- ### **基础定义对比** | 维度 | TCP服务端 | TCP客户端 | |-------------|-----------------------------------|-----------------------------------| | **角色定位** | 服务提供者(如银行柜台) | 服务请求者(如办理业务的客户) | | **启动顺序** | 先启动,持续运行 | 后启动,完成任务后可能断开 | | **端口特征** | 绑定固定知名端口(如HTTP-80) | 使用随机临时端口(32768~61000) | | **资源消耗** | 高(需处理多连接) | 低(通常单连接) | --- ### **核心功能对比** #### **服务端关键行为** 1. **端口监听**:通过`bind()`绑定端口,`listen()`开启监听 ```python # Python示例 import socket server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.bind(('0.0.0.0', 8080)) # 绑定所有网卡的8080端口 server.listen(5) # 设置等待队列长度为5 ``` 2. **接受连接**:`accept()`阻塞等待连接 ```python while True: client_sock, addr = server.accept() # 获得新连接 print(f"收到来自 {addr} 的连接") ``` 3. **多路处理**:通常使用多线程/异步处理并发 ```python from threading import Thread def handle_client(client): # 处理客户端请求 client.send(b"Welcome!") client.close() Thread(target=handle_client, args=(client_sock,)).start() ``` #### **客户端关键行为** 1. **主动连接**:`connect()`发起三次握手 ```python client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client.connect(('127.0.0.1', 8080)) # 向服务端发起连接 ``` 2. **请求响应**:遵循应用层协议通信 ```python client.send(b"GET /index.html HTTP/1.1\r\nHost: localhost\r\n\r\n") response = client.recv(4096) print(response.decode()) ``` 3. **连接管理**:主动关闭或保持长连接 ```python client.close() # 发起四次挥手 ``` --- ### **通信流程详解** **三次握手建立连接**: ``` 客户端 --> SYN=1, seq=x --> 服务端 客户端 <-- SYN=1, ACK=1, seq=y, ack=x+1 <-- 服务端 客户端 --> ACK=1, seq=x+1, ack=y+1 --> 服务端 ``` - 服务端在`accept()`时完成握手 - 客户端在`connect()`返回时确认连接建立 **数据传输阶段**: - 服务端使用`recv()/send()`处理已连接socket - 客户端服务端双向字节流通信 **四次挥手终止连接**(以客户端主动关闭为例): ``` 客户端 --> FIN=1 --> 服务端客户端进入FIN-WAIT-1) 客户端 <-- ACK=1 <-- 服务端客户端进入FIN-WAIT-2) 客户端 <-- FIN=1 <-- 服务端 客户端 --> ACK=1 --> 服务端 (进入TIME-WAIT状态) ``` --- ### **典型应用场景** | 场景 | 服务端实现 | 客户端实现 | |---------------------|-----------------------------------|-----------------------------------| | Web服务 | Nginx/Apache | 浏览器/爬虫程序 | | 文件传输 | FTP Server | FileZilla等客户端 | | 远程登录 | SSH Daemon | PuTTY/MobaXterm | | 数据库访问 | MySQL Server | Navicat/DBeaver | | 即时通讯 | 自研消息服务器 | 手机APP/PC客户端 | --- ### **核心差异总结** 1. **连接方向**: - 服务端被动接受(`accept()`) - 客户端主动连接(`connect()`) 2. **并发处理**: - 服务端必须处理多客户端并发 ```c // C语言示例:fork子进程处理连接 int client_fd = accept(...); if(fork() == 0) { close(listen_fd); // 子进程关闭监听套接字 handle_client(client_fd); exit(0); } close(client_fd); // 父进程关闭已连接套接字 ``` - 客户端通常单线程处理单个连接 3. **异常处理**: - 服务端需防范DDoS攻击(半连接洪水) - 客户端需处理网络波动重连 --- ### **开发注意事项** 1. **服务端最佳实践**: - 设置SO_REUSEADDR避免端口占用 ```python server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) ``` - 使用epoll/kqueue实现高并发(Linux示例) ```python import select epoll = select.epoll() epoll.register(server.fileno(), select.EPOLLIN) ``` 2. **客户端优化技巧**: - 设置连接超时 ```python client.settimeout(5.0) # 5秒超时 ``` - 实现断线重连机制 ```python while True: try: client.connect(...) break except ConnectionRefusedError: sleep(1) ``` --- ### **协议分析工具推荐** 1. Wireshark:抓包分析三次握手过程 2. telnet:手动模拟TCP客户端 ```bash telnet example.com 80 # 连接Web服务器 GET / HTTP/1.1 Host: example.com ``` 3. netcat:快速创建临时TCP服务端 ```bash nc -l 8080 # 监听8080端口 ``` 通过理解这些核心差异和实现细节,开发者可以更高效地构建健壮的TCP网络应用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值