设为首页 收藏本站
查看: 535|回复: 0

[经验分享] Linux下的socket网络编程

[复制链接]
累计签到:1 天
连续签到:1 天
发表于 2016-4-12 08:54:33 | 显示全部楼层 |阅读模式
linux 网络编程是通过socket(套接字)接口实现,Socket是一种文件描述符,socket起源于UNIX,在Unix一切皆文件哲学的思想下,socket是一种"打开—读/写—关闭"模式的实现,服务器和客户端各自维护一个"文件",在建立连接打开后,可以向自己文件写入内容供对方读取或者读取对方内容,通讯结束时关闭文件。
socket 类型
    常见的socket有3种类型如下。
    (1)流式socket(SOCK_STREAM )
    流式套接字提供可靠的、面向连接的通信流;它使用TCP 协议,从而保证了数据传输的正确性和顺序性。
    (2)数据报socket(SOCK_DGRAM )
    数据报套接字定义了一种无连接的服 ,数据通过相互独立的报文进行传输,是无序的,并且不保证是可靠、无差错的。它使用数据报协议UDP。
    (3)原始socket(SOCK_RAW)
    原始套接字允许对底层协议如IP或ICMP进行直接访问,功能强大但使用较为不便,主要用于一些协议的开发。
进行Socket编程,常用的函数有:
1.socket:创建一个socket
  int socket(int family, int type, int protocol);
//family指定协议族;type参数指定socket的类型:SOCK_STREAM、SOCK_DGRAM、SOCK_RAW;protocol通常赋值"0", socket()调用返回一个整型socket描述符


2.bind:用于绑定IP地址和端口号到socket
int bind(int sockfd, struct sockaddr *my_addr, int addrlen);
//sockfd是一个socket描述符,my_addr是一个指向包含有本机IP地址及端口号等信息的sockaddr类型的针; addrlen常被设置为sizeof(struct sockaddr),bind()函数在成功被调用时返回0;遇到错误时返回"-1"并将errno置为相应的错误号


3.connect:该函数用于绑定之后的client端,与服务器建立连接
int connect(int sockfd, struct sockaddr *serv_addr, int addrlen);
//sockfd是目的服务器的sockect描述符;serv_addr是服务器端的IP地址和端口号的地址,addrlen常被设置为sizeof(struct sockaddr)。遇到错误时返回-1,并且errno中包含相应的错误码


4.listen:设置能处理的最大连接数,listen()并未开始接受连线,只是设置sockect为listen模式
int listen(int sockfd, int backlog);
// sockfd是socket系统调用返回的服务器端socket描述符;backlog指定在请求队列中允许的最大请求数    


5.accept:用来接受socket连接
int accept(int sockfd, struct sockaddr *addr, int *addrlen);
//sockfd是被监听的服务器socket描述符,addr通常是一个指向sockaddr_in变量的指针,该变量用来存放提出连接请求的客户端地址;addrten通常为一个指向值为sizeof(struct sockaddr_in)的整型指针变量。错误发生时返回一个-1并且设置相应的errno值


6.send:发送数据        
int send(int sockfd, const void *msg, int len, int flags);
//sockfd是你想用来传输数据的socket描述符,msg是一个指向要发送数据的指针。 len是以字节为单位的数据的长度。flags一般情况下置为0


7.recv:接受数据        
int recv(int sockfd,void *buf,int len,unsigned int flags);
//sockfd是接受数据的socket描述符;buf 是存放接收数据的缓冲区;len是缓冲的长度。flags也被置为0。recv()返回实际上接收的字节数,或当出现错误时,返回-1并置相应的errno值。


8.sendto:发送数据,用于面向非连接的socket(SOCK_DGRAM/SOCK_RAW)
int sendto(int sockfd, const void *msg,int len,unsigned int flags,const struct sockaddr *to,int tolen);
//该函数比send()函数多了两个参数,to表示目地机的IP地址和端口号信息,而tolen常常被赋值为sizeof (struct sockaddr)。sendto 函数也返回实际发送的数据字节长度或在出现发送错误时返回-1。


