网络编程 pthread/fork

序言

TCP

Transmission Control Protocol 传输控制协议TCP是一种面向连接(连接导向)的、可靠的、基于字节流的运输层(Transport layer)通信协议,由IETF的RFC 793说明(specified)。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能,UDP是同一层内另一个重要的传输协议。

嵌套字

TCP用主机的IP地址加上主机上的端口号作为TCP连接的端点,这种端点就叫做套接字(socket)或插口。套接字是通信的基石,是支持TCP/IP协议的路通信的基本操作单元。套接字存在于通信域中,通信域是为了处理一般的线程通过套接字通信而引进的一种抽象概念。套接字又可分为三种类型:字节流套接字,数据报套接字,原始套接字。

1.查看源代码

1.1 socket函数

函数原型:

int socket(int family,int type,int protpcol)

参数解释:

(1)domain:即协议域,又称为协议族(family)。协议族决定了socket的地址类型,在通信中必须采用对应的地址,如AF_INET决定了要用ipv4地址(32位的)与端口号(16位的)的组合、AF_UNIX决定了要用一个绝对路径名作为地址。
(2)type:指定socket类型。网络程序所采用的通信协议。常用的socket类型有,SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等等。
(3)protocol:就是指定协议。如果指定了type,可以直接使用0。

1.2 bind函数

函数原型:

int bind(int scokfd,struct sockaddr *myaddr,int addrlen);

参数解释:

(1)sockfd:即socket描述字,它是通过socket()函数创建了,唯一标识一个socket。bind()函数就是将给这个描述字绑定一个名字。由socket调用返回的文件描述符

(2)addrlen:sockaddr结构的长度。

(3)myaddr:一个指向sockaddr的指针,在中有sockaddr的定义。

结构体sockaddr的定义

struct sockaddr{
						unsigned short sa_family
						char           sa_data[14]
}

结构体sockaddr_in的定义

struct sockaddr_in{
							unsigned short              sin_family;
							unsigned short int          sin_port;
							struct in_addr              sin_addr;
							unsigned char               sin_zero[8];
}

1.3 listen、connect函数

函数原型:

int listen(int sockfd,int backlog);
int connect(int sockfd,const struct sockaddr *serv_addr,socklen_t addrlen);

参数解释:

(1)socket()函数创建的socket默认是一个主动类型的,listen函数将socket变为被动类型的,等待客户的连接请求。

(2)backing 设置请求排队的最大长度,当有个多个客户端程序和服务端相连时,使用这个表示可以介绍的排队长度。

(3)connect函数的第一个参数即为客户端的socket描述字,第二参数为服务器的socket地址,第三个参数为socket地址的长度。客户端通过调用connect函数来建立与TCP服务器的连接。

1.4 accept函数

函数原型:

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)

参数解释:

accept函数的第一个参数为服务器的socket描述字,第二个参数为指向struct sockaddr *的指针,用于返回客户端的协议地址,第三个参数为协议地址的长度。

(1)sockfd socket返回的文件描述符

(2)addr/addrlen 用来给客户端程序填写的,服务器端只要传递指针就可以

2.编译并在ubuntu下运行

编译

$ gcc server-while-tcp.c -o server-while- tcp.out
$ gcc cilent.c -o client.out

运行

$ ./server-while-tcp.out     //在服务端输入
$ ./client.out 服务端IP地址  //在客户端输入

结果

服务器界面

客户端界面

3.修改服务器为多线程模式

server服务端源代码

#include <sys/types.h>      /* 网络编程所需头文件*/
#include <sys/socket.h>     /* 网络编程所需头文件*/
#include <string.h>
#include <netinet/in.h>     /* 包含类似inet_ntoa函数的头文件 */
#include <arpa/inet.h>      /* 包含类似inet_ntoa函数的头文件 */
#include <unistd.h>         /* 包含close函数、forck函数等系统调用函数的头文件 */
#include <stdio.h>
#include <signal.h>         /* 包含signal函数的头文件 */

#define SERVER_PORT 8888        /* 监听端口号 */
#define BACKLOG 10              /* listen函数中最大同时监听连接路数 */

/* socket              
 * bind 
 * listen
 * accept
 * send/recv
*/
int charup(unsigned char ch[1000]);

