【Unix 网络编程】TCP 客户/服务器简单 Socket 程序

建立一个 TCP 连接时会发生下述情形:

1. 服务器必须准备好接受外来的连接。这通常通过调用 socket、bind 和 listen 这三个函数来完成,我们称之为被动打开。

2. 客户通过调用 connect 发起主动打开,这导致客户TCP发送一个SYN(同步)分节,标识希望连接的服务器端口以及初始序号。通常SYN分节不携带数据,其所在IP数据报只含有一个IP首部、一个TCP首部及可能有的TCP选项。

3. 服务器发送回一个包含服务器初始序号以及对客户端 SYN 段确认的 SYN + ACK 段作为应答,由于一个 SYN 占用一个序号,因此确认序号设置为客户端初始序号加 1。

4. 客户端发送确认序号为服务器初始序号加 1 的 ACK 段,对服务器 SYN 段进行确认。

这种交换至少需要三个分组,因此称之为TCP的三路握手。

一旦TCP建立连接,客户/服务器之间便可以进行数据通信。

 1. 服务器端

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<unistd.h>
#include<netinet/in.h>
#include <errno.h>
#define PORT 6666

int main(int argc,char **argv)
{
    int ser_sockfd,cli_sockfd;
    int err,n;
    int addlen;
    struct sockaddr_in ser_addr;
    struct sockaddr_in cli_addr;
    char recvline[200],sendline[200];

    ser_sockfd = socket(AF_INET,SOCK_STREAM,0);          //创建套接字
    if(ser_sockfd == -1)
    {
        printf("socket error:%s\n",strerror(errno));
        return -1;
    }

    bzero(&ser_addr,sizeof(ser_addr));

    /*在待捆绑到该TCP套接口(sockfd)的网际套接口地址结构中填入通配地址(INADDR_ANY)
    和服务器的众所周知端口(PORT,这里为6666),这里捆绑通配地址是在告知系统:要是系统是
    多宿主机(具有多个网络连接的主机),我们将接受宿地址为任何本地接口的地址*/
    ser_addr.sin_family = AF_INET;
    ser_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    ser_addr.sin_port = htons(PORT);

    //将网际套接口地址结构捆绑到该套接口
    err = bind(ser_sockfd,(struct sockaddr *)&ser_addr,sizeof(ser_addr));
    if(err == -1)
    {
        printf("bind error:%s\n",strerror(errno));
        return -1;
    }
    //将套接口转换为一个监听套接口,监听等待来自客户端的连接请求
    err = listen(ser_sockfd,5);
    if(err == -1)
    {
        printf("listen error\n");
        return -1;
    }

    printf("listen the port:\n");

    while(1)
    {
        addlen = sizeof(struct sockaddr);
        //等待阻塞,等待客户端申请,并接受客户端的连接请求
        //accept成功,将创建一个新的套接字,并为这个新的套接字分配一个套接字描述符
        cli_sockfd = accept(ser_sockfd,(struct sockaddr *)&cli_addr,&addlen);
        if(cli_sockfd == -1)
        {
            printf("accept error\n");
        }

        //数据传输
        while(1)
        {
            printf("waiting for client...\n");
            n = recv(cli_sockfd,recvline,1024,0);
            if(n == -1)
            {
                printf("recv error\n");
            }
            recvline[n] = '\0';

            printf("recv data is:%s\n",recvline);

            printf("Input your words:");
            scanf("%s",sendline);
            send(cli_sockfd,sendline,strlen(sendline),0);
        }
        close(cli_sockfd);
    }
    close(ser_sockfd);

    return 0;
}

1.首先通过 socket 函数创建套接字,此时套接字数据结构字段并未填充,在使用之前必须调用过程来填充对应字段,这里在地址结构中填入通配地址(INADDR_ANY),通配地址就是指定地址为 0.0.0.0 的地址,表示服务器接受机器上所有IP地址的连接,用于多IP机器上。这样无论客户 connect 哪个IP地址,服务器端都会接收到请求,即接受宿地址为任何本地接口的地址。如果是指定地址,那么机器只有 connect 这个地址才能成功。后面是填充端口号,如果指定为 0,则由系统随机选择一个未被使用的端口。

