socket是进程通信的一种方式,通过调用一些API可以实现进程间通信,建立连接以及收发信息的过程如下图所示:

这些函数的用法如下:

1、int socket(int protocolFamily, int type, int protocol); 返回描述符sockfd

  • l  protocolFamily:协议族,AF_INET(IPV4)、AF_INET6(IPV6)、AF_LOCAL(或称AF_UNIX,unix域socket)、AF_ROUTE等。协议族决定了socket的地址类型,在通信中必须采用对应的地址,如AF_INET决定了用IPV4地址(32位)与端口号(16位),AF_UNIX决定了要用一个绝对路径名作为地址
  • l  type:指定socket类型。常用的类型有,SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等
  • l  protocol:协议名

调用socket创建之后,返回的描述符存在于协议族空间中,但没有一个具体的地址,必须要通过bind才行

2、int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

  • l  sockfd:socket描述字,socket的返回值
  • l  addr:一个const struct *addr指针,指向要绑定的sockfd的协议地址。这个地址结构根据地址创建socket时的地址协议族的不同而不同

如, 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 */
}; struct in_addr{
uint32_t s_addr; // address in network byte order
};
  • l  addrlen:对应的地址的长度

3、int listen(int sockfd, int backlog);服务器监听函数

  • l  sockfd:socket描述字,socket的返回值
  • l  backlog: socket 可以排队的最大连接个数

  listen函数将socket变为被动类型的, 等待客户连接请求

4、int connect(int sockfd, const struct *addr, socklen_t addrlen);

  • l  sockfd:socket描述字,socket的返回值
  • l  addr:服务器的socket地址
  • l  addrlen:socket地址的长度

5、int accept(int sockfd, struct sockaddr* addr, socklen_t *addrlen);

  • l  sockfd:socket描述字,socket的返回值
  • l  addr:结果参数,接收一个返回值指向客户端的地址,如果对客户的地址不在乎,可以设置为NULL
  • l  addrlen:结果参数,接收上述addr结构大小,指明addr结构所占字节数,也可NULL

  accept()成功返回一个SOCKET描述符,表示接收到的套接字的描述符。否则返回值INVALID_SOCKET。

6、ssize_t send(int sockfd, const void *buf, ssize_t len, int flags);

  • l  sockfd: socket描述字,socket的返回值
  • l  buf:要发送的数据buffer
  • l  len:要传送的数据大小
  • l  flag:一般取值为0,影响TCP首部的可选部分

  send将自己的数据copy到内核的send buffer,返回值为[-1,size]:

    • 返回-1说明发送数据失败,系统内部出问题了
    • 返回[0,size]:由于send是从内核的send buffer中写数据,那么send buffer中剩下的长度为m,返回min(m, size),返回0就是send buffer没有空间了

7、ssize_t recv(int sockfd, void *buf, ssize_t len, int flags);

各参数意义基本与send相同,recv操作将内核中的数据拷贝到应用程序内部

返回值是[-1,size]:

    • 返回-1表明接收失败,socket失效、recv操作由于系统内部原因中断等原因
    • 返回0表明没有数据,在TCP中接收发送有一个timeout,当timeout的时候还没有数据返回0
    • 返回[1,size]:recv的操作时从内核拷贝数据,数据有多少拷贝多少,内核现在收到长度为m的数据,返回min(m, size);

8、int close(int fd);

一个echo小例子,客户端向服务器发送什么服务器就给客户端返回什么信息:

1、服务器代码:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<sys/types.h> // 基本系统数据类型,是系统的基本系统数据类型的头文件
#include<sys/socket.h>
#include<netinet/in.h> // 互联网地址族
#include<arpa/inet.h> //IP地址转换函数inet_pton
#include<unistd.h> //close #include<iostream> int main(int argc, char** argv){
int socketfd, bindfd, connectfd;
char buffer[];
struct sockaddr_in serverAddress;
int sendSize, recvSize; //printf("================== create socket ======================\n");
// create socket
socketfd = socket(AF_INET, SOCK_STREAM, );
if(socketfd == -){
printf("Create socket error:%s(errno:%d)\n", strerror(errno),errno);
exit();
} //printf("================== set address ========================\n");
//set server address
memset(&serverAddress, , sizeof(serverAddress));
serverAddress.sin_family = AF_INET;
serverAddress.sin_port = htons();
//serverAddress.sin_addr.s_addr = htonl(127.0.0.1);
inet_pton(AF_INET, "127.0.0.1", &serverAddress.sin_addr); std::cout << serverAddress.sin_addr.s_addr << '\n'; //printf("================== bind address ========================\n");
// bind address to the socket
bindfd = bind(socketfd, (struct sockaddr*)&serverAddress, sizeof(serverAddress));
if(bindfd == -){
printf("bind socket error:%s(errno:%d)\n", strerror(errno),errno);
exit();
} //printf("================== listen ========================\n");
if(listen(socketfd, ) == -){
printf("listen socket error:%s(errno:%d)\n",strerror(errno),errno);
exit();
} printf("================== waiting connect ========================\n");
while(){
sleep();
// recvive a connect and accept
connectfd = accept(socketfd, (struct sockaddr*)NULL, NULL);
if(connectfd == -){
printf("connet socket error:%s(errno:%d)\n",strerror(errno),errno);
continue;
} // receive data
recvSize = recv(connectfd, buffer, , );
if(recvSize == -){
printf("recvive data error:%s(errno:%d)\n",strerror(errno),errno);
continue;
} printf("%s\n", buffer); // send data
sendSize = send(connectfd, buffer, , );
if(sendSize == -){
printf("send data error:%s(errno:%d)\n",strerror(errno),errno);
continue;
}
close(connectfd); }
close(socketfd); }

2、客户端代码:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<sys/types.h> // 基本系统数据类型,是系统的基本系统数据类型的头文件
#include<sys/socket.h>
#include<netinet/in.h> // 互联网地址族
#include<arpa/inet.h> //IP地址转换函数inet_pton
#include<unistd.h> //close #include<iostream> int main(){
int socketfd, connectfd;
int sendSize, recvSize; struct sockaddr_in serverAddress; char sendBuf[];
char recvBuf[]; printf("================== create socket ========================\n");
socketfd = socket(AF_INET, SOCK_STREAM, );
if(socketfd == -){
printf("create socket error:%s(error%d)\n", strerror(errno),errno);
exit();
} memset(&serverAddress, , sizeof(serverAddress));
serverAddress.sin_family = AF_INET;
serverAddress.sin_port = htons();
//serverAddress.sin_addr.s_addr = htonl("127.0.0.1");
inet_pton(AF_INET, "127.0.0.1", &serverAddress.sin_addr); std::cout << serverAddress.sin_addr.s_addr << '\n'; connectfd = connect(socketfd, (struct sockaddr*)&serverAddress, sizeof(serverAddress));
if(connectfd == -){
printf("connect server error:%s(error(%d))\n", strerror(errno), errno);
exit();
} printf("send message to server:\n");
//std::cin >> sendBuf;
fgets(sendBuf, , stdin);
//printf("%s\n", sendBuf);
sendSize = send(socketfd, sendBuf, strlen(sendBuf), );
if(sendSize == -){
printf("send data error:%s(error%d)\n", strerror(errno), errno);
exit();
} printf("wait echo from server:\n"); sleep();
recvSize = recv(socketfd, recvBuf, sizeof(recvBuf), );
if(recvSize < ){
printf("recvive echo error:%s(error%d)\n", strerror(errno), errno);
exit();
}
printf("%s", recvBuf); close(socketfd); }

在这过程中遇到的几个问题:

1、(客户端)errno 111:connection refused

  这个问题说明客户端没有找到应该连接的端口,需要做到:

  • 确保服务端在相应的端口监听;
  • 关闭防火墙(ubuntu下面的命令:sudo ufw disable);
  • 而且server端要 sudo 运行;

  由于我客户端和服务器端口号不匹配,就出现了这个问题

2、(服务器)errno 14:bad address

  accept()函数的第二个参数指的是一个接收返回结果的缓存区,表示的是本次连接的客户端的地址,如果无所谓客户端地址可以写为NULL,而一旦给这个参数赋了非NULL得值,但是这个缓存区又不可写的话,就会出现bad address报错

3、(服务器)errno 107: transport endpoint is not connected.

  这个问题,是由于我服务器端的recv和send函数的第一个参数写的本地socket描述符造成的,事实上,这应该是已连接的socket描述符