int main(int argc, char **argv)
{
    int iSocketServer;
    int iSocketClient;
    struct sockaddr_in tSocketServerAddr;   /* 存放服务器端的通讯协议族、要监听的端口号等信息的结构体 */
    struct sockaddr_in tSocketClientAddr;   /* 存放连接的客户端的IP地址等信息的结构体 */
    int iRet;
    int iAddrLen;

    int iRecvLen;
    unsigned char ucRecvBuf[1000];

    int iClientNum = -1;

    signal(SIGCHLD, SIG_IGN);     /* 处理僵死进程,如果不加,被关闭的客户端所创建的服务器端子进程的资源将不会被父进程回收,导致资源浪费 */

    iSocketServer = socket(AF_INET, SOCK_STREAM, 0);    /* 创建socket */
    if (-1 == iSocketServer)
    {
        printf("Socket error!\n");
        return -1;
    }

    tSocketServerAddr.sin_family        = AF_INET;
    tSocketServerAddr.sin_port          = htons(SERVER_PORT);   /* 将short型的端口数据转换成适合于网络传输的数据类型 */
    tSocketServerAddr.sin_addr.s_addr   = INADDR_ANY;           /* 允许和任何的主机通信 */
    memset(tSocketServerAddr.sin_zero, 0, 8);                   /* 内存置0,确保和struct sockaddr的长度相同 */

    iRet =  bind(iSocketServer, (const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr));  /* 之前创建的socket文件描述符将被bind修饰 */
    if (iRet == -1)
    {
        printf("Bind error!\n");
        return -1;
    }

    iRet = listen(iSocketServer, BACKLOG);      /* 调用listen函数来监听 */
    if (-1 == iRet)
    {
        printf("listen error!\n");
        return -1;
    }
    else
    {
        printf("Listening & Accepting...\n");
    }

    while (1)
    {
        iAddrLen = sizeof(struct sockaddr);
        /* 调用accept函数来等待客户端来连接,客户连接成功返回一个值,连接失败返回-1; */
        iSocketClient = accept(iSocketServer, (struct sockaddr *)&tSocketClientAddr, &iAddrLen);
        if (-1 != iSocketClient)
        {
            iClientNum++;
            /* 支持多个客户端连接,每有一个就调用fork(),并创建一个子进程 */
            printf("Get connnect from NO.%d : %s\n", iClientNum, inet_ntoa(tSocketClientAddr.sin_addr));
            if (!fork())                /* 执行到fork()后马上复制一个代码完全一样的子进程*/
            {                           /* 父进程走fork()=0;子进程走fork()!=0; */
                /*子进程的源码*/
                while (1)
                {

                    /* 接受客户端发来的数据并显示出来 */
                    iRecvLen = iRecvLen = recv(iSocketClient, ucRecvBuf, 999, 0);

                    if (iRecvLen <= 0)
                    {
                        close(iSocketClient);       /* 一直接受客户端传来的消息 */
                        return -1;
                    }
                    else
                    {
                        ucRecvBuf[iRecvLen] = '\0';            /* 加上结束符 */
                        printf("Get Msg From client %d : %s\n", iClientNum, ucRecvBuf);
                    }
                    charup(ucRecvBuf);      /* 字符串转大写字母函数 */
                    iRecvLen = send(iSocketClient, ucRecvBuf, strlen(ucRecvBuf), 0);
                    if(iRecvLen <= 0)
                    {
               }
            }
        }
    }
    close(iSocketServer);
    return 0;
}

// 定义字符串转大写字母函数
int charup(unsigned char ch[1000])
{
    int i = 0;
    while (ch[i] != '\0')
    {
        if(ch[i]>='a'&&ch[i]<='z')
        ch[i]=ch[i]-32;  //如果你忘记了大小写之间相差32的值,也可以用'a'-'A'来表示
        i ++;
    }
    return 0;
}

 client客户端源代码

#include <sys/types.h>      /* 网络编程所需头文件*/
#include <sys/socket.h>     /* 网络编程所需头文件*/
#include <string.h>
#include <netinet/in.h>     /* 包含类似inet_ntoa函数的头文件 */
#include <arpa/inet.h>      /* 包含类似inet_ntoa函数的头文件 */
#include <unistd.h>         /* 包含close函数、forck函数等系统调用函数的头文件 */
#include <stdio.h>
#include <signal.h>


#define SERVER_PORT 8888                  //同一端口号

/* socket               //系统调用
 * connect 
 * listen
 * send/recv
*/



int main(int argc, char **argv)
{
    int iSocketClient;
    struct sockaddr_in tSocketServerAddr;

    int iRet;                  //返回值
    unsigned char ucSendBuf[1000];
    unsigned char ucRecvBuf[1000];
    int iSendLen;
    int iRecvLen;

    if (argc != 2)
    {
        printf("Usage:\n");
        printf("%s <server_ip>\n", argv[0]);
        return -1;
    }

    iSocketClient = socket(AF_INET, SOCK_STREAM, 0);

    tSocketServerAddr.sin_family = AF_INET;
    tSocketServerAddr.sin_port = htons(SERVER_PORT); //host to net,short
    //tSocketServerAddr.sin_addr.s_addr = INADDR_ANY;   //本机上所有IP
    if(0 == inet_aton(argv[1], &tSocketServerAddr.sin_addr))
    {
        printf("invalid server_ip\n");
        return -1;
    }
    memset(tSocketServerAddr.sin_zero, 0 , 8);

    iRet = connect(iSocketClient, (const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr));
    if (-1 == iRet)
    {
        printf("connect error!\n");
        printf("%s <server_ip>\n", argv[0]);
        return -1;
    }

    while (1)
    {
        if(fgets(ucSendBuf, 999, stdin))
        {
            iSendLen = send(iSocketClient, ucSendBuf, strlen(ucSendBuf), 0);
            if(iSendLen <= 0)
            {
                close(iSocketClient);
                return -1;
            }
        }
        iRecvLen = iRecvLen = recv(iSocketClient, ucRecvBuf, 999, 0);
        if (iRecvLen <= 0)
        {
            close(iSocketClient);       //一直接受客户端传来的消息
            return -1;
        }
        else
        {
            ucRecvBuf[iRecvLen] = '\0';            //加上结束符
            printf("Feedback:%s\n", ucRecvBuf);
        }
    }
    return 0;
}

 编译

$ gcc server.c -o server.out
$ gcc client.c -o client.out

 运行

$ ./server.out               //服务端输入
$ ./client.out IP地址        //客户端输入

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值