linux编程 -- 网络编程(一)

常用函数

//socket函数
头文件
#include <sys/types.h>
#include <sys/socket.h>
//创建socket
int socket(int domain, int type, int protocol);

int bind(int sockfd, const struct sockaddr *my_addr, socklen_t addrlen);
//bind将进程和一个套接口联系起来,bind通常用于服务器进程为接入客户连接建立一个套接口
//sockfd:函数socket调用返回的套接口值
//my_addr:结构sockaddr的地址
//addrlen:设置了my_addr能容纳的最大字节数
//成功返回0,失败返回-1,并设置errno

int listen(int sockfd, int backlog);
//创建套接口并且使用bind将它和一个进程关联起来以后,服务端就需要调用listen来监听指定端口的客户端连接
//参数sockfd是调用socket返回的套接口的描述符
//参数backlog设置接入队列的大小,通常把这个值设置的足够大就可以了
//成功返回0,失败返回-1,并设置errno

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
//当有客户端连接到服务端,他们会排入队列,直到服务器准备好处理他们为止,accept会返回一个新的套接口,同时原来的套接口继续listen指定端口号
//sockfd:调用socket返回的套接口描述符
//addr:指向结构sockaddr结构
//addrlen:设置了addr能容纳的最大字节数
//成功返回新的套接字,失败返回-1,并设置errno

//客户端和服务端建立了连接就可以在客户端和服务端之间传输数据了,需要两个系统调用
-send---发送数据
-recv---接收数据
一个套接字接口既可以发送数据,也可以接收数据

ssize_t send(int s, const void *buf, size_t len, int flags)
//send函数用来发送数据
//s:已经建立连接的套接口
//buf:接收数据内存buffer地址指针
//len:指明buffer的大小,单位字节
//flages:一般填0.
//成功返回发送的字节数,失败返回-1,并设置errno

ssize_t recv(int s, void *buf, size_t len, int flags)
//recv函数用来接收数据
//s:已经建立连接的套接口
//buf:接收数据内存buffer地址指针
//len:指明buffer的大小,单位字节
//flages:一般填0.
//成功返回发送的字节数,失败返回-1,如果对端套接字已经关闭,返回0

