TCP客户端的搭建流程:
1.创建套接字 socket
2.要指定服务器的ip和端口、协议等
绑定ip 和端口号 bind(可以不绑定)
3.连接服务器 connect
4.通信
-
- read、write
- Recv、send
- Recvfrom、sendto
5.关闭套接字:close/shutdown(套接字,howto):howto:0(读端) 1(写端)
出现问题如何优化:
1、
-
设置套接字重用:setsockopt()
- Int opt = 1;
- Setsockopt(套接字,SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt) );
-
打印客户端ip和端口
- 需要创建新的结构体变量保存 客户端的ip等信息
-
Eg:
- struct sockaddr_in caddr ={0};
- int len = sizeof(caddr);
- Accept(listenfd, (struct sockaddr *)caddr, &len);
- Printf(“ip:%s–port:%d\n”, inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port));
代码如下:
#include <stdio.h>
#include <string.h>
#include <sys/types.h> /* See NOTES */ //man socket
#include <sys/socket.h>
#include <sys/socket.h> // man 7 ip
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#include <sys/socket.h> //man 3 inet_addr
#include <netinet/in.h>
#include <arpa/inet.h> //man 2 read
#include <unistd.h>
#include <ctype.h>
#include <stdlib.h>
#define SIZE 1024
#define SERV_IP “0”
#define SERV_PORT 6666
int main(int argc,const char *argv[])
{
int listenfd; //用于保存监听套结字
int connfd ; //用于通信的套结字
int ret;
char recvbuf[SIZE] = {0};
//1、创建套结字 socket
listenfd = socket(AF_INET, SOCK_STREAM, 0); //AF_INET:IPV4协议 SOCK_STREAM:流式套结字
if(-1 == listenfd)
{
perror(“socket”);
return -1;
}
printf(“socket %d ok\n”, listenfd); //
//填充ip等信息到通用ip结构体
#if 0
struct sockaddr_in saddr ;
memset(&saddr, 0, sizeof(saddr)); // bzero(&saddr, sizeof(saddr));
saddr.sin_family = AF_INET; //IPV4 协议
saddr.sin_port = htons(6666);//端口号 :1024-49151
saddr.sin_addr.s_addr= inet_addr(“192.168.16.100”);
#else
struct sockaddr_in saddr = {
.sin_family = AF_INET,
.sin_port = htons(SERV_PORT),
.sin_addr.s_addr = inet_addr(SERV_IP)
};
#endif
//优化2:设置套结字属性 端口重用 setsockopt();
int opt = 1;
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
//2、绑定ip和端口a
socklen_t slen = sizeof(saddr);
ret = bind(listenfd, (struct sockaddr* )&saddr, slen);
if(-1 == ret)
{
perror(“bind”);
return -1;
}
printf(“bind ok\n”);
//3、监听
ret = listen(listenfd, 8);
if(-1 == ret)
{
perror(“listen”);
return -1;
}
printf(“listen ok, wait for connect…\n”);
//优化1:循环监听客户端
while(1)
{
//4、处理客户端请求
#if 0
//accept之后 监听套结字listenfd 转接 为新的 通信套结字 connfd 使用
connfd = accept(listenfd, NULL, NULL); //不关心客户端的ip和端口
printf(“had client connect%d\n”, connfd);
#else
//优化 3:关心客户端ip和端口了并打印
struct sockaddr_in caddr = {0};
// memset(caddr, 0, sizeof(caddr));
socklen_t clen = sizeof(caddr);
connfd = accept(listenfd, (struct sockaddr *)&caddr, &clen);
if(connfd == -1)
{
perror(“accept”);
return -1;
}
printf(“client(%s:%d) had connected success\n”, inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port) );
#endif
//5、通信
while(1)
{
memset(recvbuf, 0, sizeof(recvbuf));
int count = read(connfd, recvbuf, sizeof(recvbuf));
if(-1 == count)
{
perror(“read”);
return -1;
}
else if(0 == count)
{
printf(“client quit\n”);
break;
}
printf(“recv:%s\n”,recvbuf);
//判断客户端发来的指令 做出响应
if( 0 == strncmp(recvbuf, “sl”, 2) )
{
system(“sl”);
}
else if(0 == strncmp(recvbuf, “xcowsay”, 7))
{
system(“xcowsay 爱 老虎油!”);
}
//
int i;
for(i=0; i<count; i++)
{
recvbuf[i] = toupper(recvbuf[i]); //将单个字符转化大写
}
//write(connfd, “ok”, 2);
write(connfd, recvbuf, count);
}
//6、关闭套结字退出
close(connfd);
}
close(listenfd);
return 0;
}