Linux网络编程(socket)

入门知识查看https://blog.csdn.net/qq_31505483/article/details/73929798

 

网络编程本质上也是数据传输,也满足数据传输三要素:源、目的、长度。服务器只能被动地响应请求,而客户端可以主动发出请求。

网络通信有两种协议:TCP和UDP,其中,TCP是可靠的(“三次握手”的验证方法,可参考博客中的讲解),有一定的重传机制。UDP没有三次握手机制,数据发出后就不再进行验证,不关心对方有没有收到。并不一定所有场合下都适合使用可靠地TCP,比如在视频传输过程中,由于TCP的重传机制,如果某一帧对方接收不成功,就会一直重传,后面的图像就会被滞后,那么视频播放就会很卡,而使用UDP就不会有这种问题。对于一些控制命令,就比较适合用TCP。

下图中的A和B不是指两台设备,而是指两个程序,这两个程序可以在同一台电脑上,也可以在不同设备上。

TCP传输程序编写方法:

使用TCP协议进行数据传输怎么写程序呢?如下图所示。Socket函数所返回的fd文件描述符,服务器与客户端都可以通过对这个文件的读写操作来完成数据传输,也就是说可以把fb理解为一个通道,服务器与客户端将数据放在上面或者从上面把数据读取出来。由于服务器只能被动地响应请求,因此要不断地检测某个端口,才能知道有数据传输进来,因此要使用bind函数将自己的IP、端口和fd绑定(IP和端口的关系可参照https://blog.csdn.net/ios_xumin/article/details/50502720)。

前面提到过,网络编程也是一种数据传输,也需指定数据传输的三要素:源、目的、长度,源和目的就是在bind和connect函数中指定的,长度是在send或write函数,recv或read函数中指定的。

以下程序中的TCP相关的函数的参数都可以参照博客

TCP传输程序示例:

Server.c

#include <sys/types.h>          /* See NOTES */

#include <sys/socket.h>

#include <string.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <arpa/inet.h>

#include <unistd.h>

#include <stdio.h>

#include <signal.h>

 

//使用man socket可以查看socket函数所需要包含的头文件

 

/* socket

 * bind

 * listen

 * accept

 * send/recv

 */

 

#define SERVER_PORT 8888

#define BACKLOG     10

 

int main(int argc, char **argv)

{

       int iSocketServer;

       int iSocketClient;

       struct sockaddr_in tSocketServerAddr;

       struct sockaddr_in tSocketClientAddr;

       int iRet;

       int iAddrLen;

 

       int iRecvLen;

       unsigned char ucRecvBuf[1000];

 

       int iClientNum = -1;

 

       signal(SIGCHLD,SIG_IGN); //在销毁子进程后,会产生僵尸进程,需要父进程来清理,换句话说,父进程要为子进程收尸,该函数就是这个作用。子进程退出后会给父进程发送一个信号,这个信号的处理函数就是SIG_IGN。

      

       iSocketServer = socket(AF_INET, SOCK_STREAM, 0);

       if (-1 == iSocketServer)

       {

              printf("socket error!\n");

              return -1;

       }

 

       tSocketServerAddr.sin_family      = AF_INET;

       tSocketServerAddr.sin_port        = htons(SERVER_PORT);  /* host to net, short */ //转换为网络字节序,这一个函数在博客里也有讲明。这个参数指明要检测的端口

      tSocketServerAddr.sin_addr.s_addr = INADDR_ANY;  //指定要检测的IP,INADDR_ANY这个参数表示所有IP

       memset(tSocketServerAddr.sin_zero, 0, 8);

      

       iRet = bind(iSocketServer, (const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr));  //一台电脑上可能会有多个IP,多个端口,检测哪一个IP,哪一个端口,可以在这里指定。

       if (-1 == iRet)

       {

              printf("bind error!\n");

              return -1;

       }

 

       iRet = listen(iSocketServer, BACKLOG);

       if (-1 == iRet)

       {

              printf("listen error!\n");

              return -1;

       }

 

       while (1)

       {

              iAddrLen = sizeof(struct sockaddr);

              iSocketClient = accept(iSocketServer, (struct sockaddr *)&tSocketClientAddr, &iAddrLen);  //接收谁的连接呢?客户端的IP等信息就会保存在tSocketClientAddr结构体里面,返回客户端的socket。

              if (-1 != iSocketClient)

              {

                     iClientNum++;

                     printf("Get connect from client %d : %s\n",  iClientNum, inet_ntoa(tSocketClientAddr.sin_addr));  //连接成功后打印客户端的IP地址,但是需要将sin_addr转换为我们常见的192.168….这样的字符串打印

                     if (!fork())    //有多个客户端时,每来一个连接,就创建一个进程。子进程的代码和父进程完全一样,但是子进程是直接从fork()处开始往下跑的。

                     {

                            /* 子进程的源码 */

                            while (1)

                            {

                                   /* 接收客户端发来的数据并显示出来 */

                                   iRecvLen = recv(iSocketClient, ucRecvBuf, 999, 0);//从iSocketClient接收999长度的数据,存到ucRecvBuf里面

                                   if (iRecvLen <= 0)

                                   {

                                          close(iSocketClient);

                                          return -1;

                                   }

                                   else

                                   {

                                          ucRecvBuf[iRecvLen] = '\0';

                                          printf("Get Msg From Client %d: %s\n", iClientNum, ucRecvBuf);

                                   }

                            }                         

                     }

              }

       }

      

       close(iSocketServer);

       return 0;

}

 

插讲fork()创建子进程:对于程序中的if(0==fork()) {} else {}结构(子进程创建成功),主进程会执行else分支,子进程和父进程代码完全一样,但是会从for()处直接开始执行,如果子进程创建成功,会执行if分支。

 

Client.c:

#include <sys/types.h>          /* See NOTES */

#include <sys/socket.h>

#include <string.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <arpa/inet.h>

#include <unistd.h>

#include <stdio.h>

 

/* socket

 * connect

 * send/recv

 */

 

#define SERVER_PORT 8888

 

int main(int argc, char **argv)

{

       int iSocketClient;

       struct sockaddr_in tSocketServerAddr;

      

       int iRet;

       unsigned char ucSendBuf[1000];

       int iSendLen;

 

       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;

      if (0 == inet_aton(argv[1], &tSocketServerAddr.sin_addr))  //通过参数[1]传入服务器的地址信息

      {

              printf("invalid server_ip\n");

              return -1;

       }

       memset(tSocketServerAddr.sin_zero, 0, 8);

 

 

       iRet = connect(iSocketClient, (const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr));       //请求建立到服务器的连接,tSocketServerAddr存有服务器的地址信息

       if (-1 == iRet)

       {

              printf("connect error!\n");

              return -1;

       }

 

       while (1)

       {

              if (fgets(ucSendBuf, 999, stdin))  //从标准输入那里获取要传输的数据,如下图所示,保存到ucSendBuf

              {

                     iSendLen = send(iSocketClient, ucSendBuf, strlen(ucSendBuf), 0);

                     if (iSendLen <= 0)

                     {

                            close(iSocketClient);

                            return -1;

                     }

              }

       }

      

       return 0;

}

 

 

 

UDP传输程序示例:

    UDP传输涉及到的系统调用如下图所示。其中,用send发送数据时,connect函数必须使用,使用sendto发送数据时,connect函数可以不用。

 

Server.c

#include <sys/types.h>          /* See NOTES */

#include <sys/socket.h>

#include <string.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <arpa/inet.h>

#include <unistd.h>

#include <stdio.h>

#include <signal.h>

 

 

/* socket

 * bind

 * sendto/recvfrom

 */

 

#define SERVER_PORT 8888

 

int main(int argc, char **argv)

{

       int iSocketServer;

       int iSocketClient;

       struct sockaddr_in tSocketServerAddr;

       struct sockaddr_in tSocketClientAddr;

       int iRet;

       int iAddrLen;

 

       int iRecvLen;

       unsigned char ucRecvBuf[1000];

 

       int iClientNum = -1;

      

       iSocketServer = socket(AF_INET, SOCK_DGRAM, 0);

       if (-1 == iSocketServer)

       {

              printf("socket error!\n");

              return -1;

       }

 

       tSocketServerAddr.sin_family      = AF_INET;

       tSocketServerAddr.sin_port        = htons(SERVER_PORT);  /* host to net, short */

      tSocketServerAddr.sin_addr.s_addr = INADDR_ANY;

       memset(tSocketServerAddr.sin_zero, 0, 8);

      

       iRet = bind(iSocketServer, (const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr));

       if (-1 == iRet)

       {

              printf("bind error!\n");

              return -1;

       }

 

 

       while (1)

       {

              iAddrLen = sizeof(struct sockaddr);

              iRecvLen = recvfrom(iSocketServer, ucRecvBuf, 999, 0, (struct sockaddr *)&tSocketClientAddr, &iAddrLen);

              if (iRecvLen > 0)

              {

                     ucRecvBuf[iRecvLen] = '\0';

                     printf("Get Msg From %s : %s\n", inet_ntoa(tSocketClientAddr.sin_addr), ucRecvBuf);

              }

       }

      

       close(iSocketServer);

       return 0;

}

 

 

 

 

Client.c

#include <sys/types.h>          /* See NOTES */

#include <sys/socket.h>

#include <string.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <arpa/inet.h>

#include <unistd.h>

#include <stdio.h>

 

/* socket

 * connect

 * send/recv

 */

 

#define SERVER_PORT 8888

 

int main(int argc, char **argv)

{

       int iSocketClient;

       struct sockaddr_in tSocketServerAddr;

      

       int iRet;

       unsigned char ucSendBuf[1000];

       int iSendLen;

       int iAddrLen;

 

       if (argc != 2)

       {

              printf("Usage:\n");

              printf("%s <server_ip>\n", argv[0]);

              return -1;

       }

 

       iSocketClient = socket(AF_INET, SOCK_DGRAM, 0);

 

       tSocketServerAddr.sin_family      = AF_INET;

       tSocketServerAddr.sin_port        = htons(SERVER_PORT);  /* host to net, short */

      //tSocketServerAddr.sin_addr.s_addr = INADDR_ANY;

      if (0 == inet_aton(argv[1], &tSocketServerAddr.sin_addr))

      {

              printf("invalid server_ip\n");

              return -1;

       }

       memset(tSocketServerAddr.sin_zero, 0, 8);

 

#if 0

       iRet = connect(iSocketClient, (const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr));    

       if (-1 == iRet)

       {

              printf("connect error!\n");

              return -1;

       }

#endif

 

       while (1)

       {

              if (fgets(ucSendBuf, 999, stdin))

              {

#if 0

                     iSendLen = send(iSocketClient, ucSendBuf, strlen(ucSendBuf), 0);

#else

                     iAddrLen = sizeof(struct sockaddr);

                     iSendLen = sendto(iSocketClient, ucSendBuf, strlen(ucSendBuf), 0,

                                           (const struct sockaddr *)&tSocketServerAddr, iAddrLen);

 

#endif

                     if (iSendLen <= 0)

                     {

                            close(iSocketClient);

                            return -1;

                     }

              }

       }

      

       return 0;

}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值