2. bind 将没有指定端口的 socket(ser_sockfd)绑定到我们指定的端口上(通配地址+指定端口号),服务器是通过它们的众所周知端口被大家认识的。这样 socket(ser_sockfd)就与指定的端口产生了关联,即指向了指定端口。

3. listen 将套接口转换为一个监听套接口,被动打开,允许监听客户端的连接请求,然后 accept 客户端的连接请求,没有请求则阻塞。

5. accept 成功后,将创建新的套接字,并为新套接字分配一个套接口描述符,该套接字除了记录本地(服务器)的IP和端口号信息外,还记录了目的(客户)IP和端口号信息。服务器与客户的通信则是通过该新创建的套接字(已连接套接字)进行。

2. 客户端

 

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<unistd.h>
#include<netinet/in.h>
#define PORT 6666

int main(int argc,char **argv)
{
    int sockfd;
    int err,n;
    struct sockaddr_in addr_ser;
    char sendline[200],recvline[200];

    sockfd = socket(AF_INET,SOCK_STREAM,0);       //创建套接字
    if(sockfd == -1)
    {
        printf("socket error\n");
        return -1;
    }

    bzero(&addr_ser,sizeof(addr_ser));     

    /*用通配地址和指定端口号装填一个网际接口地址结构*/
    addr_ser.sin_family = AF_INET;
    addr_ser.sin_addr.s_addr = htonl(INADDR_ANY);
    addr_ser.sin_port = htons(PORT);                

    //TCP:客户(sockfd)向服务器(套接口地址结构)发起连接,主动请求
    //服务器的IP地址和端口号有参数addr_ser指定
    err = connect(sockfd,(struct sockaddr *)&addr_ser,sizeof(addr_ser));
    if(err == -1)
    {
        printf("connect error\n");
        return -1;
    }

    printf("connect with server...\n");

    //数据传输
    while(1)
    {
        printf("Input your words:");
        scanf("%s",sendline);

        send(sockfd,sendline,strlen(sendline),0);            

        printf("waiting for server...\n");

        n = recv(sockfd,recvline,100,0);
        recvline[n] = '\0'; 

        printf("recv data is:%s\n",recvline);
    }

    return 0;
}

1. 客户端同样通过 socket 创建套接字,TCP 客户通常不把IP地址捆绑到它的套接口上,当连接套接口时,内核将根据所用外出网络接口来确定源IP地址,并选择一个临时端口作为源端口。
2. 用通配地址和指定端口填充的是待连接服务器端的套接字地址结购,这里采用的是通配地址,由于服务器端指定的是通配地址,即接受机器上所有IP地址的连接,同样客户也可向机器上任何IP地址发起连接,服务器端都会接收到。

 

3. 客户端向服务器发起连接请求,connect 成功后,其请求连接的服务器的IP和端口号信息将会写入该套接字,这样该套接字也同时记录了本地和目的的IP地址和端口信息。也就可以进行通信了。

结果如下:

时间: 2024-10-27 16:43:18

【Unix 网络编程】TCP 客户/服务器简单 Socket 程序的相关文章

UNIX网络编程:如何处理服务器中大量的TIME_WAIT

出现条件: 服务器主动关闭 短连接服务加剧 根据TCP协议定义的3次握手断开连接规定,发起socket主动关闭的一方 socket将进入TIME_WAIT状态,TIME_WAIT状态将持续2个MSL(Max Segment Lifetime),TIME_WAIT状态下的socket不能被回收使用. 具体现象是对于一个处理大量短连接的服务器,如果是由服务器主动关闭客户端的连接,将导致服务器端存在大量的处于TIME_WAIT状态的socket, 甚至比处于Established状态下的socket多

UNIX网络编程:并发服务器(TCP)

在迭代服务器中,服务器只能处理一个客户端的请求,如何同时服务多个客户端呢?在未讲到select/poll/epoll等高级IO之前,比较老土的办法是使用fork来实现. 网络服务器通常用fork来同时服务多个客户端,父进程专门负责监听端口,每次accept一个新的客户端连接就fork出一个子进程专门服务这个客户端.但是子进程退出时会产生僵尸进程,父进程要注意处理SIGCHLD信号和调用wait清理僵尸进程,最简单的办法就是直接忽略SIGCHLD信号. 当一个连接建立时,accept返回,服务器接

