Socket
先介绍一下socket的启动过程:
根据连接启动的方式以及本地套接字要连接的目标,套接字之间的连接过程可以分为三个步骤:服务器监听,客户端请求,连接确认。
(1)服务器监听:是服务器端套接字并不定位具体的客户端套接字,而是处于等待连接的状态,实时监控网络状态。
(2)客户端请求:是指由客户端的套接字提出连接请求,要连接的目标是服务器端的套接字。为此,客户端的套接字必须首先描述它要连接的服务器的套接字,指出服务器端套接字的地址和端口号,然后就向服务器端套接字提出连接请求。
(3)连接确认:是指当服务器端套接字监听到或者说接收到客户端套接字的连接请求,它就响应客户端套接字的请求,建立一个新的线程,把服务器端套接字的描述发给客户端,一旦客户端确认了此描述,连接就建立好了。而服务器端套接字继续处于监听状态,继续接收其他客户端套接字的连接请求。 内容来自百度
socket函数
创建
函数原型:
int socket(int domain, int type, int protocol);
- domain:协议域,又称协议族(family)。常用的协议族有AF_INET、AF_INET6、AF_LOCAL(或称AF_UNIX,Unix域Socket)、AF_ROUTE等。协议族决定了socket的地址类型,在通信中必须采用对应的地址,如AF_INET决定了要用ipv4地址(32位的)与端口号(16位的)的组合、AF_UNIX决定了要用一个绝对路径名作为地址。
- type:指定Socket类型。常用的socket类型有SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等。流式Socket(SOCK_STREAM)是一种面向连接的Socket,针对于面向连接的TCP服务应用。数据报式Socket(SOCK_DGRAM)是一种无连接的Socket,对应于无连接的UDP服务应用。
- protocol:指定协议。常用协议有IPPROTO_TCP、IPPROTO_UDP、IPPROTO_SCTP、IPPROTO_TIPC等,分别对应TCP传输协议、UDP传输协议、STCP传输协议、TIPC传输协议。
如果调用成功就返回新创建的套接字的描述符,如果失败就返回INVALID_SOCKET(Linux下失败返回-1)。
绑定
函数原型
int bind(SOCKET socket, const struct sockaddr* address, socklen_t address_len);
- socket:是一个套接字描述符。
- address:是一个sockaddr结构指针,该结构中包含了要结合的地址和端口号。
- address_len:确定address缓冲区的长度。
如果函数执行成功,返回值为0,否则为SOCKET_ERROR。
接收
函数原型:
int recv(SOCKET socket, char FAR* buf, int len, int flags);
- socket:一个标识已连接套接口的描述字。
- buf:用于接收数据的缓冲区。
- len:缓冲区长度。
- flags:指定调用方式。取值:MSG_PEEK 查看当前数据,数据将被复制到缓冲区中,但并不从输入队列中删除;MSG_OOB 处理带外数据。
若无错误发生,recv()返回读入的字节数。如果连接已中止,返回0。否则的话,返回SOCKET_ERROR错误,应用程序可通过WSAGetLastError()获取相应错误代码。
函数原型:
ssize_t recvfrom(int sockfd, void buf, int len, unsigned int flags, struct socketaddr* from, socket_t* fromlen);
- sockfd:标识一个已连接套接口的描述字。
- buf:接收数据缓冲区。
- len:缓冲区长度。
- flags:调用操作方式。
- from:(可选)指针,指向装有源地址的缓冲区。
- fromlen:(可选)指针,指向from缓冲区长度值。
发送
函数原型:
int sendto( SOCKET s, const char FAR* buf, int size, int flags, const struct sockaddr FAR* to, int tolen);
- s:套接字
- buf:待发送数据的缓冲区
- size:缓冲区长度
- flags:调用方式标志位, 一般为0, 改变Flags,将会改变Sendto发送的形式
- addr:(可选)指针,指向目的套接字的地址
- tolen:addr所指地址的长度
如果成功,则返回发送的字节数,失败则返回SOCKET_ERROR。
接收连接请求
函数原型:
int accept( int fd, struct socketaddr* addr, socklen_t* len);
- fd:套接字描述符。
- addr:返回连接着的地址
- len:接收返回地址的缓冲区长度
成功返回客户端的文件描述符,失败返回-1。
TCP代码实现
TCP(Transmission Control Protocol 传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议。
所以我们要知道三次握手和四次分手。有空详细讲解。
TCP Server
//
// main.c
// SocketServer
//
// Created by Alps on 15/8/17.
// Copyright (c) 2015年 chen. All rights reserved.
//
#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 <unistd.h>
#ifndef DEFAULT_PORT
#define DEFAULT_PORT 8001
#endif
#ifndef MAXLINE
#define MAXLINE 4096
#endif
int main(int argc, char const *argv[])
{
int socket_fd = socket(AF_INET, SOCK_STREAM, 0);
int connect_fd;
struct sockaddr_in servaddr;
char *buffer = (char*)malloc(MAXLINE * sizeof(char));
int recv_len,write_len;
FILE *fp = fopen("/Users/alps/Desktop/recv.jpg", "wb+");
if (fp == NULL)
{
printf("Open file error!\n");
exit(0);
}
if (socket_fd == -1)
{
printf("create socket error %s(errno: %d\n)", strerror(errno), errno);
exit(0);
}
memset(&servaddr, 0 , sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//IP地址设置成INADDR_ANY,让系统自动获取本机的IP地址。
servaddr.sin_port = htons(DEFAULT_PORT);//设置的端口为DEFAULT_PORT
//将本地地址绑定到所创建的套接字上
if( bind(socket_fd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1){
printf("bind socket error: %s(errno: %d)\n",strerror(errno),errno);
exit(0);
}
//开始监听是否有客户端连接
if( listen(socket_fd, 10) == -1){
printf("listen socket error: %s(errno: %d)\n",strerror(errno),errno);
exit(0);
}
printf("======waiting for client's request======\n");
while(1){
//阻塞直到有客户端连接,不然多浪费CPU资源。
if( (connect_fd = accept(socket_fd, (struct sockaddr*)NULL, NULL)) == -1){
printf("accept socket error: %s(errno: %d)",strerror(errno),errno);
continue;
}
//接受客户端传过来的数据
bzero(buffer, MAXLINE);
while(1){
recv_len = (int)recv(connect_fd, buffer, MAXLINE, 0);
if (recv_len < 0)
{
printf("Receive Data From Client Error!\n");
break;
}
printf("%s\n",buffer);
write_len = (int)fwrite(buffer, sizeof(char), recv_len, fp);;
if (write_len < recv_len)
{
printf("Write File Failed!\n");
break;
}
bzero(buffer, MAXLINE);
}
close(connect_fd);
break;
}
close(socket_fd);
return 0;
}
以上是Server代码。
TCP Client
//
// main.c
// SocketClient
//
// Created by Alps on 15/8/9.
// Copyright (c) 2015年 chen. All rights reserved.
//
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#ifndef DEFAULT_PORT
#define DEFAULT_PORT 8001
#endif
#define MAXLINE 4096
#define IP "127.0.0.1"
int main(int argc, char** argv)
{
int sockfd,rec_len,send_len;
char recvline[4096], sendline[4096];
char *buffer = (char *)malloc(MAXLINE * sizeof(char));
struct sockaddr_in servaddr;
FILE *outfp = fopen("/Users/alps/Desktop/socket.jpg","rb+");
if (outfp == NULL)
{
printf("Client Open File error!\n");
exit(0);
}
if( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){
printf("create socket error: %s(errno: %d)\n", strerror(errno),errno);
exit(0);
}
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(DEFAULT_PORT);
if( inet_pton(AF_INET, IP, &servaddr.sin_addr) <= 0){
printf("inet_pton error for %s\n",argv[1]);
exit(0);
}
if( connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0){
printf("connect error: %s(errno: %d)\n",strerror(errno),errno);
exit(0);
}
// while(1){
printf("send file to server... \n");
while((send_len = (int)fread(buffer, sizeof(char), MAXLINE, outfp)) > 0){
if (send(sockfd, buffer, send_len, 0) < 0)
{
printf("Send File Error!\n");
break;
}
bzero(buffer, MAXLINE);
}
close(sockfd);
exit(0);
}
以上是Client代码。
UDP代码实现
UDP协议全称是用户数据报协议[1] ,在网络中它与TCP协议一样用于处理数据包,是一种无连接的协议。在OSI模型中,在第四层——传输层,处于IP协议的上一层。UDP有不提供数据包分组、组装和不能对数据包进行排序的缺点,也就是说,当报文发送之后,是无法得知其是否安全完整到达的
所以呢我们UDP的代码和TCP的稍有不同。
UDP Server
//
// main.c
// UDP_Server
//
// Created by Alps on 15/8/12.
// Copyright (c) 2015年 chen. All rights reserved.
//
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main(int argc, char **argv)
{
int sockfd;
struct sockaddr_in servaddr;
sockfd = socket(PF_INET, SOCK_DGRAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(50001);
bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
char recvline[1024];
recvfrom(sockfd, recvline, 1024, 0, NULL, NULL);
printf("%s\n", recvline);
close(sockfd);
}
这个是Server代码。
UDP Client
//
// main.c
// UDP_Client
//
// Created by Alps on 15/8/12.
// Copyright (c) 2015年 chen. All rights reserved.
//
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main(int argc, char **argv)
{
int sockfd;
struct sockaddr_in servaddr;
sockfd = socket(PF_INET, SOCK_DGRAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(50001);
servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
char sendline[100];
sprintf(sendline, "Hello, world!");
sendto(sockfd, sendline, strlen(sendline), 0, (struct sockaddr *)&servaddr, sizeof(servaddr));
close(sockfd);
return 1;
}
以上是Client代码。
其实这两个的协议不同,注定不同的是一些发送和接收方式。但其实最终收到数据和发送数据的处理都是一样的。