先介绍一下socket的启动过程:
根据连接启动的方式以及本地套接字要连接的目标,套接字之间的连接过程可以分为三个步骤:服务器监听,客户端请求,连接确认。
(1)服务器监听:是服务器端套接字并不定位具体的客户端套接字,而是处于等待连接的状态,实时监控网络状态。
(2)客户端请求:是指由客户端的套接字提出连接请求,要连接的目标是服务器端的套接字。为此,客户端的套接字必须首先描述它要连接的服务器的套接字,指出服务器端套接字的地址和端口号,然后就向服务器端套接字提出连接请求。
(3)连接确认:是指当服务器端套接字监听到或者说接收到客户端套接字的连接请求,它就响应客户端套接字的请求,建立一个新的线程,把服务器端套接字的描述发给客户端,一旦客户端确认了此描述,连接就建立好了。而服务器端套接字继续处于监听状态,继续接收其他客户端套接字的连接请求。 内容来自百度
创建
函数原型:
int socket(int domain, int type, int protocol);
如果调用成功就返回新创建的套接字的描述符,如果失败就返回INVALID_SOCKET(Linux下失败返回-1)。
绑定
函数原型
int bind(SOCKET socket, const struct sockaddr* address, socklen_t address_len);
如果函数执行成功,返回值为0,否则为SOCKET_ERROR。
接收
函数原型:
int recv(SOCKET socket, char FAR* buf, int len, int flags);
若无错误发生,recv()返回读入的字节数。如果连接已中止,返回0。否则的话,返回SOCKET_ERROR错误,应用程序可通过WSAGetLastError()获取相应错误代码。
函数原型:
ssize_t recvfrom(int sockfd, void buf, int len, unsigned int flags, struct socketaddr* from, socket_t* fromlen);
发送
函数原型:
int sendto( SOCKET s, const char FAR* buf, int size, int flags, const struct sockaddr FAR* to, int tolen);
接收连接请求
函数原型:
int accept( int fd, struct socketaddr* addr, socklen_t* len);
成功返回客户端的文件描述符,失败返回-1。
以上内容来自百度
TCP(Transmission Control Protocol 传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议。
所以我们要知道三次握手和四次分手。有空详细讲解。
//
// main.c
// SocketServer
//
// Created by Alps on 15/8/17.
// Copyright (c) 2015年 chen. All rights reserved.
//
#include
#include
#include
#include
#include
#include
#include
#include
#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代码。
//
// main.c
// SocketClient
//
// Created by Alps on 15/8/9.
// Copyright (c) 2015年 chen. All rights reserved.
//
#include
#include
#include
#include
#include
#include
#include
#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协议全称是用户数据报协议[1] ,在网络中它与TCP协议一样用于处理数据包,是一种无连接的协议。在OSI模型中,在第四层——传输层,处于IP协议的上一层。UDP有不提供数据包分组、组装和不能对数据包进行排序的缺点,也就是说,当报文发送之后,是无法得知其是否安全完整到达的
所以呢我们UDP的代码和TCP的稍有不同。
//
// main.c
// UDP_Server
//
// Created by Alps on 15/8/12.
// Copyright (c) 2015年 chen. All rights reserved.
//
#include
#include
#include
#include
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代码。
//
// main.c
// UDP_Client
//
// Created by Alps on 15/8/12.
// Copyright (c) 2015年 chen. All rights reserved.
//
#include
#include
#include
#include
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代码。
其实这两个的协议不同,注定不同的是一些发送和接收方式。但其实最终收到数据和发送数据的处理都是一样的。