Linux系统编程:socket网络编程(操作篇)
一、问题思考
问1.网络通信应用在什么场合?通信的前提是什么?
答1.主要应用在不同主机进程间的互相通信,同一主机的进程也可以使用网络进行通信。通信的前提是如何标识通信进程的唯一,由于不同主机的进程极有可能具有相同的PID,因此,在网络中单单靠PID是无法准确进行标识进程身份的,TCP/IP协议族网络层的IP地址可以唯一的标识连入网络的主机。
问2.socket编程重点是什么?
答2.掌握基于TCP、UDP的S/C架构的编程要点;掌握网络通信方式之间的区别和应用场合。
问3.什么是网络模型?
答3.网络模型主要分为4层,从上至下依次为:应用层、运输层、协议层、链路层。如图所示。
问4.什么是socket套接字?
答4.是一种文件描述符。有三种类型:
①流式套接字(SOCK_STREAM)——过程类似于打电话。提供可靠的、面向连接的通信流,对应使用TCP/IP协议,能够保证数据传输的正确性和顺序性。
②数据报套接字(SOCK_DGRAM)——过程类似于手机之间发短信。无连接服务、数据的传输是独立的不需要经过对方的响应,可靠性无保证。对应使用UDP/IP协议。
③原始套接字(SOCK_RAW)——该套接字直接基于IP地址。
二、TCP、UDP通信编程模型
主要相关操作函数
1. 创建套接字
#include<sys/types.h>
#include<sys/socket.h>
int socket(int protofamil,int type,int protocol);
protofamil 设置协议族即设置socket的地址类型,定义在/usr/include/bits/socket.h 内,有:AF_INET(IPV4——32位IP、16位的端口号)、AF_INET6(IPV6)、AF_LOCAL(Unix域socket)、AF_ROUTE等等; type 常用的有SOCK_STREAM、SOCK_DGRAM和SOCK_RAW; protocol 用来指定socket所使用的传输协议编号,通常此参考不用管它,设为0即可。
参数含义
返回值:成功——socket描述符;失败——(-1)
2.绑定地址
#include<sys/types.h>
#include<sys/socket.h>
int bind(int sockfd,struct sockaddr * my_addr,int addrlen);
sockfd:即socket描述字,它是通过socket()函数创建了,唯一标识一个socket。bind()函数就是将给这个描述字绑定一个名字。
my_addr:指向sockfd要绑定的协议地址。IPv4对应的协议地址结构为:
struct sockaddr_in {
sa_family_t sin_family; /* address family: AF_INET */
in_port_t sin_port; /* port in network byte order */
struct in_addr sin_addr; /* internet address */
};
/* Internet address. */
struct in_addr {
uint32_t s_addr; /* address in network byte order */
};
addrlen:对应的是地址的长度,使用sizeof来计算。
参数含义
返回值:成功——(0),失败——(-1)
3.监听
#include<sys/socket.h>
int listen(int sockfd,int backlog);
参数backlog是能处理的最大连接数目,如果连接数目达此上限则client端将出错。
返回值:成功——(0),失败——(-1)
4.建立连接
#include<sys/types.h>
#include<sys/socket.h>
int connect (int sockfd,struct sockaddr * serv_addr,int addrlen);
参数:sockaddr 服务器的socket地址,addrlen为socket地址的长度。
返回值:成功——(0),失败——(-1)
5.接受请求
#include<sys/types.h>
#include<sys/socket.h>
int accept(int sockfd,struct sockaddr * addr,int * addrlen);
参数:sockfd ——用来监听客户的一个端口号;addr——客户机的地址数据;addrlen即前面这个地址数据的大小
返回值:成功——已建立连接的新的套接字描述符,可用于read/write函数;失败——(-1)
6.读写操作
第①组:用于面向连接的TCP编程中
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);
返回值:成功——字节数;失败——(-1)
第②组:用于面向连接的TCP编程中
#include<sys/types.h>
#include<sys/socket.h>
int recv(int sockfd,void *buf,int len,unsigned int flags);
int send(int sockfd,const void * msg,int len,unsigned int falgs);
参数:buf/msg分别指向接收缓冲区和发送的信息,len是接收或发送的最大长度,flags通常设置为0。
返回值:成功——字节数;失败——(-1)
第③组:可用于面向连接和无连接的套接字
#include<sys/types.h>
#include<sys/socket.h> int recvfrom(int sockfd,void *buf,int len,unsigned int flags ,struct sockaddr *from ,int *fromlen);
int sendto ( int sockfd, const void * msg, int len, unsigned int flags, const struct sockaddr * to , int tolen ) ; int recvmsg(int sockfd,struct msghdr *msg,unsigned int flags);
int sendmsg(int sockfd,const strcut msghdr *msg,unsigned int flags);
返回值:成功——字节数;失败——(-1)
7.关闭操作
#include<unistd.h>
int close(int fd);
8.其他:申请共享内存
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);
函数功能:得到一个共享内存标识符
参数含义:
key:常设置为IPC_PRIVATE(),建立新共享内存对象,大于0的32位整数:视参数shmflg来确定操作。
size:新建的共享内存大小,以字节为单位
shmflg:根据如下进行设置
:取共享内存标识符,若不存在则函数会报错;
IPC_CREAT:当shmflg&IPC_CREAT为真时,如果内核中不存在键值与key相等的共享内存,则新建一个共享内存;如果存在这样的共享内存,返回此共享内存的标识符;
IPC_CREAT|IPC_EXCL:如果内核中不存在键值 与key相等的共享内存,则新建一个共享内存;如果存在这样的共享内存则报错;
共享内存的存取权限:S_IRUSR|S_IWUSR...
shmget
#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);
函数功能:映射共享内存区到调用该函数的进程地址空间
参数含义:
shmid:共享内存标识符,由shmget函数产生
shmaddr:通常指定为NULL()让内核自己决定一个合适的地址位置
shmflg:如果设置为SHM_RDONLY-只读模式,其他值为读写模式
成功:返回共享内存地址
shmat
#include <sys/types.h>
#include <sys/shm.h>
int shmdt(const void *shmaddr);
函数功能:断开共享内存连接
参数分析:
shmaddr:要断开共享内存的起始地址
shmdt
三、socket编程框架与示例
1.TCP网络编程设计:TCP并发服务器设计——每一个客户机的请求由服务器创建的一个子进程/线程来处理,不是用服务器进程来处理(TCP循环服务器:一次只能处理一个客户机额请求)。优点:可以“同时”响应多个客户端的服务请求。
代码功能实现:客户端A和B通过服务端进行通信
//编译指令:gcc -o server server.c
//执行指令:./server
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h> //数据类型定义
#include<sys/stat.h>
#include<netinet/in.h> //定义数据结构sockaddr_in
#include<sys/socket.h> //提供socket函数及数据结构
#include<string.h>
#include<unistd.h>
#include<signal.h>
#include<sys/ipc.h>
#include<errno.h>
#include<sys/shm.h>
#include<time.h>
#define PERM S_IRUSR|S_IWUSR
#define MYPORT 5000 //宏定义定义通信端口
#define MAX_CLIENT 5 //宏定义,定义服务程序可以连接的最大客户数量
#define WELCOME "chating room:" //宏定义,当客户端连接服务端时,想客户发送此欢迎字符串 //转换函数,将int类型转换成char *类型
static void itoa(int i,char*string)
{
int power,j;
j=i;
for(power=;j>=;j/=)
power*=;
for(;power>;power/=)
{
*string++=''+i/power;
i%=power;
}
*string='\0';
} //得到当前系统时间,并将时间转换成字符串形式存放time_str指向处。
void get_cur_time(char * time_str)
{
time_t timep;
struct tm *p_curtime;
char *time_tmp;
time_tmp=(char *)malloc();
memset(time_tmp,,);
memset(time_str,,);
time(&timep);
p_curtime = localtime(&timep);
strcat(time_str," (");
itoa(p_curtime->tm_hour,time_tmp);
strcat(time_str,time_tmp);
strcat(time_str,":");
itoa(p_curtime->tm_min,time_tmp);
strcat(time_str,time_tmp);
strcat(time_str,":");
itoa(p_curtime->tm_sec,time_tmp);
strcat(time_str,time_tmp);
strcat(time_str,")");
free(time_tmp);
} //创建共享存储区
key_t shm_create()
{
key_t shmid; //key_t实际上是int型
if((shmid = shmget(IPC_PRIVATE,,PERM)) == -)
{
fprintf(stderr,"Create Share Memory Error:%s\n\a",strerror(errno));
exit();
}
return shmid; //返回共享内存的标识符
} //端口绑定函数:创建套接字,并绑定到指定端口
int bindPort(unsigned short int port)
{
int sockfd;
struct sockaddr_in my_addr;
sockfd = socket(AF_INET,SOCK_STREAM,);//创建基于流套接字
my_addr.sin_family = AF_INET;//IPv4协议族
my_addr.sin_port = htons(port);//端口转换
my_addr.sin_addr.s_addr = INADDR_ANY;
bzero(&(my_addr.sin_zero),);
if(bind(sockfd,(struct sockaddr*)&my_addr,sizeof(struct sockaddr)) == -)
{
perror("bind");
exit();
}
printf("bing success!\n");
return sockfd;
} int main(int argc, char *argv[])
{
int sockfd,clientfd,sin_size,recvbytes; //定义监听套接字、客户套接字
pid_t pid,ppid;
char *buf, *r_addr, *w_addr, *temp, *time_str;//="\0"; //定义临时存储区
struct sockaddr_in their_addr; //定义地址结构
key_t shmid;
shmid = shm_create(); //创建共享存储区
temp = (char *)malloc();
time_str=(char *)malloc(); sockfd = bindPort(MYPORT);//绑定端口 while()
{
if(listen(sockfd,MAX_CLIENT) == -)//在指定端口上监听
{
perror("listen");
exit();
}
printf("listening......\n");
if((clientfd = accept(sockfd,(struct sockaddr*)&their_addr,&sin_size)) == -)//接收客户端连接
{
perror("accept");
exit();
}
printf("accept from:%d\n",inet_ntoa(their_addr.sin_addr));
send(clientfd,WELCOME,strlen(WELCOME),);//发送问候信息
buf = (char *)malloc();
ppid = fork(); //创建子进程
if(ppid == )
{
pid = fork(); //创建子进程 -- TCP并发服务器
while()
{
if(pid > )
{
//父进程:接收信息
memset(buf,,);
if((recvbytes = recv(clientfd,buf,,)) <= )
{
perror("recv1");
close(clientfd);
raise(SIGKILL);
exit();
}
//write buf's data to share memory
w_addr = shmat(shmid, , );
memset(w_addr, '\0', );
strncpy(w_addr, buf, );
get_cur_time(time_str);
strcat(buf,time_str);
printf(" %s\n",buf);
}
else if(pid == )
{
//子进程用于发送信息
sleep();
r_addr = shmat(shmid, , );
if(strcmp(temp,r_addr) != )
{
strcpy(temp,r_addr);
get_cur_time(time_str);
strcat(r_addr,time_str);
if(send(clientfd,r_addr,strlen(r_addr),) == -)
{
perror("send");
}
memset(r_addr, '\0', );
strcpy(r_addr,temp);
}
}
else
perror("fork");
}
}
}
printf("------------------------------\n");
free(buf);
close(sockfd);
close(clientfd);
return ;
}
server.c
//编译指令:gcc -o client client.c
//执行指令:./client 192.168.1.107 5000 user_name
#include<stdio.h>
#include<netinet/in.h> //定义数据结构sockaddr_in
#include<sys/socket.h> //提供socket函数及数据结构
#include<sys/types.h> //数据类型定义
#include<string.h>
#include<stdlib.h>
#include<netdb.h>
#include<unistd.h>
#include<signal.h>
#include<time.h> int main(int argc, char *argv[])
{
struct sockaddr_in clientaddr;//定义地址结构
pid_t pid;
int clientfd,sendbytes,recvbytes;//定义客户端套接字
struct hostent *host;
char *buf,*buf_r;
if(argc < )
{
printf("usage:\n");
printf("%s host port name\n",argv[]);
exit();
}
host = gethostbyname(argv[]);
if((clientfd = socket(AF_INET,SOCK_STREAM,)) == -) //创建客户端套接字
{
perror("socket\n");
exit();
}
//绑定客户端套接字
clientaddr.sin_family = AF_INET;
clientaddr.sin_port = htons((uint16_t)atoi(argv[]));
clientaddr.sin_addr = *((struct in_addr *)host->h_addr);
bzero(&(clientaddr.sin_zero),);
if(connect(clientfd,(struct sockaddr *)&clientaddr,sizeof(struct sockaddr)) == -) //连接服务端
{
perror("connect error.\n");
exit();
}
buf=(char *)malloc();
memset(buf,,);
buf_r=(char *)malloc();
if( recv(clientfd,buf,,) == -)
{
perror("recv:");
exit();
}
printf("\n%s\n",buf);
pid = fork();//创建子进程
while()
{
if(pid > )
{
//父进程:发送信息
strcpy(buf,argv[]);
strcat(buf,":");
memset(buf_r,,);
fgets(buf_r,,stdin); //从标准输入获取
strncat(buf,buf_r,strlen(buf_r)-); //连接两个字符串
if((sendbytes = send(clientfd,buf,strlen(buf),)) == -)
{
perror("send\n");
exit();
}
}
else if(pid == )
{
//子进程:接收信息
memset(buf,,);
if(recv(clientfd,buf,,) <= )
{
perror("recv:");
close(clientfd);
raise(SIGSTOP);
exit();
}
printf("%s\n",buf);
}
else
perror("fork error.");
}
close(clientfd);
return ;
}
client.c
提示:运行测试程序时,客户端A/B和服务端分别在一个终端页面进行。
2.UDP网络编程设计:UDP循环服务器设计——server每一次从socket上获得client的请求,然后进行处理,再把结果返回给client,如此循环。缺点:如果一个客户端一直占有这个服务端,其他的客户端就不能获得服务端的服务。
//编译指令:gcc -o server server.c
//运行指令:./server
#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 5001
#define MAX_MSG_SIZE 1024 int main(void)
{
int sockfd;
struct sockaddr_in addr,addr1;
int addrlen,n; char msg[MAX_MSG_SIZE]; /* 服务器端建立socket描述符 */
sockfd=socket(AF_INET,SOCK_DGRAM,);
if(sockfd<)
{
fprintf(stderr,"Socket Error:%s\n",strerror(errno));
exit();
} 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); if(bind(sockfd,(struct sockaddr *)&addr,sizeof(struct sockaddr_in))<)
{
fprintf(stderr,"Bind Error:%s\n",strerror(errno));
exit();
} while()
{
bzero(msg,sizeof(msg));
addrlen = sizeof(struct sockaddr);
n=recvfrom(sockfd,msg,MAX_MSG_SIZE,,(struct sockaddr*)&addr1,&addrlen); // 从客户端接收消息
msg[n]=;//添加上字符串结束标志
fprintf(stdout,"Server have received %s",msg); // 显示消息
} close(sockfd);
}
server.c
//编译指令:gcc -o client client.c
//运行指令:./client 192.168.1.107
#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 5001
#define MAX_BUF_SIZE 1024 int main(int argc,char *argv[])
{
int sockfd;
struct sockaddr_in addr;
char buffer[MAX_BUF_SIZE];
int n; if(argc!=)
{
fprintf(stderr,"Usage:%s server_ip\n",argv[]);
exit();
} /* 建立 sockfd描述符 */
sockfd=socket(AF_INET,SOCK_DGRAM,);
if(sockfd<)
{
fprintf(stderr,"Socket Error:%s\n",strerror(errno));
exit();
} bzero(&addr,sizeof(struct sockaddr_in));
addr.sin_family=AF_INET;
addr.sin_port=htons(SERVER_PORT);
if(inet_aton(argv[],&addr.sin_addr)<) //inet_aton函数用于把字符串型的IP地址转化成网络二进制数字
{
fprintf(stderr,"Ip error:%s\n",strerror(errno));
exit();
} while()
{ /* 从键盘读入,写到服务端 */
printf("Please input char:\n");
fgets(buffer,MAX_BUF_SIZE,stdin);
sendto(sockfd,buffer,strlen(buffer),,(struct sockaddr *)(&addr),sizeof(struct sockaddr_in));
bzero(buffer,MAX_BUF_SIZE);
} close(sockfd);
}
client.c
Linux系统编程:socket网络编程(操作篇)的更多相关文章
- Linux学习 : Socket 网络编程入门
一.socket()函数 int socket(int domain, int type, int protocol); domain:即协议域,又称为协议族(family).常用的协议族有,AF_I ...
- Python之路【第七篇】python基础 之socket网络编程
本篇文章大部分借鉴 http://www.cnblogs.com/nulige/p/6235531.html python socket 网络编程 一.服务端和客户端 BS架构 (腾讯通软件:ser ...
- Linux Socket 网络编程
Linux下的网络编程指的是socket套接字编程,入门比较简单.在学校里学过一些皮毛,平时就是自学玩,没有见识过真正的socket编程大程序,比较遗憾.总感觉每次看的时候都有收获,但是每次看完了之后 ...
- Socket网络编程-基础篇
Socket网络编程 网络通讯三要素: IP地址[主机名] 网络中设备的标识 本地回环地址:127.0.0.1 主机名:localhost 端口号 用于标识进程的逻辑地址 有效端口:0~65535 其 ...
- linux下C语言socket网络编程简例
原创文章,转载请注明转载字样和出处,谢谢! 这里给出在linux下的简单socket网络编程的实例,使用tcp协议进行通信,服务端进行监听,在收到client的连接后,发送数据给client:clie ...
- 【linux高级程序设计】(第十三章)Linux Socket网络编程基础 2
BSD Socket网络编程API 创建socket对象 int socket (int __domain, int __type, int __protocol) :成功返回socket文件描述符, ...
- Python全栈【Socket网络编程】
Python全栈[socket网络编程] 本章内容: Socket 基于TCP的套接字 基于UDP的套接字 TCP粘包 SocketServer 模块(ThreadingTCPServer源码剖析) ...
- Socket网络编程(2)--服务端实现
中秋了,首先祝大家中秋快乐,闲着无事在家整一个socket的聊天程序,有点仿QQ界面,就是瞎折腾,不知道最后是不是能将所有功能实现. 如果你对socket不了解,请看这篇文章:http://www.c ...
- Socket网络编程详解
一,socket的起源 socket一词的起源 在组网领域的首次使用是在1970年2月12日发布的文献IETF RFC33中发现的, 撰写者为Stephen Carr.Steve Crocker和Vi ...
- Socket网络编程基本介绍
一,socket的起源 socket一词的起源 在组网领域的首次使用是在1970年2月12日发布的文献IETF RFC33中发现的, 撰写者为Stephen Carr.Steve Crocker和Vi ...
随机推荐
- vue 使用axios 数据请求第三方插件的使用
axios 基于http客户端的promise,面向浏览器和nodejs 特色 浏览器端发起XMLHttpRequests请求 node端发起http请求 支持Promise API 监听请求和返回 ...
- Google’s Project Tango is shutting down because ARCore is already here
https://www.theverge.com/2017/12/15/16782556/project-tango-google-shutting-down-arcore-augmented-rea ...
- 3D 相关
1. STL 2. AMF 3. X3D 网址: http://www.web3d.org/x3d-resources/content/examples/X3dResources.html
- [label][JavaScript][The Defined Guide of JavaScript] 如何声明变量
因为觉得我自己的JavaScript基础很不扎实,或者可以说根本就没有所谓基础,所以就最近一直在看<The Defined Guide of JavaScript> . 在一边看的同时,我 ...
- Windbg and resources leaks in .NET applications 资源汇总
Windows Forms Leaks 1.http://blogs.msdn.com/b/tess/archive/2008/02/04/net-debugging-demos-informatio ...
- ubuntu16.04系统精简
ubuntu16.04系统精简 一.更新系统 apt-get update apt-get dist-upgrade 二.查看所有内核 sudo dpkg --get-selections |grep ...
- ASP Base64位 加密解密
网上找了很多,运行时都会提示某个错误,有点乱.后面找到测试,这个转ASNI码的能实现 sBASE_64_CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabc ...
- WP8.1StoreApp(WP8.1RT)---第三方启动
8.1的协议和wp8是相互通用的 被启动: 相比较wp8而言,基本变化不大,但添加方式更直观了 1:打开Package.appxmanifest 2:切换到"声明"选项卡 3:左侧 ...
- Linq的Join == 两个foreach
因为实在太懒了,很久没动笔,今天强迫自己写一个小短篇. 之前讨论过用SelectMany代替两重的foreach循环.今天我们看一下Join和foreach的关系. 首先是Join的定义 public ...
- java实际项目中interface和abstract interface 区别
参考:https://zhidao.baidu.com/question/424485344260391052.html 这2种有什么区别,根据实际项目经验 帮我解答下 谢谢啊~~~~~~~~~问题补 ...