9.recvform: 接受数据,用于面向非连接的socket(SOCK_DGRAM/SOCK_RAW)
int recvfrom(int sockfd,void *buf,int len,unsigned int flags,struct sockaddr *from,int *fromlen);
//from是一个struct sockaddr类型的变量,该变量保存源机的IP地址及端口号。fromlen常置为sizeof(struct sockaddr)。当recvfrom()返回时,fromlen包含实际存入from中的数据字节数。Recvfrom()函数返回接收到的字节数或当出现错误时返回-1,并置相应的errno   


几个字节顺序转换函数:
    htons()   --"Host to Network Short" ; htonl()--"Host to Network Long"
    ntohs()   --"Network to Host Short" ; ntohl()--"Network to Host Long"
    在这里, h表示"host" ,n表示"network",s 表示"short",l表示 "long"。
地址转换函数:
    in_addr_t inet_addr(const char * strptr);
    将字符串IP地址转换为IPv4地址结构in_addr值
    char * inet_ntoa(struct in_addr * addrptr);
    将IPv4地址结构in_addr值转换为字符串IP
    域名和IP地址的转换:
    struct hostent *gethostbyname(const char *name);
    函数返回一种名为hostent的结构类型,它的定义如下:
    struct hostent
    {
         char *h_name;        /* 主机的官方域名 */
         char **h_aliases;    /* 一个以NULL结尾的主机别名数组 */
         int    h_addrtype;   /* 返回的地址类型,在Internet环境下为AF-INET */
         int h_length;        /*地址的字节长度 */
         char **h_addr_list;  /* 一个以0结尾的数组,包含该主机的所有地址*/
  };
    #define h_addr h_addr_list[0] /*在h-addr-list中的第一个地址*/


下面给出两个示例,一个是面向TCP数据流的socket通信,一个是面向UDP数据报的socket通信
1.面向TCP数据流的socket通信的演示程序由基于TCP的服务器和基于TCP的客户端程序组成。
    TCP的服务器程序结构:
    1.创建一个socket,用函数socket()
    2.绑定IP地址、端口信息到socket上,用函数bind()
    3.设置允许的最大连接数,用函数listen()
    4.接受客户端的连接,用函数accept()
    5.收发数据,用send()、recv()或者read()、write()
    6.关闭网络连接
   TCP的客户端程序结构:
    1.创建一个socket,用函数socket()
    2.设置要连接的服务器的IP地址和端口属性
    3.连接服务器,用函数connet()
    4.收发数据,用send()、recv()或者read()、write()
    5.关闭网络连接
