一、问题思考

问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网络编程(操作篇)的更多相关文章

  1. Linux学习 : Socket 网络编程入门

    一.socket()函数 int socket(int domain, int type, int protocol); domain:即协议域,又称为协议族(family).常用的协议族有,AF_I ...

  2. Python之路【第七篇】python基础 之socket网络编程

    本篇文章大部分借鉴 http://www.cnblogs.com/nulige/p/6235531.html python socket  网络编程 一.服务端和客户端 BS架构 (腾讯通软件:ser ...

  3. Linux Socket 网络编程

    Linux下的网络编程指的是socket套接字编程,入门比较简单.在学校里学过一些皮毛,平时就是自学玩,没有见识过真正的socket编程大程序,比较遗憾.总感觉每次看的时候都有收获,但是每次看完了之后 ...

  4. Socket网络编程-基础篇

    Socket网络编程 网络通讯三要素: IP地址[主机名] 网络中设备的标识 本地回环地址:127.0.0.1 主机名:localhost 端口号 用于标识进程的逻辑地址 有效端口:0~65535 其 ...

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

    原创文章,转载请注明转载字样和出处,谢谢! 这里给出在linux下的简单socket网络编程的实例,使用tcp协议进行通信,服务端进行监听,在收到client的连接后,发送数据给client:clie ...

  6. 【linux高级程序设计】(第十三章)Linux Socket网络编程基础 2

    BSD Socket网络编程API 创建socket对象 int socket (int __domain, int __type, int __protocol) :成功返回socket文件描述符, ...

  7. Python全栈【Socket网络编程】

    Python全栈[socket网络编程] 本章内容: Socket 基于TCP的套接字 基于UDP的套接字 TCP粘包 SocketServer 模块(ThreadingTCPServer源码剖析) ...

  8. Socket网络编程(2)--服务端实现

    中秋了,首先祝大家中秋快乐,闲着无事在家整一个socket的聊天程序,有点仿QQ界面,就是瞎折腾,不知道最后是不是能将所有功能实现. 如果你对socket不了解,请看这篇文章:http://www.c ...

  9. Socket网络编程详解

    一,socket的起源 socket一词的起源 在组网领域的首次使用是在1970年2月12日发布的文献IETF RFC33中发现的, 撰写者为Stephen Carr.Steve Crocker和Vi ...

  10. Socket网络编程基本介绍

    一,socket的起源 socket一词的起源 在组网领域的首次使用是在1970年2月12日发布的文献IETF RFC33中发现的, 撰写者为Stephen Carr.Steve Crocker和Vi ...

随机推荐

  1. vue 使用axios 数据请求第三方插件的使用

    axios 基于http客户端的promise,面向浏览器和nodejs 特色 浏览器端发起XMLHttpRequests请求 node端发起http请求 支持Promise API 监听请求和返回 ...

  2. 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 ...

  3. 3D 相关

    1. STL 2. AMF 3. X3D 网址: http://www.web3d.org/x3d-resources/content/examples/X3dResources.html

  4. [label][JavaScript][The Defined Guide of JavaScript] 如何声明变量

    因为觉得我自己的JavaScript基础很不扎实,或者可以说根本就没有所谓基础,所以就最近一直在看<The Defined Guide of JavaScript> . 在一边看的同时,我 ...

  5. 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 ...

  6. ubuntu16.04系统精简

    ubuntu16.04系统精简 一.更新系统 apt-get update apt-get dist-upgrade 二.查看所有内核 sudo dpkg --get-selections |grep ...

  7. ASP Base64位 加密解密

    网上找了很多,运行时都会提示某个错误,有点乱.后面找到测试,这个转ASNI码的能实现 sBASE_64_CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabc ...

  8. WP8.1StoreApp(WP8.1RT)---第三方启动

    8.1的协议和wp8是相互通用的 被启动: 相比较wp8而言,基本变化不大,但添加方式更直观了 1:打开Package.appxmanifest 2:切换到"声明"选项卡 3:左侧 ...

  9. Linq的Join == 两个foreach

    因为实在太懒了,很久没动笔,今天强迫自己写一个小短篇. 之前讨论过用SelectMany代替两重的foreach循环.今天我们看一下Join和foreach的关系. 首先是Join的定义 public ...

  10. java实际项目中interface和abstract interface 区别

    参考:https://zhidao.baidu.com/question/424485344260391052.html 这2种有什么区别,根据实际项目经验 帮我解答下 谢谢啊~~~~~~~~~问题补 ...