Linux网络编程简单示例
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客户端的通信图如下:
下面贴出TCP服务器的代码tcp_server.c:
tcp_server.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>
- #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客户端的代码tcp_client.c
tcp_client.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>
- #include <arpa/inet.h> //inet_ntoa()函数的头文件
- #define portnumber 3333 //定义端口号:(0-1024为保留端口号,最好不要用)
- 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);
- }
编译tcp_server.c和tcp_client.c
gcc tcp_server.c -o tcp_server
gcc tcp_client.c -o tcp_client
运行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服务器的代码udp_server.c:
udp_serer.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 //定义端口号:(0-1024为保留端口号,最好不要用)
- #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; //socket描述符
- 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)); // 初始化,置0
- addr.sin_family=AF_INET; // Internet
- addr.sin_addr.s_addr=htonl(INADDR_ANY); // (将本机器上的long数据转化为网络上的long数据)和任何主机通信 //INADDR_ANY 表示可以接收任意IP地址的数据,即绑定到所有的IP
- //addr.sin_addr.s_addr=inet_addr("192.168.1.1"); //用于绑定到一个固定IP,inet_addr用于把数字加格式的ip转化为整形ip
- addr.sin_port=htons(SERVER_PORT); // (将本机器上的short数据转化为网络上的short数据)端口号
- /* 捆绑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客户端的代码udp_client.c:
udp_client.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 //定义端口号:(0-1024为保留端口号,最好不要用)
- #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; //socket描述符
- 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)); // 初始化,置0
- addr.sin_family=AF_INET; // Internet
- addr.sin_port=htons(SERVER_PORT);// (将本机器上的short数据转化为网络上的short数据)端口号
- 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);
- }
编译udp_server.c和udp_client.c
gcc udp_server.c -o udp_server
gcc udp_client.c -o udp_client
运行udp服务器段程序和客户端程序,显示过程截图如下:
Linux网络编程简单示例的更多相关文章
- Linux网络编程(简单的时间获取服务器)
1.一个简单的服务器时间获取程序 服务器和客户端采用UDP通信的方式,来编写一个简单的时间获取应用. 把过程大致理顺一下,首先是服务器端的编写,使用的是迭代的方式,没有并发 先创建一个socket而后 ...
- Linux网络编程:一个简单的正向代理服务器的实现
Linux是一个可靠性非常高的操作系统,但是所有用过Linux的朋友都会感觉到, Linux和Windows这样的"傻瓜"操作系统(这里丝毫没有贬低Windows的意思,相反这应该 ...
- 【linux草鞋应用编程系列】_5_ Linux网络编程
一.网络通信简介 第一部分内容,暂时没法描述,内容实在太多,待后续专门的系列文章. 二.linux网络通信 在linux中继承了Unix下“一切皆文件”的思想, 在linux中要实现网 ...
- Linux网络编程一步一步学【转】
转自:http://blog.chinaunix.net/uid-10747583-id-297982.html Linux网络编程一步一步学+基础 原文地址:http://blogold.chin ...
- linux网络编程-(socket套接字编程UDP传输)
今天我们来介绍一下在linux网络环境下使用socket套接字实现两个进程下文件的上传,下载,和退出操作! 在socket套接字编程中,我们当然可以基于TCP的传输协议来进行传输,但是在文件的传输中, ...
- Linux网络编程&内核学习
c语言: 基础篇 1.<写给大家看的C语言书(第2版)> 原书名: Absolute Beginner's Guide to C (2nd Edition) 原出版社: Sams 作者: ...
- Linux网络编程入门 (转载)
(一)Linux网络编程--网络知识介绍 Linux网络编程--网络知识介绍客户端和服务端 网络程序和普通的程序有一个最大的区别是网络程序是由两个部分组成的--客户端和服务器端. 客户 ...
- [转] - Linux网络编程 -- 网络知识介绍
(一)Linux网络编程--网络知识介绍 Linux网络编程--网络知识介绍客户端和服务端 网络程序和普通的程序有一个最大的区别是网络程序是由两个部分组成的--客户端和服务器端. 客户 ...
- 【转】Linux网络编程入门
(一)Linux网络编程--网络知识介绍 Linux网络编程--网络知识介绍客户端和服务端 网络程序和普通的程序有一个最大的区别是网络程序是由两个部分组成的--客户端和服务器端. 客户 ...
随机推荐
- Linux-ngnix服务(二)
Nginx介绍 特性: 模块化设计,较好的扩展性 高可靠性 支持热部署:不停机更新配置文件,升级版本,更换日志文件 低内存消耗:10000个keep-alive连接模式下的非活动连接,仅需2.5M内存 ...
- VS搭建一个WEB的简历第二天,,,最终目标写个好看的简历,再搭建一个自己脑海的网页
VS做简历的第二天 第二天吸取了第一天的教训写的代码 第一天写的代码https://www.cnblogs.com/pythonywy/p/10816215.html,写了一堆错误T T 非常感谢Li ...
- 我的Python分析成长之路5
一.装饰器: 本质是函数,装饰其他函数,为其他函数添加附加功能. 原则: 1.不能修改被装饰函数的源代码. 2.不能修改被装饰函数的调用方式. 装饰器用到的知识: 1.函数即变量 (把函数体赋值给 ...
- (转)iOS开发之Pch预编译文件的创建
本文转自 http://www.cnblogs.com/496668219long/p/4568265.html 在Xcode6之前,创建一个新工程xcode会在Supporting files文件夹 ...
- 剑指Offer(书):反转链表
题目:输入一个链表,反转链表后,输出新链表的表头. 分析:要分清他的前一个节点和后一个节点,开始的时候前节点为null,后节点为head.next,之后,反转. public ListNode Rev ...
- 牛客网暑期ACM多校训练营(第五场)F take(概率, 递推)
链接: https://www.nowcoder.com/discuss/84119 题意: 给定n个箱子, 每个箱子打开发现钻石的概率P(这里的P要除100), 每个钻石的重量, 有一个人只能持有一 ...
- ecplise建立模拟器,安装apk文件
方法一,把所要安装的apk,例xxx.apk拷贝到sdk下的adb的路径下,也就是和adb在同一个文件夹,比如我的是D:\Program Files\Android\sdk\platform-tool ...
- python--getitem一拦截索引运算
getitem一拦截索引运算 __getitem__方法拦截实例的索引运算.当实例x出现在x[i]这样的索引运算中时,Python会调用这个实例继承的__getitem__方法(如果有的话),把x作为 ...
- SQL2012 分页(最新)
--提取分页数据,返回总记录数 ALTER procedure [dbo].[sp_Common_GetDataPaging_ReturnDataCount] ( @SqlString varchar ...
- iOS阴影
但是如果把masksToBounds设置为yes就没有阴影了 UIButton *view = [[UIButton alloc]initWithFrame:CGRectMake(, , , ...