TCP服务器和TCP客户端的通信图如下:
wKioL1cLG7SiJFhMAAC1zSaJlAk808.jpg
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h> //inet_ntoa()函数的头文件
#define portnumber 3333 //定义端口号:(0-1024为保留端口号,最好不要用)
int main(int argc, char *argv[])
{
     int sockfd,new_fd;
     struct sockaddr_in server_addr; //描述服务器地址
     struct sockaddr_in client_addr; //描述客户端地址
     int sin_size;
     char hello[]="Hello! Are You Fine?\n";
    /* 服务器端开始建立sockfd描述符 */
    if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1) // AF_INET:IPV4;SOCK_STREAM:TCP
{
        fprintf(stderr,"Socket error:%s\n\a",strerror(errno));
        exit(1);
     }
    /* 服务器端填充 sockaddr结构 */
     bzero(&server_addr,sizeof(struct sockaddr_in)); // 初始化,置0
     server_addr.sin_family=AF_INET; // Internet
     server_addr.sin_addr.s_addr=htonl(INADDR_ANY); // (将本机器上的long数据转化为网络上的long数据)和任何主机通信 //INADDR_ANY 表示可以接收任意IP地址的数据,即绑定到所有的IP
     //server_addr.sin_addr.s_addr=inet_addr("192.168.1.1"); //用于绑定到一个固定IP,inet_addr用于把数字加格式的ip转化为整形ip
     server_addr.sin_port=htons(portnumber); // (将本机器上的short数据转化为网络上的short数据)端口号
     /* 捆绑sockfd描述符到IP地址 */
     if(bind(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr))==-1)
     {
        fprintf(stderr,"Bind error:%s\n\a",strerror(errno));
        exit(1);
     }
   
     /* 设置允许连接的最大客户端数 */
     if(listen(sockfd,5)==-1)
    {
         fprintf(stderr,"Listen error:%s\n\a",strerror(errno));
         exit(1);
     }
    while(1)
    {
        /* 服务器阻塞,直到客户程序建立连接 */
  sin_size=sizeof(struct sockaddr_in);
        if((new_fd=accept(sockfd,(struct sockaddr *)(&client_addr),&sin_size))==-1)
        {
            fprintf(stderr,"Accept error:%s\n\a",strerror(errno));
            exit(1);
         }
        fprintf(stderr,"Server get connection from %s\n",inet_ntoa(client_addr.sin_addr)); // 将网络地址转换成.字符串,并打印到输出终端
         //向客户端程序写入hello数组里的字符
         if(write(new_fd,hello,strlen(hello))==-1)
        {
            fprintf(stderr,"Write Error:%s\n",strerror(errno));
            exit(1);
        }
         /* 这个通讯已经结束 */
         close(new_fd);
         /* 循环下一个 */
    }
     /* 结束通讯 */
    close(sockfd);
    exit(0);
}



TCP客户端的代码TCPclient.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#define portnumber 3333
int main(int argc, char *argv[])
{
int sockfd;
char buffer[1024];
struct sockaddr_in server_addr;
struct hostent *host;
int nbytes;
        /* 使用hostname查询host 名字 */
if(argc!=2)
{
  fprintf(stderr,"Usage:%s hostname \a\n",argv[0]);
  exit(1);
}
if((host=gethostbyname(argv[1]))==NULL)
{
  fprintf(stderr,"Gethostname error\n");
  exit(1);
}
/* 客户程序开始建立 sockfd描述符 */
if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1) // AF_INET:Internet;SOCK_STREAM:TCP
{
  fprintf(stderr,"Socket Error:%s\a\n",strerror(errno));
  exit(1);
}
/* 客户程序填充服务端的资料 */
bzero(&server_addr,sizeof(server_addr)); // 初始化,置0
server_addr.sin_family=AF_INET;          // IPV4
server_addr.sin_port=htons(portnumber);  // (将本机器上的short数据转化为网络上的short数据)端口号
server_addr.sin_addr=*((struct in_addr *)host->h_addr); // IP地址
  
/* 客户程序发起连接请求 */
if(connect(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr))==-1)
{
  fprintf(stderr,"Connect Error:%s\a\n",strerror(errno));
  exit(1);
}
/* 连接成功了 */
if((nbytes=read(sockfd,buffer,1024))==-1)
{
  fprintf(stderr,"Read Error:%s\n",strerror(errno));
  exit(1);
}
buffer[nbytes]='\0';
printf("I have received:%s\n",buffer);
/* 结束通讯 */
close(sockfd);
exit(0);
}




编译TCPserver.c和TCPclient.c
gcc TCPserver.c -o TCPserver
gcc TCPclient.c -o TCPclient
运行tcp服务器段程序和客户端程序,显示过程截图如下:
wKioL1cLjRGjL8RlAACHfUzPRDk712.jpg
wKiom1cLjHiS46xfAABvYDnj8XM227.jpg

2.面向UDP数据报的socket通信的演示程序由基于UCP的服务器和基于UDP的客户端程序组成。
    UDP的服务器程序结构:
    1.创建一个socket,用函数socket()
    2.绑定IP地址、端口信息到socket上,用函数bind()
    3.循环接受数据,用函数recvform()
    4.关闭网络连接
   UDP的客户端程序结构:
    1.创建一个socket,用函数socket()
    2.设置要连接的服务器的IP地址和端口属性
    3.发送数据,用函数sento()
    4.关闭网络连接