unix网络编程问题(初学者)

问题描述 unix网络编程问题(初学者) 除了头文件和打印外,我都是照着书敲的,但是结果不对,希望帮我解决,谢谢啦!!!书上的正确结果是一串表示时间的字符 问题:connect error 在建立服务器的连接出错输入:./get_tima 192.168.154.130(ip地址我用ifconfig得到的,我也用127.0.0.1做过一样得不到想要的结果)结果:connect errorread error 出处:unix网络编程 1.2-一个简单的时间获取客户程序 #include#inclu

UNIX网络编程:I/O复用:select和poll函数

我们看到TCP客户同时处理两个输入:标准输入和TCP套接字.我们遇到的问题是就在客户阻塞于(标准输入上)fgets调用,服务器进程会被杀死.服务器TCP虽然正确的给客户TCP发送了一个FIN,但是既然客户进程正阻塞于从标准输入读入的过程,它将看不到这个EOF,直到从套接字读时为止(可能额已过了很长时间).这样的进程需要一种预先告知内核的能力,使得内核一旦发现进程指定的一个或多个I/O条件就绪(也就是说输入已准备好被读入,或者描述符已能承接更多的输出),它就通知进程.这个能力称为I/O复用,是由s

select-调试信息无法打印 unix网络编程

问题描述 调试信息无法打印 unix网络编程 如代码注释信息所示,应该是缓冲区的问题,但是stderr是无缓冲区的,而且也使用了fflush函数,也没用.希望能有人指点指点,谢谢 #include "globle.h" #define port 8082 int main( int argc, char *argv[] ){ fprintf(stderr, "11111111111n");//无法打印 fflush(stdout); int listenfd,soc

python网络编程学习笔记(五):socket的一些补充_python

1.半开放socket 利用shutdown()函数使socket双向数据传输变为单向数据传输.shutdown()需要一个单独的参数,该参数表示了如何关闭socket.具体为:0表示禁止将来读:1 表示禁止将来写:2表示禁止将来读和写. 2.timeouts控制超时 调用socket的settimeout()函数,向其传递参数,表明超时时间设置.当访问一个socket,如果经过了参数设定的时间后,什么都没有发生,则会产生一个socket.timeout异常.例如:当程序运行后,会等待数据传入.

iOS网络编程之三——NSURLConnection的简单使用

iOS网络编程之三--NSURLConnection的简单使用 一.引言     在iOS7后,NSURLSession基本代替了NSURLConnection进行网络开发,在iOS9后,NSURLConnection相关方法被完全的弃用,iOS系统有向下兼容的特性,尽管NSURLConnection已经被弃用,但在开发中,其方法依然可以被使用,并且如果需要兼容到很低版本的iOS系统,有时就必须使用NSURLConnection类了. 二.使用NSURLConnection进行同步请求     

UNIX网络编程之旅-配置unp.h头文件环境

最近在学习Unix网络编程(UNP),书中steven在处理网络编程时只用了一个#include "unp.h"  相当有个性并且也很便捷 于是我把第三版的源代码编译实现了这个过程,算是一种个性化的开发环境的搭建吧,顺便把过程记录下来,以便自己以后查阅.   首先去网上找到源代码包unpv.13e.tar.gz 一找一大堆 解压缩到你的某个目录,unpv13e里面大致有这些目录 ├── aclocal.m4 ├── advio ├── bcast ├── config.guess ├─

python实现简单socket程序在两台电脑之间传输消息的方法_python

本文实例讲述了python实现简单socket程序在两台电脑之间传输消息的方法.分享给大家供大家参考.具体分析如下: python开发简单socket程序在两台电脑之间传输消息,分为客户端和服务端,分别在两台电脑上运行后即可进行简单的消息传输,也可以在一台电脑上测试,设置两个不同的端口即可. # Save as server.py 服务端代码 # Message Receiver import os from socket import * host = "" port = 13000