Linux高级编程--10.Socket编程
Linux下的Socket编程大体上包括Tcp Socket、Udp Socket即Raw Socket这三种,其中TCP和UDP方式的Socket编程用于编写应用层的socket程序,是我们用得比较多的,而Raw Socket则用得相对较少,不在本文介绍范围之列。
TCP Socket
基于TCP协议的客户端/服务器程序的一般流程一般如下:
它基本上可以分为三个部分:
一、建立连接:
- 服务器调用socket()、bind()、listen()完成初始化后,调用accept()阻塞等待,处于监听端口的状态
- 客户端调用socket()初始化后,调用connect()发出SYN段并阻塞等待服务器应答
- 服务器应答一个SYN-ACK段,客户端收到后从connect()返回,同时应答一个ACK段,服务器收到后从accept()返回。
二、传输数据:
建立连接后,TCP协议提供全双工的通信管道,服务器端和客户端根据协议可以通过read和write的反复调用实现数据的传输
三、关闭连接:
当数据传输已经完成后,服务器和客户端可以调用Close关闭连接,一端关闭连接后,另一端read函数则会返回0,可以根据这个特征来感应另一端的退出。
下面就以一个简单的EchoServer演示一下如何创建服务器端和客户端代码,其中和socket相关api都会高亮显示。
服务器端示例:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define MAXLINE 80
#define SERV_PORT 8000
int main(void)
{
char buf[MAXLINE];
int listenfd = 0;
listenfd = socket(AF_INET, SOCK_STREAM, 0);
sockaddr_in servaddr = {0};
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);
bind(listenfd, (sockaddr *)&servaddr, sizeof(servaddr));
listen(listenfd, 20);
printf("Accepting connections ...\n");
while (1)
{
sockaddr_in cliaddr = {0};
socklen_t cliaddr_len = sizeof(cliaddr);
int connfd = accept(listenfd, (sockaddr *)&cliaddr, &cliaddr_len);
char str[INET_ADDRSTRLEN];
printf("connected from %s at PORT %d\n",
inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),
ntohs(cliaddr.sin_port));
while(true)
{
int count = read(connfd, buf, MAXLINE);
if (count == 0)
break;
write(connfd, buf, count);
}
close(connfd);
printf("closed from %s at PORT %d\n",
inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),
ntohs(cliaddr.sin_port));
}
}
PS:这里需要注意的一下的是sock函数的第二个参数SOCK_STREAM,它表示是一个TCP连接,后面我们会介绍通过传入SOCK_DGRAM打开udp连接。
服务器端主体流程就是一个死循环,它接受一个socket连接,然后将其原封不动的返回给客户端,待客户端退出后,关闭socket连接,再次接受下一个socket连接。
客户端代码如下:
#include <stdio.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#define MAXLINE 80
#define SERV_PORT 8000
#define MESSAGE "hello world"
int main(int argc, char *argv[])
{
char buf[MAXLINE];
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
sockaddr_in servaddr = {0};
servaddr.sin_family = AF_INET;
inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);
servaddr.sin_port = htons(SERV_PORT);
if (0 != connect(sockfd, (sockaddr *)&servaddr, sizeof(servaddr)))
{
printf("connected failed");
return 1;
}
write(sockfd, MESSAGE, sizeof(MESSAGE));
int count = read(sockfd, buf, MAXLINE);
printf("Response from server: %s\n",buf);
close(sockfd);
return 0;
}
UDP Socket
典型的UDP客户端/服务器通讯过程如下图所示:
由于UDP不需要维护连接,程序逻辑简单了很多,但是UDP协议是不可靠的,实际上有很多保证通讯可靠性的机制需要在应用层实现,可能反而会需要更多代码。
典型的示例如下:
/* server.cpp */
#include <stdio.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define MAXLINE 80
#define SERV_PORT 8000
int main(void)
{
char buf[MAXLINE];
char str[INET_ADDRSTRLEN];
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
sockaddr_in servaddr = {0};
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);
bind(sockfd, (sockaddr *)&servaddr, sizeof(servaddr));
printf("Accepting connections ...\n");
while (1)
{
sockaddr_in cliaddr;
socklen_t cliaddr_len = sizeof(cliaddr);
int count = recvfrom(sockfd, buf, MAXLINE, 0, (sockaddr *)&cliaddr, &cliaddr_len);
if (count < 0)
{
printf("recvfrom error");
continue;
}
printf("received from %s at PORT %d\n",
inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),
ntohs(cliaddr.sin_port));
sendto(sockfd, buf, count, 0, (sockaddr *)&cliaddr, sizeof(cliaddr));
}
}
/* client.cpp */
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define MAXLINE 80
#define SERV_PORT 8000
int main(int argc, char *argv[])
{
char buf[MAXLINE];
char str[INET_ADDRSTRLEN];
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
sockaddr_in servaddr = {0};
servaddr.sin_family = AF_INET;
inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);
servaddr.sin_port = htons(SERV_PORT);
while (fgets(buf, MAXLINE, stdin) != NULL)
{
int count = sendto(sockfd, buf, strlen(buf), 0, (sockaddr *)&servaddr, sizeof(servaddr));
if (count == -1)
{
printf("sendto error");
return 0;
}
count = recvfrom(sockfd, buf, MAXLINE, 0, NULL, 0);
if (count == -1)
{
printf("recvfrom error");
return 0;
}
write(STDOUT_FILENO, buf, count);
}
close(sockfd);
return 0;
}
Linux高级编程--10.Socket编程的更多相关文章
- ZT Linux系统环境下的Socket编程详细解析
Linux系统环境下的Socket编程详细解析 来自: http://blog.163.com/jiangh_1982/blog/static/121950520082881457775/ 什么是So ...
- Linux下的C Socket编程 -- server端的继续研究
Linux下的C Socket编程(四) 延长server的生命周期 在前面的一个个例子中,server在处理完一个连接后便会立即结束掉自己,然而这种server并不科学啊,server应该是能够一直 ...
- Linux下的C Socket编程 -- server端的简单示例
Linux下的C Socket编程(三) server端的简单示例 经过前面的client端的学习,我们已经知道了如何创建socket,所以接下来就是去绑定他到具体的一个端口上面去. 绑定socket ...
- Linux下的C Socket编程 -- 获取对方IP地址
Linux下的C Socket编程(二) 获取域名对应的IP地址 经过上面的讨论,如果我们想要连接到远程的服务器,我们需要知道对方的IP地址,系统函数gethostbyname便能够实现这个目的.它能 ...
- Linux下的C Socket编程 -- 简介与client端的处理
Linux下的C Socket编程(一) 介绍 Socket是进程间通信的方式之一,是进程间的通信.这里说的进程并不一定是在同一台机器上也有可能是通过网络连接的不同机器上.只要他们之间建立起了sock ...
- 第九章:Python高级编程-Python socket编程
第九章:Python高级编程-Python socket编程 Python3高级核心技术97讲 笔记 9.1 弄懂HTTP.Socket.TCP这几个概念 Socket为我们封装好了协议 9.2 cl ...
- 多线程编程以及socket编程_Linux程序设计4chapter15
看了Linux程序设计4中文版,学习了多线程编程和socket编程.本文的程序参考自Linux程序设计4的第15章. 设计了一个客户端程序,一个服务端程序.使用TCP协议进行数据传输. 客户端进程创建 ...
- 【Linux编程】socket编程
套接字是通信端点的抽象.文件描写叙述符用open函数创建,而套接字描写叙述符用socket函数创建.socket函数原型例如以下: int socket(int domain, int type, i ...
- 学习笔记(10) : Socket 编程典型代码积累
网络编程实现的机制: 服务器端: 申请一个socket 绑定到一个IP地址和端口上 开启侦听,等待接受连接 客户端: 申请一个socket 连接服务器(指明IP.端口) 服务器端: 接收到 ...
随机推荐
- 充分利用 UE4 中的噪声
转自:https://www.unrealengine.com/zh-CN/blog/getting-the-most-out-of-noise-in-ue4 UE4 推出基于材质的程序式噪声已经有一 ...
- 几种你不知道的获取浙A牌照的方法
http://www.19lou.com/forum-464848-thread-18191429174490953-1-1.html 杭州限牌政策执行已有一年多,因为限牌政策,很多人买车之前,都不得 ...
- windows10 qt5 mingw32编译cryptopp563
windows10 qt5 mingw32编译cryptopp563 参考链接: http://www.qtcentre.org/threads/28809-Compiling-amp-using-C ...
- //暴力打开某个APP iOS 私有API LSApplicationWorkspace
//暴力打开某个APP = .= 如果可以打开.直接打开不解释 +(BOOL)isOpenApp:(NSString*)appIdentifierName { Class LSApplicationW ...
- 深入学习golang(4)—new与make
Go语言中的内建函数new和make是两个用于内存分配的原语(allocation primitives).对于初学者,这两者的区别也挺容易让人迷糊的.简单的说,new只分配内存,make用于slic ...
- windows批处理
1.日期作为变量当做文件名的一部分. C:\Documents and Settings\Simon>echo %date%2008-09-09 星期二 C:\Documents and Set ...
- Activemq消息类型
Activemq消息类型JMS规范中的消息类型包括TextMessage.MapMessage.ObjectMessage.BytesMessage.和StreamMessage等五种.ActiveM ...
- Codeforces Round #379 (Div. 2) C. Anton and Making Potions 枚举+二分
C. Anton and Making Potions 题目连接: http://codeforces.com/contest/734/problem/C Description Anton is p ...
- windows下使用TortoiseGit代替Git命令行操作
windows下使用TortoiseGit代替Git命令行操作 大家在使用svn的时候,都非常喜欢使用小乌龟,也就是TortoiseSVN:那么git也有小乌龟版本,即TortoiseGit. 1.安 ...
- TargetProcess公司敏捷开发历程-开发实践篇