文档声明:
以下资料均属于本人在学习过程中产出的学习笔记,如果错误或者遗漏之处,请多多指正。并且该文档在后期会随着学习的深入不断补充完善。感谢各位的参考查看。
笔记资料仅供学习交流使用,转载请标明出处,谢谢配合。
如果存在相关知识点的遗漏,可以在评论区留言,看到后将在第一时间更新。
作者:Aliven888
客户端
// client.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define SERVER_PORT 20001
#define SERVER_IP "127.0.0.1"
int main()
{
int sock_fd;
struct sockaddr_in addr_server;
if((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
perror("socket");
exit(1);
}
printf("sock successful\n");
//设置socket参数, 根据需求选择自己需要的
{
// 1.设置调用close(socket)后,仍可继续重用该socket。调用close(socket)一般不会立即关闭socket,而经历TIME_WAIT的过程。
bool bReuseaddr = TRUE;
setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, (const char*)&bReuseaddr, sizeof(bool) );
// 2. 如果要已经处于连接状态的soket在调用closesocket()后强制关闭,不经历TIME_WAIT的过程:
BOOL bDontLinger = FALSE;
setsockopt(sock_fd, SOL_SOCKET, SO_DONTLINGER, (const char*)&bDontLinger, sizeof(bool));
//3. 超时等待设置
int iSecond = 1;
setsockopt(sock_fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&iSecond, sizeof(iSecond)); //recv timeout
setsockopt(sock_fd, SOL_SOCKET, SO_SNDTIMEO, (const char*)&iSecond, sizeof(iSecond)); //send timeout
//4. 缓冲区设置
// socket缓冲区的概念:
// socket编程基于传输层,是应用层和传输层之间的一个抽象层。在使用socket API时,
// 实际上每创建一个socket,都会分配两个缓冲区,输入缓冲区和输出缓冲区(大小一般是8K),
// Linux下一切皆文件的思想,两台主机在进行通信时,write函数是向缓冲区里写,read函数是从缓冲区里读,
// 至于缓冲区里的数据什么时候被传输,有没有达到目标主机,这些都交给传输层的TCP/UDP来做。
int iRecvBufLen = 32 * 1024; //设置为32K
setsockopt(sock_fd, SOL_SOCKET, SO_RCVBUF, (const char*)&iRecvBufLen, sizeof(int) );
int iSendBufLen = 32*1024; //设置为32K
setsockopt(sock_fd, SOL_SOCKET, SO_SNDBUF, (const char*)&iSendBufLen, sizeof(int) );
// 5.在发送数据的时,不执行由系统缓冲区到socket缓冲区的拷贝,以提高程序的性能:
int iZero = 0;
setsockopt(sock_fd, SOL_SOCKET, SO_SNDBUF, (char *)&iZero, sizeof(iZero) );
// 6.在接收数据时,不执行将socket缓冲区的内容拷贝到系统缓冲区:
int iZero = 0;
setsockopt(sock_fd, SOL_SOCKET, SO_RCVBUF, (char *)&iZero, sizeof(int) );
// 7.一般在发送UDP数据报的时候,希望该socket发送的数据具有广播特性:
bool bBroadcast = true;
setsockopt(sock_fd, SOL_SOCKET, SO_BROADCAST, (const char*)&bBroadcast, sizeof(bool));
// 8.在client连接服务器过程中,如果处于非阻塞模式下的socket在connect()的过程中可以设置connect()延时,直到accpet()被调用(此设置只
// 有在非阻塞的过程中有显著的作用,在阻塞的函数调用中作用不大)
bool bConditionalAccept = true;
setsockopt(sock_fd, SOL_SOCKET, SO_CONDITIONAL_ACCEPT, (const char*)&bConditionalAccept, sizeof(bool));
// 9.如果在发送数据的过程中send()没有完成,还有数据没发送,而调用了close(socket),以前一般采取的措施是shutdown(s,SD_BOTH),但是数
// 据将会丢失。 某些具体程序要求待未发送完的数据发送出去后再关闭socket,可通过设置让程序满足要求:
struct linger {
u_short l_onoff;
u_short l_linger;
};
struct linger m_sLinger;
m_sLinger.l_onoff = 1; //在调用close(socket)时还有数据未发送完,允许等待
// 若m_sLinger.l_onoff=0;则调用closesocket()后强制关闭
m_sLinger.l_linger = 5; //设置等待时间为5秒
setsockopt(sock_fd, SOL_SOCKET, SO_LINGER, (const char*)&m_sLinger, sizeof(struct linger));
}
memset(&addr_server, 0, sizeof(addr_server));
addr_server.sin_family = AF_INET;
addr_server.sin_port = htons(SERVER_PORT);
addr_server.sin_addr.s_addr = inet_addr(SERVER_IP);
if(connect(sock_fd, (struct sockaddr *)&addr_server, sizeof(struct sockaddr))<0)
{
perror("connect");
exit(1);
}
printf("connect successful\n");
send(sock_fd, "ok", 2, 0);
//如果需要接收数据的话,可以再加个 recv
//int len = recv(sock_fd, buf, sizeof(buf), 0);
//处理 recv 接收的内容
close(sock_fd);
}
服务端
// server.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define PORT 20001
int main()
{
int sock_fd, client_fd;
int addr_client_len;
struct sockaddr_in addr_server, addr_client;
addr_client_len = sizeof(struct sockaddr);
if((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
perror("socket");
exit(1);
}
printf("sock successful\n");
//设置socket参数, 根据需求选择自己需要的
{
// 1.设置调用close(socket)后,仍可继续重用该socket。调用close(socket)一般不会立即关闭socket,而经历TIME_WAIT的过程。
bool bReuseaddr = TRUE;
setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, (const char*)&bReuseaddr, sizeof(bool) );
// 2. 如果要已经处于连接状态的soket在调用closesocket()后强制关闭,不经历TIME_WAIT的过程:
BOOL bDontLinger = FALSE;
setsockopt(sock_fd, SOL_SOCKET, SO_DONTLINGER, (const char*)&bDontLinger, sizeof(bool));
//3. 超时等待设置
int iSecond = 1;
setsockopt(sock_fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&iSecond, sizeof(iSecond)); //recv timeout
setsockopt(sock_fd, SOL_SOCKET, SO_SNDTIMEO, (const char*)&iSecond, sizeof(iSecond)); //send timeout
//4. 缓冲区设置
// socket缓冲区的概念:
// socket编程基于传输层,是应用层和传输层之间的一个抽象层。在使用socket API时,
// 实际上每创建一个socket,都会分配两个缓冲区,输入缓冲区和输出缓冲区(大小一般是8K),
// Linux下一切皆文件的思想,两台主机在进行通信时,write函数是向缓冲区里写,read函数是从缓冲区里读,
// 至于缓冲区里的数据什么时候被传输,有没有达到目标主机,这些都交给传输层的TCP/UDP来做。
int iRecvBufLen = 32 * 1024; //设置为32K
setsockopt(sock_fd, SOL_SOCKET, SO_RCVBUF, (const char*)&iRecvBufLen, sizeof(int) );
int iSendBufLen = 32*1024; //设置为32K
setsockopt(sock_fd, SOL_SOCKET, SO_SNDBUF, (const char*)&iSendBufLen, sizeof(int) );
// 5.在发送数据的时,不执行由系统缓冲区到socket缓冲区的拷贝,以提高程序的性能:
int iZero = 0;
setsockopt(sock_fd, SOL_SOCKET, SO_SNDBUF, (char *)&iZero, sizeof(iZero) );
// 6.在接收数据时,不执行将socket缓冲区的内容拷贝到系统缓冲区:
int iZero = 0;
setsockopt(sock_fd, SOL_SOCKET, SO_RCVBUF, (char *)&iZero, sizeof(int) );
// 7.一般在发送UDP数据报的时候,希望该socket发送的数据具有广播特性:
bool bBroadcast = true;
setsockopt(sock_fd, SOL_SOCKET, SO_BROADCAST, (const char*)&bBroadcast, sizeof(bool));
// 8.在client连接服务器过程中,如果处于非阻塞模式下的socket在connect()的过程中可以设置connect()延时,直到accpet()被调用(此设置只
// 有在非阻塞的过程中有显著的作用,在阻塞的函数调用中作用不大)
bool bConditionalAccept = true;
setsockopt(sock_fd, SOL_SOCKET, SO_CONDITIONAL_ACCEPT, (const char*)&bConditionalAccept, sizeof(bool));
// 9.如果在发送数据的过程中send()没有完成,还有数据没发送,而调用了close(socket),以前一般采取的措施是shutdown(s,SD_BOTH),但是数
// 据将会丢失。 某些具体程序要求待未发送完的数据发送出去后再关闭socket,可通过设置让程序满足要求:
struct linger {
u_short l_onoff;
u_short l_linger;
};
struct linger m_sLinger;
m_sLinger.l_onoff = 1; //在调用close(socket)时还有数据未发送完,允许等待
// 若m_sLinger.l_onoff=0;则调用closesocket()后强制关闭
m_sLinger.l_linger = 5; //设置等待时间为5秒
setsockopt(sock_fd, SOL_SOCKET, SO_LINGER, (const char*)&m_sLinger, sizeof(struct linger));
}
memset(&addr_server, 0, sizeof(addr_server));
addr_server.sin_family = AF_INET;
addr_server.sin_port = htons(PORT);
addr_server.sin_addr.s_addr = htonl(INADDR_ANY);
if(bind(sock_fd, (struct sockaddr *)&addr_server, sizeof(struct sockaddr_in)) < 0)
{
perror("bind");
exit(1);
}
printf("bind sucessful\n");
if(listen(sock_fd, 5))
{
perror("listen");
exit(1);
}
printf("listen sucessful\n");
char buf[1024];
while(1)
{
memset(buf, '\0', 1024);
if((client_fd = accept(sock_fd, (struct sockaddr *)&addr_client, &addr_client_len)) < 0)
{
perror("accept");
exit(1);
}
int len = recv(client_fd, buf, sizeof(buf), 0);
//char *buf = new(std::nothrow) char[1024];
//recv(client_fd, buf, 1024);
printf("accept client ip: %s : %s\n", inet_ntoa(addr_client.sin_addr), buf);
send(client_fd, "ok", 2, 0);
close(client_fd);
//delete buf;
//buf = null;
}
close(sock_fd);
}