网络编程学习笔记:linux下的socket编程的更多相关文章

  1. Linux下TCP网络编程与基于Windows下C#socket编程间通信

    一.linux下TCP网络编程基础,需要了解相关函数 Socket():用于套接字初始化. Bind():将 socket 与本机上的一个端口绑定,就可以在该端口监听服务请求. Listen():使s ...

  2. Linux下Golang Socket编程原理分析与代码实现

    在POSIX标准推出后,socket在各大主流OS平台上都得到了很好的支持.而Golang是自带Runtime的跨平台编程语言,Go中提供给开发者的Socket API是建立在操作系统原生Socket ...

  3. MongoDB学习笔记—Linux下搭建MongoDB环境

    1.MongoDB简单说明 a MongoDB是由C++语言编写的一个基于分布式文件存储的开源数据库系统,它的目的在于为WEB应用提供可扩展的高性能数据存储解决方案. b MongoDB是一个介于关系 ...

  4. Linux 程序设计学习笔记----Linux下文件类型和属性管理

    转载请注明出处:http://blog.csdn.net/suool/article/details/38318225 部分内容整理自网络,在此感谢各位大神. Linux文件类型和权限 数据表示 文件 ...

  5. Java学习笔记——Linux下安装配置tomcat

    朝辞白帝彩云间,千里江陵一日还. 两岸猿声啼不住,轻舟已过万重山. ——早发白帝城 首先需要安装配置JDK,这里简单回顾下.Linux下用root身份在/opt/文件夹下创建jvm文件夹,然后使用ta ...

  6. LINUX学习笔记——LINUX下EXP命令全库备份数据库文件

    LINUX下EXP命令全库备份数据库文件 1)建立备份目录,目录操作权限授权给Oracle用户 mkdir /backup  --创建backup文件夹 cd  /   --进入cd语句 ls  -l ...

  7. Socket编程学习之道:揭开Socket编程的面纱

    对TCP/IP.UDP.Socket编程这些词你不会非常陌生吧?随着网络技术的发展.这些词充斥着我们的耳朵. 那么我想问: 1.         什么是TCP/IP.UDP? 2.         S ...

  8. 网络编程学习笔记-linux常用的网络命令

    网络参数设置命令 所有时刻如果你想要做好自己的网络参数设置,包括IP参数.路由参数和无线网络等,就得要了解下面这些相关的命令才行.其中Route及ip这两条命令是比较重要的.当然,比较早期的用法,我们 ...

  9. LINUX 下 ipv6 socket 编程

    大家都知道,随着互联网上主机数量的增多,现有的32位IP地址已经不够用了,所以推出了下一代IP地址IPv6,写网络程序的要稍微改变一下现有的网络程序适应IPv6网络是相当容易的事.对于我们来说就是IP ...

随机推荐

  1. squid介绍及其简单配置

    squid的简单介绍 squid的概念 squid是一种用来缓存Internet数据的软件.接受来自人们需要下载的目标(object)的请求并适当的处理这些请求.也就是说,如果一个人想下载一web界面 ...

  2. Nodejs学习笔记(十二)--- 定时任务(node-schedule)

    目录 写在之前 Cron风格定时器 通配符解释 范围触发 递归规则定时器 对象文本语法定时器 取消定时器 写在之后 写在之前 在实际开发项目中,会遇到很多定时任务的工作.比如:定时导出某些数据.定时发 ...

  3. 深入Java核心 Java中多态的实现机制(1)

    在疯狂java中,多态是这样解释的: 多态:相同类型的变量,调用同一个方法时,呈现出多中不同的行为特征, 这就是多态. 加上下面的解释:(多态四小类:强制的,重载的,参数的和包含的) 同时, 还用人这 ...

  4. Unix系统引导过程(简单步骤)

    1.从MBR中读取引导加载程序(boot loader) 2.初始化内核 3.硬件检测 4.创建内核进程 5.系统管理员干预(仅仅在进入单用户模式或者恢复模式的时候) 6.执行系统启动脚本

  5. Oracle查询实用命令

    1.设置每行的长度: SET LIN[ESIZE] 200; 2.设置分页数量: SET PAGES[IZE] 50; 3.查看表空间相关信息: select file_id, tablespace_ ...

  6. python base64的加密与解密

    Base64编码是一种“防君子不防小人”的编码方式.广泛应用于MIME协议,作为电子邮件的传输编码,生成的编码可逆,后一两位可能有“=”,生成的编码都是ascii字符. 优点:速度快,ascii字符, ...

  7. PHP 汉字数字互转(100以内)| PHP 汉字转数字 | PHP数字转汉字

    <?php function numDatabase(){ $numarr =array(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,2 ...

  8. [Leetcode] Roman to Integer

    Given a roman numeral, convert it to an integer. Input is guaranteed to be within the range from 1 t ...

  9. 操作jQuery

    ==================================== $('img').each(function(){ this.alt='This is image['+n+'] with a ...

  10. BZOJ1493 [NOI2007]项链工厂

    未完待续... 终于改对了 热泪盈眶.jpg 错误原因:pushdown的时候没有判断是否有左右儿子,也没当x=0 return,于是出现一些奇怪的错误 #include<bits/stdc++ ...