UDP服务器和UDP客户端的通信图如下:
25695950_1411196776Sol9.jpg
下面贴出UDP服务器的代码UDPserver.c:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <arpa/inet.h>
#define SERVER_PORT 8888
#define MAX_MSG_SIZE 1024
void udps_respon(int sockfd)
{
struct sockaddr_in addr;
int addrlen,n;
char msg[MAX_MSG_SIZE];
while(1)
{ /* 从网络上读,并写到网络上 */
  bzero(msg,sizeof(msg)); // 初始化,清零
  addrlen = sizeof(struct sockaddr);
  n=recvfrom(sockfd,msg,MAX_MSG_SIZE,0,(struct sockaddr*)&addr,&addrlen); // 从客户端接收消息
  msg[n]=0;
  /* 显示服务端已经收到了信息 */
  fprintf(stdout,"Server have received %s",msg); // 显示消息
}
}
int main(void)
{
int sockfd;
struct sockaddr_in addr;
/* 服务器端开始建立socket描述符 */
sockfd=socket(AF_INET,SOCK_DGRAM,0);
if(sockfd<0)
{
  fprintf(stderr,"Socket Error:%s\n",strerror(errno));
  exit(1);
}
/* 服务器端填充 sockaddr结构 */
bzero(&addr,sizeof(struct sockaddr_in));
addr.sin_family=AF_INET;
addr.sin_addr.s_addr=htonl(INADDR_ANY);
addr.sin_port=htons(SERVER_PORT);
/* 捆绑sockfd描述符 */
if(bind(sockfd,(struct sockaddr *)&addr,sizeof(struct sockaddr_in))<0)
{
  fprintf(stderr,"Bind Error:%s\n",strerror(errno));
  exit(1);
}
udps_respon(sockfd); // 进行读写操作
close(sockfd);
}



UDP客户端的代码UDPclient.c:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <arpa/inet.h>
#define SERVER_PORT 8888
#define MAX_BUF_SIZE 1024
void udpc_requ(int sockfd,const struct sockaddr_in *addr,int len)
{
char buffer[MAX_BUF_SIZE];
int n;
while(1)
{  /* 从键盘读入,写到服务端 */
  printf("Please input char:\n");
  fgets(buffer,MAX_BUF_SIZE,stdin);
  sendto(sockfd,buffer,strlen(buffer),0,(struct sockaddr*)addr,len);
  bzero(buffer,MAX_BUF_SIZE);
}
}
int main(int argc,char **argv)
{
int sockfd;
struct sockaddr_in addr;
if(argc!=2)
{
  fprintf(stderr,"Usage:%s server_ip\n",argv[0]);
  exit(1);
}
/* 建立 sockfd描述符 */
sockfd=socket(AF_INET,SOCK_DGRAM,0);
if(sockfd<0)
{
  fprintf(stderr,"Socket Error:%s\n",strerror(errno));
  exit(1);
}
/* 填充服务端的资料 */
bzero(&addr,sizeof(struct sockaddr_in));
addr.sin_family=AF_INET;
addr.sin_port=htons(SERVER_PORT);
if(inet_aton(argv[1],&addr.sin_addr)<0)  /*inet_aton函数用于把字符串型的IP地址转化成网络2进制数字*/
{
  fprintf(stderr,"Ip error:%s\n",strerror(errno));
  exit(1);
}
udpc_requ(sockfd,&addr,sizeof(struct sockaddr_in)); // 进行读写操作
close(sockfd);
}




编译UDPserver.c和UDPclient.c
gcc UDPserver.c -o UDPserver
gcc UDPclient.c -o UDPclient
运行udp服务器段程序和客户端程序,显示过程截图如下:
wKiom1cLq06xemRxAACvAnearmI841.jpg


运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-202852-1-1.html 上篇帖子: dnsmasq配置 下篇帖子: Tor on Ubuntu Lucid Lynx 网络编程 Linux
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表