linux网络编程之socket编程(二)
2024-08-22 10:38:17
今天继续对socket编程进行研究,这里会真正开如用socket写一个小例子,进入正题:
TCP客户/服务器模型:
关于这个模型的流程这里就不多说了,比较容易理解,下面则利用这种模型来编写一个实际的例子。
回射客户/服务器:
这个例子的效果就是:客户端从命令行获取一行命令,然后发送给服务器端,当服务端接收到这行命令之后,不做任何操作,将其又回送给客户端,然后客户端进行回显,下面则开始一步步来实现这样的效果,来初步感受下Socket编程:
首先编写服务端:echosrv.c
第一步:创建套接字:
关于第一个参数domain,man帮助中也有说明:
但是,AF_INET等价于PF_INET,这里推荐用后者,因为刚好代表protocol family含义,下面代码如下:
第二步:绑定一个地址到套接字上:
首先准备一下第二个参数,也就是要绑定的地址:
其中绑定地址还有其它两种方式:
另外,其实"servaddr.sin_addr.s_addr = htonl(INADDR_ANY);"这种写法是可以省略掉的,因为它是全0,但这里为了显示说明所以保留。
【提示】:关于上面的写法,可以参考博文:http://www.cnblogs.com/webor2006/p/3905799.html 中的地址转换函数
下面开始进行绑定:
第三步:则开始进行监听:
具体代码如下:
其中SOMAXCONN可以从man帮助中查看到:
它代表了socket的并发最大连接个数。
另外还得注意,套接字有被动套接字和主动套接字之分,当调用listen之后,该socket就变动被动套接字了,需要由主动套接字来发起连接,主动套接字是用connect函数来发起连接的。
第四步:从已完成连接队列中返回第一个连接:
接下来,则进行数据的接收,并将数据回显给客户端:
accept函数会返回一个新的套接字,注意:此时的套接字不再是被动套接字,而变为了主动:
可以通过accept的man手册来得知:
下面,则开始从该套接字中读取客户端发过来的数据:
至此,服务端的代码都已经编写完了,下面则先编译一下:
查看man帮助:
于是在代码中加入头:
再次编译:
还是出错,那IPPPOTO_TCP是在哪定义的呢?
可以通过以下命令进行查找:
于是乎,加上该头文件后再编译:
用同样的办法来进行查找:
于是加入它:
再次编译:
还是报错,对于这里面对应的头文件这里就不具体一个个查找了,不然有点充数的嫌疑,将所有头文件加上再次编译:
接下来,开始编写客户端的代码:echocli.c
首先创建一个socket:
第二步开始与服务器进行连接:
【说明】:用connect发起连接的套接字是主动套接字。
连接成功之后,就可以向服务器发送数据了:
另外,服务端在使用资源之后,最后也得关闭掉,所以修改服务端程序如下:
这时,客户端程序也已经编写完成,下面编译运行看一下效果:
也就是第一次客户端输入很长的字符串,而第二次输入很短的字符串时,这时就会输出有问题,照理应该是客户端输入什么,服务端就会回显给客户端,也就是打印两条一模一样的语句,产生这样的问题原因是什么呢?
下面来用图来分析一下:
所以,解决该问题的思路就是每次循环时将值初始化一下既可,修改代码如下:
再次编译运行:
关于这个原因,之后会来解决,先占且不关心,等一会就会正常了,正常运行的效果如下:
这样,就实现了客户端与服务器端的socket通讯了,最终的代码如下:
echosrv.c【服务端】:
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h> #include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h> #define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while() int main(void)
{
int listenfd;
if ((listenfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < )
/* if ((listenfd = socket(PF_INET, SOCK_STREAM, 0)) < 0)*/
ERR_EXIT("socket"); struct sockaddr_in servaddr;
memset(&servaddr, , sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons();
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
/*servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");*/
/*inet_aton("127.0.0.1", &servaddr.sin_addr);*/ if (bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < )
ERR_EXIT("bind");
if (listen(listenfd, SOMAXCONN) < )
ERR_EXIT("listen"); struct sockaddr_in peeraddr;
socklen_t peerlen = sizeof(peeraddr);
int conn;
if ((conn = accept(listenfd, (struct sockaddr*)&peeraddr, &peerlen)) < )
ERR_EXIT("accept"); char recvbuf[];//用来存储客户端发来的数据
while ()
{
memset(recvbuf, , sizeof(recvbuf));
int ret = read(conn, recvbuf, sizeof(recvbuf));//从套接字中读取数据
fputs(recvbuf, stdout);//打印到屏幕上
write(conn, recvbuf, ret);//并且将其又回显给客户端,其第三个参数的长度正好是我们接收到的长度
}
close(conn);
close(listenfd); return ;
}
echocli.c【客户端】:
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h> #include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h> #define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while() int main(void)
{
int sock;
if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < )
ERR_EXIT("socket"); struct sockaddr_in servaddr;
memset(&servaddr, , sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons();
servaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); if (connect(sock, (struct sockaddr*)&servaddr, sizeof(servaddr)) < )
ERR_EXIT("connect"); char sendbuf[] = {};
char recvbuf[] ={};
while (fgets(sendbuf, sizeof(sendbuf), stdin) != NULL)
{
write(sock, sendbuf, strlen(sendbuf));
read(sock, recvbuf, sizeof(recvbuf)); fputs(recvbuf, stdout);
memset(sendbuf, , sizeof(sendbuf));
memset(recvbuf, , sizeof(recvbuf));
} close(sock); return ;
}
好了,今天的内容学到这,下回见~
最新文章
- 伪类before和after
- 课前HTML基础
- FileReader对象
- 解决ListView和ScrollView同时使用时滑动的冲突问题
- 一些java面试题
- PLSQL查询表是否被锁定(转)
- 30天,O2O速成攻略【8.30南京站】
- C++-static的用法
- 通过JAVA代码获取手机的一些基本信息(本机号码,SDK版本,系统版本,手机型号)
- Netty实现高性能RPC服务器
- ReactNative遇到的一些坑
- chrome中安装.crx后缀的离线插件
- word 添加文本框
- poj1426 Find The Multiple(c语言巧解)
- Angular 学习笔记 Material
- 设计Web程序,计算任意两个整数的和,并在网页上显示结果。要求在javabean中实现数据的求和功能。
- zookeeper的安装与配置(单机和集群)
- 夯实基础之--new关键字、instanceOf原理
- 在maven 2工程中加入iTextAsian支持(maven添加自定义jar包到本地仓库)
- Thinkphp自动验证规则