int setsockopt(int s, int level, int optname, const void *optval, socklen_t optlen)
//setsockopt函数设置套接口
//常见用法为
  int on = 1;
  setsockopt(st, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
//由于TCP套接字状态TIME_WAIT引起该套接字关闭后约保留2-4分钟,再次期间bind绑定该端口会失败
//SO_REUSEADDR指示系统地址可重用

//最后当用完套接口以后,就该释放套接口所占用的资源了,通过close做到这一点
//当试图向一个已经关闭的套接口写或者读数据会出错

服务端代码:

//服务端
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>

int main(int arg, char *args[])
{
    if(arg < 2)
        return -1;
    
    int port = atoi(args[1]);
    //初始化socket
    int st = socket(AF_INET, SOCK_STREAM, 0);
    if(st == -1)
    {
        printf("create socket error:%s\n", strerror(errno));
        return -1;
    }
    
    int on = 1;
    if(setsockopt(st, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1)
    {
        printf("setsocket failed %s\n", strerror(errno));
        return EXIT_FAILURE;
    }
    struct sockaddr_in addr; //定义一个IP地址的结构
    memset(&addr, 0, sizeof(addr)); 
    addr.sin_family = AF_INET; //设置结构地址类型为TCP/IP地址
    addr.sin_port = htons(port); //指定端口号:8080, htons:将short类型从host字节类型到net字节类型
    addr.sin_addr.s_addr = htonl(INADDR_ANY);//INADDR_ANY代表这个server上所有的地址
    //将IP地址与server程序绑定
    if(bind(st, (struct sockaddr*)&addr, sizeof(addr)) == -1)
    {
        printf("bind failed %s\n", strerror(errno));
        return EXIT_FAILURE;
    }
    //server开始listen
    if(listen(st, 20) == -1)
    {
        printf("listern failed %s\n", strerror(errno));
        return EXIT_FAILURE;
    }
    
    char s[1024];
    int client_st = 0; //client端socket
    //socklen_t len = 0;
    struct sockaddr_in client_addr; //表示客户端IP地址
    void *p = &client_addr;
    
    while(1)
    {
        memset(&client_addr, 0, sizeof(client_addr));
        socklen_t len = sizeof(client_addr);
        //accept会阻塞,直到有客户连接过来,accept返回client的socket描述符
        client_st = accept(st, (struct sockaddr *)p, &len);
        if(client_st == -1)
        {
            printf("accept failed %s\n", strerror(errno));
            return EXIT_FAILURE;
        }
        //
        printf("accept by %s\n",inet_ntoa(client_addr.sin_addr));
        while(1)
        {
            memset(s, 0, sizeof(1024));
            int rc = recv(client_st, s, sizeof(s), 0); //阻塞的
            if(rc > 0) //接收来自client的消息
            {
                printf("recv is %s\n", s);
                memset(s, 0, sizeof(s));
                read(STDIN_FILENO, s, sizeof(s));
                send(client_st, s, strlen(s), 0);
            }
            else
            {
                if(rc == 0)
                {   
                    printf("client socket closed\n");
                } 
                else
                    printf("recv failed %s\n", strerror(errno));
                break;
            }
            
        }
        close(client_st);//关闭client端socket
    }
    close(st);    
    return EXIT_SUCCESS;
}

客户端代码:

//客户端
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int main(int arg, char *args[])
{
    if(arg < 3)
        return -1;
    int port = atoi(args[2]);
    //初始化socket
    int st = socket(AF_INET, SOCK_STREAM, 0);
    if(st == -1)
    {
        printf("create socket error:%s\n", strerror(errno));
        return -1;
    }
    struct sockaddr_in addr; //定义一个IP地址的结构
    memset(&addr, 0, sizeof(addr)); 
    addr.sin_family = AF_INET; //设置结构地址类型为TCP/IP地址
    addr.sin_port = htons(port); //指定端口号:8080, htons:将short类型从host字节类型到net字节类型
    addr.sin_addr.s_addr = inet_addr(args[1]);//经字符类型的IP地址转化为int,赋给addr结构
   
    //调用connect连接到结构addr指定的IP地址和端口号
    if(connect(st, (struct sockaddr *)&addr, sizeof(addr)) == -1) 
    {
        printf("connect failed %s\n", strerror(errno));
        return EXIT_FAILURE;
    }
    char s[1024];
    while(1)
    {
      memset(s, 0, sizeof(1024));
      read(STDIN_FILENO, s, sizeof(s));
      if(send(st, s, strlen(s), 0) == -1) //发送buf的数据
      {
          printf("send failed %s\n", strerror(errno));
          return EXIT_FAILURE;
      }
      memset(s, 0, sizeof(1024));
      memset(s, 0, sizeof(s));
      if(recv(st, s, sizeof(s), 0) > 0)
          printf("recv is %s\n", s);
      else
          break;
    }
    close(st);
    
    return EXIT_SUCCESS;
}

UDP和TCP比较:

//redhat6.3接收buf为64k,发送buf超过512k,发送就被阻塞了。

UDP和TCP对比:
-UDP处理细节比TCP少
-UDP不能保证消息被传送到目的地
-UDP不能保证数据包的传递顺序
-TCP处理UDP不处理的细节
-TCP是面向连接的协议
-UDP是无连接协议
-TCP保持一个连接
-UDP只是把数据发送出去而已

TCP优点
-TCP提供以认可的方式显示的创建连接和终止连接
-TCP保证可靠的,顺序的以及不重复的数据传输
-TCP处理流控制
-TCP允许数据优先
-如果数据没有传送到,TCP套接字会返回出粗提示
-TCP通过褒词连接并将数据块分成更小的分片来处理大数据,而无需程序员编码处理。

TCP的缺点
-TCP需要创建并保持一个连接,给系统带来很大开销
-TCP数据传输效率低

TCP并发机制:
1、增加server数量
2、多层结构
一台server同时支持1000个client
中间件对于最终用户来讲是server,对于server来讲是client,
一个中间件,同时支持1000个client

UDP的优点:
-UDP不要求保持一个连接
-UDP没有因接收方没有收到数据包重传而带来开销
-设计UDP的目的是用于短应用和控制消息
-在一个数据包接一个数据包基础上,UDP要求的网络带宽比TCP小

UDP的缺点
-程序员必须创建代码监测数据包的正确性,必要时重传
-程序员必须把大数据包分片

选择使用哪一种协议
-一些消息重要程度不高,或者有规律重复,可以使用UDP
-如果要传输一个重要的数据,丢失一点就会破坏整个数据,那么需要选择TCP
-telnet,ssh,http等基本都基于TCP
-流媒体为了保证很窄的网络带宽来传送更多的数据,基本采用UDP
-多数游戏中,丢失来自某个用户的状态更新可能不会引起注意,多以采用UDP
-设计用在局域网的应用可以采用UDP,因为在局域网中丢失数据包的可能性很低

 

使用UDP与TCP所用的代码基本类似,唯一的区别在于socket函数调用的时候一个参数不同
int socket(int domain, int type, int protocal);
参数type为SOCK_STREAM代表TCP,SOCK_DGRAM代表UDP
对于TCP和UDP都可以使用recvfrom,但recv只能TCP使用

使用UDP发送数据
ssize_t sendto(int s, const void *buf, size_t len, int flags, const struct sockaddr *to,
socklen_t tolen);
UDP不需要握手机制,也不需要确认另一个系统是否有服务端在listen

使用UDP接收数据
ssize_t recvfrom(int s, void *buf, size_t len, int flages,
struct sockaddr *from, socklen_t *fromlen);
UDP不需要listen,bind之后就可以接收数据了

UDP代码:

//UDPserver.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>


int main(int argc, char *args[])
{
    int st = socket(AF_INET, SOCK_DGRAM, 0); //建立socket的时候第二个参数为SOCK_DGRAM
    if(st == -1)
    {
        printf("socket failed %s\n", strerror(errno));
        return 0;
    }

    int port = atoi(args[1]);

    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = htonl(INADDR_ANY);
    if(bind(st, (struct sockaddr *)&addr, sizeof(addr)) == -1)
    {

        printf("bind failed %s\n", strerror(errno));
        return -1;
    }

    char buf[1024];
    struct sockaddr_in client_addr;
    socklen_t len = sizeof(client_addr);
    while(1)
    {
        memset(&client_addr, 0, sizeof(client_addr));
               memset(buf, 0, sizeof(buf));
               if(recvfrom(st, buf, sizeof(buf), 0, (struct sockaddr *)&client_addr, &len) == -1)
               {
                   printf("recvfrom failed %s\n", strerror(errno));
                   break;
               }
               else
               {
                   printf("%s recv is %s\n", inet_ntoa(client_addr.sin_addr), buf);
               }
    }
    close(st);

    return EXIT_SUCCESS;
}
///////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////
//udpclient.c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <pthread.h> int main(int argc, char *args[]) { int st = socket(AF_INET, SOCK_DGRAM, 0); //建立socket的时候第二个参数为SOCK_DGRAM if(st == -1) { printf("socket failed %s\n", strerror(errno)); return 0; } int port = atoi(args[2]); //设置UDP广播属性 int on = 1; //设置UDP可以发送广播消息 if(setsockopt(st, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) == -1) { printf("setsockopt failed %s", strerror(errno)); return EXIT_FAILURE; } struct sockaddr_in addr; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(port); addr.sin_addr.s_addr = inet_addr(args[1]); char buf[1024]; while(1) { memset(buf, 0, sizeof(buf)); read(STDIN_FILENO, buf, sizeof(buf)); if(sendto(st, buf, strlen(buf), 0, (struct sockaddr *) &addr, sizeof(addr)) == -1) { printf("sendto failed %s\n", strerror(errno)); break; } } close(st); return EXIT_SUCCESS; }

 

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。