Linux下的socket网络编程

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客户端的通信图如下:

 #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

#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服务器段程序和客户端程序,显示过程截图如下:

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客户端的通信图如下:


下面贴出UDP服务器的代码UDPserver.c:

#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:

 #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服务器段程序和客户端程序,显示过程截图如下:

时间: 2024-10-16 16:23:18

Linux下的socket网络编程的相关文章

linux下简单socket网络编程

在进行socket网络编程时, 我们需要了解一些必备的知识,例如什么是socket,ipv4的地址结构,套接字类型等等,不然上来直接看代码就会晕,当初学习网络编程时,看书上的例子,总有感觉书上讲的都很简要.再或者讲的原理太多把人绕晕.我这里只想让大家简单知道怎么使用socket进行网络编程并且给出的例子可以直接使用参考. 1. 什么是socket (1) socket 可以看成是用户进程与网络协议栈的编程接口.就是说应用层可以看成是用户进程,传输层网络层数据链路层看成网络协议栈,因为这三个层的传

windows下的socket网络编程(入门级)

windows下的socket网络编程 clinet.c 客户端 server.c 服务器端 UDP通信的实现 代码如下 已经很久没有在windows下编程了,这次因为需要做一个跨平台的网络程序,就先写了个简单的winSocket网路通信的例子,以便以后用到的时候有个参考. windows下使用winsock编程与linux/unix的区别在于windows下需要先有一个初始化的操作,结束的时候需要一个清理的操作.还有windows下编译的时候需要连接ws32_lib库. 大致过程如下 1.初始

windows下的socket网络编程

windows下的socket网络编程 windows下的socket网络编程 clinet.c 客户端 server.c 服务器端 UDP通信的实现 代码如下 已经很久没有在windows下编程了,这次因为需要做一个跨平台的网络程序,就先写了个简单的winSocket网路通信的例子,以便以后用到的时候有个参考. windows下使用winsock编程与linux/unix的区别在于windows下需要先有一个初始化的操作,结束的时候需要一个清理的操作.还有windows下编译的时候需要连接ws

Linux下高并发网络编程

1.修改用户进程可打开文件数限制 在Linux平台上,无论编写客户端程序还是服务端程序,在进行高并发TCP连接处理时, 最高的并发数量都要受到系统对用户单一进程同时可打开文件数量的限制(这是因为系统 为每个TCP连接都要创建一个socket句柄,每个socket句柄同时也是一个文件句柄). 可使用ulimit命令查看系统允许当前用户进程打开的文件数限制: [[email protected] ~]$ ulimit -n 1024 这表示当前用户的每个进程最多允许同时打开1024个文件,这1024

linux下C语言socket网络编程简例

转自:http://blog.csdn.net/kikilizhm/article/details/7858405 这里给出在linux下的简单socket网络编程的实例,使用tcp协议进行通信,服务端进行监听,在收到客户端的连接后,发送数据给客户端:客户端在接受到数据后打印出来,然后关闭.程序里有详细的说明,其中对具体的结构体和函数的实现可以参考其他资料. 程序说明: 这里服务器的端口号和ip地址使用固定的设置,移植时可以根据具体情况更改,可以改写为参数传递更好,这里为了方便,使用固定的. 移

Linux Socket 网络编程

Linux下的网络编程指的是socket套接字编程,入门比较简单.在学校里学过一些皮毛,平时就是自学玩,没有见识过真正的socket编程大程序,比较遗憾.总感觉每次看的时候都有收获,但是每次看完了之后,过段时间不看,重新拾起这些知识的时候又要从头开始,所以,在这里做个笔记也算是做个模板,以后可以直接从某一个阶段开始接着玩... 1. socket套接字介绍 socket机制其实就是包括socket, bind, listen, connect, accept等函数的方法,其通过指定的函数实现不同

嵌入式 Linux网络编程(一)——Socket网络编程基础

嵌入式 Linux网络编程一--Socket网络编程基础 一.Socket简介 1.网络中进程间通信 本机进程使用进程号区别不同的进程进程间通信方式有管道.信号.消息队列.共享内存.信号量等.网络中进程间的通信首先需要识别进程所在主机在网络中的唯一标识即网络层的IP地址主机上的进程可以通过传输层的协议与端口号识别. 2.Socket原理 Socket是应用层与TCP/IP协议族通信的中间软件抽象层是一种编程接口.Socket屏蔽了不同网络协议的差异支持面向连接(Transmission Cont

Linux程序设计学习笔记----Socket网络编程基础之TCP/IP协议簇

转载请注明出处: ,谢谢! 内容提要 本节主要学习网络通信基础,主要涉及的内容是: TCP/IP协议簇基础:两个模型 IPv4协议基础:IP地址分类与表示,子网掩码等 IP地址转换:点分十进制\二进制 TCP/IP协议簇基础 OSI模型 我们知道计算机网络之中,有各种各样的设备,那么如何实现这些设备的通信呢? 显然是通过标准的通讯协议,但是,整个网络连接的过程相当复杂,包括硬件.软件数据封包与应用程序的互相链接等等,如果想要写一支将联网全部功能都串连在一块的程序,那么当某个小环节出现问题时,整只

linux socket网络编程 常用函数及头文件

转自:http://blog.chinaunix.net/u3/102500/showart_2065640.html 一 三种类型的套接字: 1.流式套接字(SOCKET_STREAM) 提供面向连接的可靠的数据传输服务.数据被看作是字节流,无长度限制.例如FTP协议就采用这种. 2.数据报式套接字(SOCKET_DGRAM) 提供无连接的数据传输服务,不保证可靠性. 3.原始式套接字(SOCKET_RAW) 该接口允许对较低层次协议,如IP,ICMP直接访问. 二 基本套接字系统调有有如下一